| /* |
| * |
| * (C) COPYRIGHT 2012-2014 ARM Limited. All rights reserved. |
| * |
| * This program is free software and is provided to you under the terms of the |
| * GNU General Public License version 2 as published by the Free Software |
| * Foundation, and any use by you of this program is subject to the terms |
| * of such GNU licence. |
| * |
| * A copy of the licence is included with the program, and can also be obtained |
| * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| |
| |
| /** |
| * pl111_drm_fb.c |
| * Implementation of the framebuffer functions for PL111 DRM |
| */ |
| #include <linux/amba/bus.h> |
| #include <linux/amba/clcd.h> |
| #include <linux/version.h> |
| #include <linux/shmem_fs.h> |
| #include <linux/dma-buf.h> |
| #include <linux/module.h> |
| |
| #include <drm/drmP.h> |
| #include <drm/drm_crtc_helper.h> |
| #include <drm/drm_crtc.h> |
| #include "pl111_drm.h" |
| |
| static void pl111_fb_destroy(struct drm_framebuffer *framebuffer) |
| { |
| struct pl111_drm_framebuffer *pl111_fb; |
| #ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS |
| struct drm_crtc *crtc; |
| unsigned long flags; |
| #endif |
| DRM_DEBUG_KMS("Destroying framebuffer 0x%p...\n", framebuffer); |
| |
| pl111_fb = PL111_FB_FROM_FRAMEBUFFER(framebuffer); |
| |
| /* |
| * Because flips are deferred, wait for all previous flips to complete |
| */ |
| wait_event(priv.wait_for_flips, |
| atomic_read(&priv.nr_flips_in_flight) == 0); |
| #ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS |
| /* |
| * Release KDS resources if it's currently being displayed. Only occurs |
| * when the last framebuffer is destroyed. |
| */ |
| list_for_each_entry(crtc, &framebuffer->dev->mode_config.crtc_list, |
| head) { |
| struct pl111_drm_crtc *pl111_crtc = to_pl111_crtc(crtc); |
| spin_lock_irqsave(&pl111_crtc->current_displaying_lock, flags); |
| if (pl111_crtc->displaying_fb == framebuffer) { |
| /* Release the current buffers */ |
| if (pl111_crtc->old_kds_res_set != NULL) { |
| DRM_DEBUG_KMS("Releasing KDS resources for "); |
| DRM_DEBUG_KMS("displayed 0x%p\n", framebuffer); |
| kds_resource_set_release( |
| &pl111_crtc->old_kds_res_set); |
| } |
| pl111_crtc->old_kds_res_set = NULL; |
| } |
| spin_unlock_irqrestore(&pl111_crtc->current_displaying_lock, |
| flags); |
| } |
| #endif |
| drm_framebuffer_cleanup(framebuffer); |
| |
| if ((pl111_fb->bo != NULL) && (&pl111_fb->bo->gem_object != NULL)) |
| drm_gem_object_unreference_unlocked(&pl111_fb->bo->gem_object); |
| |
| kfree(pl111_fb); |
| |
| DRM_DEBUG_KMS("Destroyed framebuffer 0x%p\n", framebuffer); |
| } |
| |
| static int pl111_fb_create_handle(struct drm_framebuffer *fb, |
| struct drm_file *file_priv, |
| unsigned int *handle) |
| { |
| struct pl111_gem_bo *bo = PL111_BO_FROM_FRAMEBUFFER(fb); |
| DRM_DEBUG_KMS("DRM %s on fb=%p\n", __func__, fb); |
| |
| if (bo == NULL) |
| return -EINVAL; |
| |
| return drm_gem_handle_create(file_priv, &bo->gem_object, handle); |
| } |
| |
| const struct drm_framebuffer_funcs fb_funcs = { |
| .destroy = pl111_fb_destroy, |
| .create_handle = pl111_fb_create_handle, |
| }; |
| |
| struct drm_framebuffer *pl111_fb_create(struct drm_device *dev, |
| struct drm_file *file_priv, |
| struct drm_mode_fb_cmd2 *mode_cmd) |
| { |
| struct pl111_drm_framebuffer *pl111_fb = NULL; |
| struct drm_framebuffer *fb = NULL; |
| struct drm_gem_object *gem_obj; |
| struct pl111_gem_bo *bo; |
| int err = 0; |
| size_t min_size; |
| int bpp; |
| int depth; |
| |
| pr_info("DRM %s\n", __func__); |
| gem_obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); |
| if (gem_obj == NULL) { |
| DRM_ERROR("Could not get gem obj from handle to create fb\n"); |
| err = -ENOENT; |
| goto error; |
| } |
| |
| bo = PL111_BO_FROM_GEM(gem_obj); |
| drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp); |
| |
| if (mode_cmd->pitches[0] < mode_cmd->width * (bpp >> 3)) { |
| DRM_ERROR("bad pitch %u for plane 0\n", mode_cmd->pitches[0]); |
| err = -EINVAL; |
| goto error; |
| } |
| |
| min_size = (mode_cmd->height - 1) * mode_cmd->pitches[0] |
| + mode_cmd->width * (bpp >> 3); |
| |
| if (bo->gem_object.size < min_size) { |
| DRM_ERROR("gem obj size < min size\n"); |
| err = -EINVAL; |
| goto error; |
| } |
| |
| /* We can't scan out SHM so we can't create an fb for it */ |
| if (!(bo->type & PL111_BOT_DMA)) { |
| DRM_ERROR("Can't create FB for non-scanout buffer\n"); |
| err = -EINVAL; |
| goto error; |
| } |
| |
| switch ((char)(mode_cmd->pixel_format & 0xFF)) { |
| case 'Y': |
| case 'U': |
| case 'V': |
| case 'N': |
| case 'T': |
| DRM_ERROR("YUV formats not supported\n"); |
| err = -EINVAL; |
| goto error; |
| } |
| |
| pl111_fb = kzalloc(sizeof(struct pl111_drm_framebuffer), GFP_KERNEL); |
| if (pl111_fb == NULL) { |
| DRM_ERROR("Could not allocate pl111_drm_framebuffer\n"); |
| err = -ENOMEM; |
| goto error; |
| } |
| fb = &pl111_fb->fb; |
| |
| err = drm_framebuffer_init(dev, fb, &fb_funcs); |
| if (err) { |
| DRM_ERROR("drm_framebuffer_init failed\n"); |
| kfree(fb); |
| fb = NULL; |
| goto error; |
| } |
| |
| drm_helper_mode_fill_fb_struct(fb, mode_cmd); |
| |
| /* The only framebuffer formats supported by pl111 |
| * are 16 bpp or 32 bpp with 24 bit depth. |
| * See clcd_enable() |
| */ |
| if (!((fb->bits_per_pixel == 16) || |
| (fb->bits_per_pixel == 32 && fb->depth == 24))) { |
| DRM_DEBUG_KMS("unsupported pixel format bpp=%d, depth=%d\n", fb->bits_per_pixel, fb->depth); |
| drm_framebuffer_cleanup(fb); |
| kfree(fb); |
| fb = NULL; |
| err = -EINVAL; |
| goto error; |
| } |
| |
| pl111_fb->bo = bo; |
| |
| DRM_DEBUG_KMS("Created fb 0x%p with gem_obj 0x%p physaddr=0x%.8x\n", |
| fb, gem_obj, bo->backing_data.dma.fb_dev_addr); |
| |
| return fb; |
| |
| error: |
| if (gem_obj != NULL) |
| drm_gem_object_unreference_unlocked(gem_obj); |
| |
| return ERR_PTR(err); |
| } |