Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2013 Red Hat
0004  * Author: Rob Clark <robdclark@gmail.com>
0005  */
0006 
0007 #include <drm/drm_crtc.h>
0008 #include <drm/drm_damage_helper.h>
0009 #include <drm/drm_file.h>
0010 #include <drm/drm_fourcc.h>
0011 #include <drm/drm_framebuffer.h>
0012 #include <drm/drm_gem_framebuffer_helper.h>
0013 #include <drm/drm_probe_helper.h>
0014 
0015 #include "msm_drv.h"
0016 #include "msm_kms.h"
0017 #include "msm_gem.h"
0018 
0019 struct msm_framebuffer {
0020     struct drm_framebuffer base;
0021     const struct msm_format *format;
0022 
0023     /* Count of # of attached planes which need dirtyfb: */
0024     refcount_t dirtyfb;
0025 
0026     /* Framebuffer per-plane address, if pinned, else zero: */
0027     uint64_t iova[DRM_FORMAT_MAX_PLANES];
0028     atomic_t prepare_count;
0029 };
0030 #define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base)
0031 
0032 static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
0033         const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos);
0034 
0035 static int msm_framebuffer_dirtyfb(struct drm_framebuffer *fb,
0036                    struct drm_file *file_priv, unsigned int flags,
0037                    unsigned int color, struct drm_clip_rect *clips,
0038                    unsigned int num_clips)
0039 {
0040     struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
0041 
0042     /* If this fb is not used on any display requiring pixel data to be
0043      * flushed, then skip dirtyfb
0044      */
0045     if (refcount_read(&msm_fb->dirtyfb) == 1)
0046         return 0;
0047 
0048     return drm_atomic_helper_dirtyfb(fb, file_priv, flags, color,
0049                      clips, num_clips);
0050 }
0051 
0052 static const struct drm_framebuffer_funcs msm_framebuffer_funcs = {
0053     .create_handle = drm_gem_fb_create_handle,
0054     .destroy = drm_gem_fb_destroy,
0055     .dirty = msm_framebuffer_dirtyfb,
0056 };
0057 
0058 #ifdef CONFIG_DEBUG_FS
0059 void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
0060 {
0061     struct msm_gem_stats stats = {};
0062     int i, n = fb->format->num_planes;
0063 
0064     seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n",
0065             fb->width, fb->height, (char *)&fb->format->format,
0066             drm_framebuffer_read_refcount(fb), fb->base.id);
0067 
0068     for (i = 0; i < n; i++) {
0069         seq_printf(m, "   %d: offset=%d pitch=%d, obj: ",
0070                 i, fb->offsets[i], fb->pitches[i]);
0071         msm_gem_describe(fb->obj[i], m, &stats);
0072     }
0073 }
0074 #endif
0075 
0076 /* prepare/pin all the fb's bo's for scanout.
0077  */
0078 int msm_framebuffer_prepare(struct drm_framebuffer *fb,
0079         struct msm_gem_address_space *aspace,
0080         bool needs_dirtyfb)
0081 {
0082     struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
0083     int ret, i, n = fb->format->num_planes;
0084 
0085     if (needs_dirtyfb)
0086         refcount_inc(&msm_fb->dirtyfb);
0087 
0088     atomic_inc(&msm_fb->prepare_count);
0089 
0090     for (i = 0; i < n; i++) {
0091         ret = msm_gem_get_and_pin_iova(fb->obj[i], aspace, &msm_fb->iova[i]);
0092         drm_dbg_state(fb->dev, "FB[%u]: iova[%d]: %08llx (%d)",
0093                   fb->base.id, i, msm_fb->iova[i], ret);
0094         if (ret)
0095             return ret;
0096     }
0097 
0098     return 0;
0099 }
0100 
0101 void msm_framebuffer_cleanup(struct drm_framebuffer *fb,
0102         struct msm_gem_address_space *aspace,
0103         bool needed_dirtyfb)
0104 {
0105     struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
0106     int i, n = fb->format->num_planes;
0107 
0108     if (needed_dirtyfb)
0109         refcount_dec(&msm_fb->dirtyfb);
0110 
0111     for (i = 0; i < n; i++)
0112         msm_gem_unpin_iova(fb->obj[i], aspace);
0113 
0114     if (!atomic_dec_return(&msm_fb->prepare_count))
0115         memset(msm_fb->iova, 0, sizeof(msm_fb->iova));
0116 }
0117 
0118 uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb,
0119         struct msm_gem_address_space *aspace, int plane)
0120 {
0121     struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
0122     return msm_fb->iova[plane] + fb->offsets[plane];
0123 }
0124 
0125 struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
0126 {
0127     return drm_gem_fb_get_obj(fb, plane);
0128 }
0129 
0130 const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb)
0131 {
0132     struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
0133     return msm_fb->format;
0134 }
0135 
0136 struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
0137         struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd)
0138 {
0139     const struct drm_format_info *info = drm_get_format_info(dev,
0140                                  mode_cmd);
0141     struct drm_gem_object *bos[4] = {0};
0142     struct drm_framebuffer *fb;
0143     int ret, i, n = info->num_planes;
0144 
0145     for (i = 0; i < n; i++) {
0146         bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
0147         if (!bos[i]) {
0148             ret = -ENXIO;
0149             goto out_unref;
0150         }
0151     }
0152 
0153     fb = msm_framebuffer_init(dev, mode_cmd, bos);
0154     if (IS_ERR(fb)) {
0155         ret = PTR_ERR(fb);
0156         goto out_unref;
0157     }
0158 
0159     return fb;
0160 
0161 out_unref:
0162     for (i = 0; i < n; i++)
0163         drm_gem_object_put(bos[i]);
0164     return ERR_PTR(ret);
0165 }
0166 
0167 static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
0168         const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
0169 {
0170     const struct drm_format_info *info = drm_get_format_info(dev,
0171                                  mode_cmd);
0172     struct msm_drm_private *priv = dev->dev_private;
0173     struct msm_kms *kms = priv->kms;
0174     struct msm_framebuffer *msm_fb = NULL;
0175     struct drm_framebuffer *fb;
0176     const struct msm_format *format;
0177     int ret, i, n;
0178 
0179     drm_dbg_state(dev, "create framebuffer: mode_cmd=%p (%dx%d@%4.4s)",
0180             mode_cmd, mode_cmd->width, mode_cmd->height,
0181             (char *)&mode_cmd->pixel_format);
0182 
0183     n = info->num_planes;
0184     format = kms->funcs->get_format(kms, mode_cmd->pixel_format,
0185             mode_cmd->modifier[0]);
0186     if (!format) {
0187         DRM_DEV_ERROR(dev->dev, "unsupported pixel format: %4.4s\n",
0188                 (char *)&mode_cmd->pixel_format);
0189         ret = -EINVAL;
0190         goto fail;
0191     }
0192 
0193     msm_fb = kzalloc(sizeof(*msm_fb), GFP_KERNEL);
0194     if (!msm_fb) {
0195         ret = -ENOMEM;
0196         goto fail;
0197     }
0198 
0199     fb = &msm_fb->base;
0200 
0201     msm_fb->format = format;
0202 
0203     if (n > ARRAY_SIZE(fb->obj)) {
0204         ret = -EINVAL;
0205         goto fail;
0206     }
0207 
0208     for (i = 0; i < n; i++) {
0209         unsigned int width = mode_cmd->width / (i ? info->hsub : 1);
0210         unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
0211         unsigned int min_size;
0212 
0213         min_size = (height - 1) * mode_cmd->pitches[i]
0214              + width * info->cpp[i]
0215              + mode_cmd->offsets[i];
0216 
0217         if (bos[i]->size < min_size) {
0218             ret = -EINVAL;
0219             goto fail;
0220         }
0221 
0222         msm_fb->base.obj[i] = bos[i];
0223     }
0224 
0225     drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
0226 
0227     ret = drm_framebuffer_init(dev, fb, &msm_framebuffer_funcs);
0228     if (ret) {
0229         DRM_DEV_ERROR(dev->dev, "framebuffer init failed: %d\n", ret);
0230         goto fail;
0231     }
0232 
0233     refcount_set(&msm_fb->dirtyfb, 1);
0234 
0235     drm_dbg_state(dev, "create: FB ID: %d (%p)", fb->base.id, fb);
0236 
0237     return fb;
0238 
0239 fail:
0240     kfree(msm_fb);
0241 
0242     return ERR_PTR(ret);
0243 }
0244 
0245 struct drm_framebuffer *
0246 msm_alloc_stolen_fb(struct drm_device *dev, int w, int h, int p, uint32_t format)
0247 {
0248     struct drm_mode_fb_cmd2 mode_cmd = {
0249         .pixel_format = format,
0250         .width = w,
0251         .height = h,
0252         .pitches = { p },
0253     };
0254     struct drm_gem_object *bo;
0255     struct drm_framebuffer *fb;
0256     int size;
0257 
0258     /* allocate backing bo */
0259     size = mode_cmd.pitches[0] * mode_cmd.height;
0260     DBG("allocating %d bytes for fb %d", size, dev->primary->index);
0261     bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN);
0262     if (IS_ERR(bo)) {
0263         dev_warn(dev->dev, "could not allocate stolen bo\n");
0264         /* try regular bo: */
0265         bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC);
0266     }
0267     if (IS_ERR(bo)) {
0268         DRM_DEV_ERROR(dev->dev, "failed to allocate buffer object\n");
0269         return ERR_CAST(bo);
0270     }
0271 
0272     msm_gem_object_set_name(bo, "stolenfb");
0273 
0274     fb = msm_framebuffer_init(dev, &mode_cmd, &bo);
0275     if (IS_ERR(fb)) {
0276         DRM_DEV_ERROR(dev->dev, "failed to allocate fb\n");
0277         /* note: if fb creation failed, we can't rely on fb destroy
0278          * to unref the bo:
0279          */
0280         drm_gem_object_put(bo);
0281         return ERR_CAST(fb);
0282     }
0283 
0284     return fb;
0285 }