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