linux/drivers/gpu/drm/msm/msm_fb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2013 Red Hat
   4 * Author: Rob Clark <robdclark@gmail.com>
   5 */
   6
   7#include <drm/drm_crtc.h>
   8#include <drm/drm_damage_helper.h>
   9#include <drm/drm_file.h>
  10#include <drm/drm_fourcc.h>
  11#include <drm/drm_gem_framebuffer_helper.h>
  12#include <drm/drm_probe_helper.h>
  13
  14#include "msm_drv.h"
  15#include "msm_kms.h"
  16#include "msm_gem.h"
  17
  18struct msm_framebuffer {
  19        struct drm_framebuffer base;
  20        const struct msm_format *format;
  21};
  22#define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base)
  23
  24static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
  25                const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos);
  26
  27static const struct drm_framebuffer_funcs msm_framebuffer_funcs = {
  28        .create_handle = drm_gem_fb_create_handle,
  29        .destroy = drm_gem_fb_destroy,
  30        .dirty = drm_atomic_helper_dirtyfb,
  31};
  32
  33#ifdef CONFIG_DEBUG_FS
  34void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
  35{
  36        int i, n = fb->format->num_planes;
  37
  38        seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n",
  39                        fb->width, fb->height, (char *)&fb->format->format,
  40                        drm_framebuffer_read_refcount(fb), fb->base.id);
  41
  42        for (i = 0; i < n; i++) {
  43                seq_printf(m, "   %d: offset=%d pitch=%d, obj: ",
  44                                i, fb->offsets[i], fb->pitches[i]);
  45                msm_gem_describe(fb->obj[i], m);
  46        }
  47}
  48#endif
  49
  50/* prepare/pin all the fb's bo's for scanout.  Note that it is not valid
  51 * to prepare an fb more multiple different initiator 'id's.  But that
  52 * should be fine, since only the scanout (mdpN) side of things needs
  53 * this, the gpu doesn't care about fb's.
  54 */
  55int msm_framebuffer_prepare(struct drm_framebuffer *fb,
  56                struct msm_gem_address_space *aspace)
  57{
  58        int ret, i, n = fb->format->num_planes;
  59        uint64_t iova;
  60
  61        for (i = 0; i < n; i++) {
  62                ret = msm_gem_get_and_pin_iova(fb->obj[i], aspace, &iova);
  63                DBG("FB[%u]: iova[%d]: %08llx (%d)", fb->base.id, i, iova, ret);
  64                if (ret)
  65                        return ret;
  66        }
  67
  68        return 0;
  69}
  70
  71void msm_framebuffer_cleanup(struct drm_framebuffer *fb,
  72                struct msm_gem_address_space *aspace)
  73{
  74        int i, n = fb->format->num_planes;
  75
  76        for (i = 0; i < n; i++)
  77                msm_gem_unpin_iova(fb->obj[i], aspace);
  78}
  79
  80uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb,
  81                struct msm_gem_address_space *aspace, int plane)
  82{
  83        if (!fb->obj[plane])
  84                return 0;
  85        return msm_gem_iova(fb->obj[plane], aspace) + fb->offsets[plane];
  86}
  87
  88struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
  89{
  90        return drm_gem_fb_get_obj(fb, plane);
  91}
  92
  93const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb)
  94{
  95        struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
  96        return msm_fb->format;
  97}
  98
  99struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
 100                struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd)
 101{
 102        const struct drm_format_info *info = drm_get_format_info(dev,
 103                                                                 mode_cmd);
 104        struct drm_gem_object *bos[4] = {0};
 105        struct drm_framebuffer *fb;
 106        int ret, i, n = info->num_planes;
 107
 108        for (i = 0; i < n; i++) {
 109                bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
 110                if (!bos[i]) {
 111                        ret = -ENXIO;
 112                        goto out_unref;
 113                }
 114        }
 115
 116        fb = msm_framebuffer_init(dev, mode_cmd, bos);
 117        if (IS_ERR(fb)) {
 118                ret = PTR_ERR(fb);
 119                goto out_unref;
 120        }
 121
 122        return fb;
 123
 124out_unref:
 125        for (i = 0; i < n; i++)
 126                drm_gem_object_put(bos[i]);
 127        return ERR_PTR(ret);
 128}
 129
 130static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
 131                const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
 132{
 133        const struct drm_format_info *info = drm_get_format_info(dev,
 134                                                                 mode_cmd);
 135        struct msm_drm_private *priv = dev->dev_private;
 136        struct msm_kms *kms = priv->kms;
 137        struct msm_framebuffer *msm_fb = NULL;
 138        struct drm_framebuffer *fb;
 139        const struct msm_format *format;
 140        int ret, i, n;
 141
 142        DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
 143                        dev, mode_cmd, mode_cmd->width, mode_cmd->height,
 144                        (char *)&mode_cmd->pixel_format);
 145
 146        n = info->num_planes;
 147        format = kms->funcs->get_format(kms, mode_cmd->pixel_format,
 148                        mode_cmd->modifier[0]);
 149        if (!format) {
 150                DRM_DEV_ERROR(dev->dev, "unsupported pixel format: %4.4s\n",
 151                                (char *)&mode_cmd->pixel_format);
 152                ret = -EINVAL;
 153                goto fail;
 154        }
 155
 156        msm_fb = kzalloc(sizeof(*msm_fb), GFP_KERNEL);
 157        if (!msm_fb) {
 158                ret = -ENOMEM;
 159                goto fail;
 160        }
 161
 162        fb = &msm_fb->base;
 163
 164        msm_fb->format = format;
 165
 166        if (n > ARRAY_SIZE(fb->obj)) {
 167                ret = -EINVAL;
 168                goto fail;
 169        }
 170
 171        for (i = 0; i < n; i++) {
 172                unsigned int width = mode_cmd->width / (i ? info->hsub : 1);
 173                unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
 174                unsigned int min_size;
 175
 176                min_size = (height - 1) * mode_cmd->pitches[i]
 177                         + width * info->cpp[i]
 178                         + mode_cmd->offsets[i];
 179
 180                if (bos[i]->size < min_size) {
 181                        ret = -EINVAL;
 182                        goto fail;
 183                }
 184
 185                msm_fb->base.obj[i] = bos[i];
 186        }
 187
 188        drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
 189
 190        ret = drm_framebuffer_init(dev, fb, &msm_framebuffer_funcs);
 191        if (ret) {
 192                DRM_DEV_ERROR(dev->dev, "framebuffer init failed: %d\n", ret);
 193                goto fail;
 194        }
 195
 196        DBG("create: FB ID: %d (%p)", fb->base.id, fb);
 197
 198        return fb;
 199
 200fail:
 201        kfree(msm_fb);
 202
 203        return ERR_PTR(ret);
 204}
 205
 206struct drm_framebuffer *
 207msm_alloc_stolen_fb(struct drm_device *dev, int w, int h, int p, uint32_t format)
 208{
 209        struct drm_mode_fb_cmd2 mode_cmd = {
 210                .pixel_format = format,
 211                .width = w,
 212                .height = h,
 213                .pitches = { p },
 214        };
 215        struct drm_gem_object *bo;
 216        struct drm_framebuffer *fb;
 217        int size;
 218
 219        /* allocate backing bo */
 220        size = mode_cmd.pitches[0] * mode_cmd.height;
 221        DBG("allocating %d bytes for fb %d", size, dev->primary->index);
 222        bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN);
 223        if (IS_ERR(bo)) {
 224                dev_warn(dev->dev, "could not allocate stolen bo\n");
 225                /* try regular bo: */
 226                bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC);
 227        }
 228        if (IS_ERR(bo)) {
 229                DRM_DEV_ERROR(dev->dev, "failed to allocate buffer object\n");
 230                return ERR_CAST(bo);
 231        }
 232
 233        msm_gem_object_set_name(bo, "stolenfb");
 234
 235        fb = msm_framebuffer_init(dev, &mode_cmd, &bo);
 236        if (IS_ERR(fb)) {
 237                DRM_DEV_ERROR(dev->dev, "failed to allocate fb\n");
 238                /* note: if fb creation failed, we can't rely on fb destroy
 239                 * to unref the bo:
 240                 */
 241                drm_gem_object_put(bo);
 242                return ERR_CAST(fb);
 243        }
 244
 245        return fb;
 246}
 247