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 "msm_drv.h"
  19#include "msm_kms.h"
  20
  21#include "drm_crtc.h"
  22#include "drm_crtc_helper.h"
  23
  24struct msm_framebuffer {
  25        struct drm_framebuffer base;
  26        const struct msm_format *format;
  27        struct drm_gem_object *planes[MAX_PLANE];
  28};
  29#define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base)
  30
  31
  32static int msm_framebuffer_create_handle(struct drm_framebuffer *fb,
  33                struct drm_file *file_priv,
  34                unsigned int *handle)
  35{
  36        struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
  37        return drm_gem_handle_create(file_priv,
  38                        msm_fb->planes[0], handle);
  39}
  40
  41static void msm_framebuffer_destroy(struct drm_framebuffer *fb)
  42{
  43        struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
  44        int i, n = drm_format_num_planes(fb->pixel_format);
  45
  46        DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
  47
  48        drm_framebuffer_cleanup(fb);
  49
  50        for (i = 0; i < n; i++) {
  51                struct drm_gem_object *bo = msm_fb->planes[i];
  52
  53                drm_gem_object_unreference_unlocked(bo);
  54        }
  55
  56        kfree(msm_fb);
  57}
  58
  59static const struct drm_framebuffer_funcs msm_framebuffer_funcs = {
  60        .create_handle = msm_framebuffer_create_handle,
  61        .destroy = msm_framebuffer_destroy,
  62};
  63
  64#ifdef CONFIG_DEBUG_FS
  65void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
  66{
  67        struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
  68        int i, n = drm_format_num_planes(fb->pixel_format);
  69
  70        seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n",
  71                        fb->width, fb->height, (char *)&fb->pixel_format,
  72                        drm_framebuffer_read_refcount(fb), fb->base.id);
  73
  74        for (i = 0; i < n; i++) {
  75                seq_printf(m, "   %d: offset=%d pitch=%d, obj: ",
  76                                i, fb->offsets[i], fb->pitches[i]);
  77                msm_gem_describe(msm_fb->planes[i], m);
  78        }
  79}
  80#endif
  81
  82/* prepare/pin all the fb's bo's for scanout.  Note that it is not valid
  83 * to prepare an fb more multiple different initiator 'id's.  But that
  84 * should be fine, since only the scanout (mdpN) side of things needs
  85 * this, the gpu doesn't care about fb's.
  86 */
  87int msm_framebuffer_prepare(struct drm_framebuffer *fb, int id)
  88{
  89        struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
  90        int ret, i, n = drm_format_num_planes(fb->pixel_format);
  91        uint64_t iova;
  92
  93        for (i = 0; i < n; i++) {
  94                ret = msm_gem_get_iova(msm_fb->planes[i], id, &iova);
  95                DBG("FB[%u]: iova[%d]: %08llx (%d)", fb->base.id, i, iova, ret);
  96                if (ret)
  97                        return ret;
  98        }
  99
 100        return 0;
 101}
 102
 103void msm_framebuffer_cleanup(struct drm_framebuffer *fb, int id)
 104{
 105        struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
 106        int i, n = drm_format_num_planes(fb->pixel_format);
 107
 108        for (i = 0; i < n; i++)
 109                msm_gem_put_iova(msm_fb->planes[i], id);
 110}
 111
 112uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb, int id, int plane)
 113{
 114        struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
 115        if (!msm_fb->planes[plane])
 116                return 0;
 117        return msm_gem_iova(msm_fb->planes[plane], id) + fb->offsets[plane];
 118}
 119
 120struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
 121{
 122        struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
 123        return msm_fb->planes[plane];
 124}
 125
 126const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb)
 127{
 128        struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
 129        return msm_fb->format;
 130}
 131
 132struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
 133                struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd)
 134{
 135        struct drm_gem_object *bos[4] = {0};
 136        struct drm_framebuffer *fb;
 137        int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format);
 138
 139        for (i = 0; i < n; i++) {
 140                bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
 141                if (!bos[i]) {
 142                        ret = -ENXIO;
 143                        goto out_unref;
 144                }
 145        }
 146
 147        fb = msm_framebuffer_init(dev, mode_cmd, bos);
 148        if (IS_ERR(fb)) {
 149                ret = PTR_ERR(fb);
 150                goto out_unref;
 151        }
 152
 153        return fb;
 154
 155out_unref:
 156        for (i = 0; i < n; i++)
 157                drm_gem_object_unreference_unlocked(bos[i]);
 158        return ERR_PTR(ret);
 159}
 160
 161struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
 162                const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
 163{
 164        struct msm_drm_private *priv = dev->dev_private;
 165        struct msm_kms *kms = priv->kms;
 166        struct msm_framebuffer *msm_fb = NULL;
 167        struct drm_framebuffer *fb;
 168        const struct msm_format *format;
 169        int ret, i, n;
 170        unsigned int hsub, vsub;
 171
 172        DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
 173                        dev, mode_cmd, mode_cmd->width, mode_cmd->height,
 174                        (char *)&mode_cmd->pixel_format);
 175
 176        n = drm_format_num_planes(mode_cmd->pixel_format);
 177        hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
 178        vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
 179
 180        format = kms->funcs->get_format(kms, mode_cmd->pixel_format);
 181        if (!format) {
 182                dev_err(dev->dev, "unsupported pixel format: %4.4s\n",
 183                                (char *)&mode_cmd->pixel_format);
 184                ret = -EINVAL;
 185                goto fail;
 186        }
 187
 188        msm_fb = kzalloc(sizeof(*msm_fb), GFP_KERNEL);
 189        if (!msm_fb) {
 190                ret = -ENOMEM;
 191                goto fail;
 192        }
 193
 194        fb = &msm_fb->base;
 195
 196        msm_fb->format = format;
 197
 198        if (n > ARRAY_SIZE(msm_fb->planes)) {
 199                ret = -EINVAL;
 200                goto fail;
 201        }
 202
 203        for (i = 0; i < n; i++) {
 204                unsigned int width = mode_cmd->width / (i ? hsub : 1);
 205                unsigned int height = mode_cmd->height / (i ? vsub : 1);
 206                unsigned int min_size;
 207
 208                min_size = (height - 1) * mode_cmd->pitches[i]
 209                         + width * drm_format_plane_cpp(mode_cmd->pixel_format, i)
 210                         + mode_cmd->offsets[i];
 211
 212                if (bos[i]->size < min_size) {
 213                        ret = -EINVAL;
 214                        goto fail;
 215                }
 216
 217                msm_fb->planes[i] = bos[i];
 218        }
 219
 220        drm_helper_mode_fill_fb_struct(fb, mode_cmd);
 221
 222        ret = drm_framebuffer_init(dev, fb, &msm_framebuffer_funcs);
 223        if (ret) {
 224                dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
 225                goto fail;
 226        }
 227
 228        DBG("create: FB ID: %d (%p)", fb->base.id, fb);
 229
 230        return fb;
 231
 232fail:
 233        kfree(msm_fb);
 234
 235        return ERR_PTR(ret);
 236}
 237