linux/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
   3 * Author:Mark Yao <mark.yao@rock-chips.com>
   4 *
   5 * This software is licensed under the terms of the GNU General Public
   6 * License version 2, as published by the Free Software Foundation, and
   7 * may be copied, distributed, and modified under those terms.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 */
  14
  15#include <linux/kernel.h>
  16#include <drm/drm.h>
  17#include <drm/drmP.h>
  18#include <drm/drm_atomic.h>
  19#include <drm/drm_fb_helper.h>
  20#include <drm/drm_crtc_helper.h>
  21
  22#include "rockchip_drm_drv.h"
  23#include "rockchip_drm_gem.h"
  24
  25#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
  26
  27struct rockchip_drm_fb {
  28        struct drm_framebuffer fb;
  29        struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
  30};
  31
  32struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
  33                                               unsigned int plane)
  34{
  35        struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
  36
  37        if (plane >= ROCKCHIP_MAX_FB_BUFFER)
  38                return NULL;
  39
  40        return rk_fb->obj[plane];
  41}
  42
  43static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
  44{
  45        struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
  46        struct drm_gem_object *obj;
  47        int i;
  48
  49        for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
  50                obj = rockchip_fb->obj[i];
  51                if (obj)
  52                        drm_gem_object_unreference_unlocked(obj);
  53        }
  54
  55        drm_framebuffer_cleanup(fb);
  56        kfree(rockchip_fb);
  57}
  58
  59static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
  60                                         struct drm_file *file_priv,
  61                                         unsigned int *handle)
  62{
  63        struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
  64
  65        return drm_gem_handle_create(file_priv,
  66                                     rockchip_fb->obj[0], handle);
  67}
  68
  69static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
  70        .destroy        = rockchip_drm_fb_destroy,
  71        .create_handle  = rockchip_drm_fb_create_handle,
  72};
  73
  74static struct rockchip_drm_fb *
  75rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
  76                  struct drm_gem_object **obj, unsigned int num_planes)
  77{
  78        struct rockchip_drm_fb *rockchip_fb;
  79        int ret;
  80        int i;
  81
  82        rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
  83        if (!rockchip_fb)
  84                return ERR_PTR(-ENOMEM);
  85
  86        drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
  87
  88        for (i = 0; i < num_planes; i++)
  89                rockchip_fb->obj[i] = obj[i];
  90
  91        ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
  92                                   &rockchip_drm_fb_funcs);
  93        if (ret) {
  94                dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
  95                        ret);
  96                kfree(rockchip_fb);
  97                return ERR_PTR(ret);
  98        }
  99
 100        return rockchip_fb;
 101}
 102
 103static struct drm_framebuffer *
 104rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
 105                        const struct drm_mode_fb_cmd2 *mode_cmd)
 106{
 107        struct rockchip_drm_fb *rockchip_fb;
 108        struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
 109        struct drm_gem_object *obj;
 110        unsigned int hsub;
 111        unsigned int vsub;
 112        int num_planes;
 113        int ret;
 114        int i;
 115
 116        hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
 117        vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
 118        num_planes = min(drm_format_num_planes(mode_cmd->pixel_format),
 119                         ROCKCHIP_MAX_FB_BUFFER);
 120
 121        for (i = 0; i < num_planes; i++) {
 122                unsigned int width = mode_cmd->width / (i ? hsub : 1);
 123                unsigned int height = mode_cmd->height / (i ? vsub : 1);
 124                unsigned int min_size;
 125
 126                obj = drm_gem_object_lookup(dev, file_priv,
 127                                            mode_cmd->handles[i]);
 128                if (!obj) {
 129                        dev_err(dev->dev, "Failed to lookup GEM object\n");
 130                        ret = -ENXIO;
 131                        goto err_gem_object_unreference;
 132                }
 133
 134                min_size = (height - 1) * mode_cmd->pitches[i] +
 135                        mode_cmd->offsets[i] +
 136                        width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
 137
 138                if (obj->size < min_size) {
 139                        drm_gem_object_unreference_unlocked(obj);
 140                        ret = -EINVAL;
 141                        goto err_gem_object_unreference;
 142                }
 143                objs[i] = obj;
 144        }
 145
 146        rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
 147        if (IS_ERR(rockchip_fb)) {
 148                ret = PTR_ERR(rockchip_fb);
 149                goto err_gem_object_unreference;
 150        }
 151
 152        return &rockchip_fb->fb;
 153
 154err_gem_object_unreference:
 155        for (i--; i >= 0; i--)
 156                drm_gem_object_unreference_unlocked(objs[i]);
 157        return ERR_PTR(ret);
 158}
 159
 160static void rockchip_drm_output_poll_changed(struct drm_device *dev)
 161{
 162        struct rockchip_drm_private *private = dev->dev_private;
 163        struct drm_fb_helper *fb_helper = &private->fbdev_helper;
 164
 165        if (fb_helper)
 166                drm_fb_helper_hotplug_event(fb_helper);
 167}
 168
 169static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
 170{
 171        struct rockchip_drm_private *priv = crtc->dev->dev_private;
 172        int pipe = drm_crtc_index(crtc);
 173        const struct rockchip_crtc_funcs *crtc_funcs = priv->crtc_funcs[pipe];
 174
 175        if (crtc_funcs && crtc_funcs->wait_for_update)
 176                crtc_funcs->wait_for_update(crtc);
 177}
 178
 179/*
 180 * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
 181 * have hardware counters for neither vblanks nor scanlines, which results in
 182 * a race where:
 183 *                              | <-- HW vsync irq and reg take effect
 184 *             plane_commit --> |
 185 *      get_vblank and wait --> |
 186 *                              | <-- handle_vblank, vblank->count + 1
 187 *               cleanup_fb --> |
 188 *              iommu crash --> |
 189 *                              | <-- HW vsync irq and reg take effect
 190 *
 191 * This function is equivalent but uses rockchip_crtc_wait_for_update() instead
 192 * of waiting for vblank_count to change.
 193 */
 194static void
 195rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state)
 196{
 197        struct drm_crtc_state *old_crtc_state;
 198        struct drm_crtc *crtc;
 199        int i, ret;
 200
 201        for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
 202                /* No one cares about the old state, so abuse it for tracking
 203                 * and store whether we hold a vblank reference (and should do a
 204                 * vblank wait) in the ->enable boolean.
 205                 */
 206                old_crtc_state->enable = false;
 207
 208                if (!crtc->state->active)
 209                        continue;
 210
 211                if (!drm_atomic_helper_framebuffer_changed(dev,
 212                                old_state, crtc))
 213                        continue;
 214
 215                ret = drm_crtc_vblank_get(crtc);
 216                if (ret != 0)
 217                        continue;
 218
 219                old_crtc_state->enable = true;
 220        }
 221
 222        for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
 223                if (!old_crtc_state->enable)
 224                        continue;
 225
 226                rockchip_crtc_wait_for_update(crtc);
 227                drm_crtc_vblank_put(crtc);
 228        }
 229}
 230
 231static void
 232rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit)
 233{
 234        struct drm_atomic_state *state = commit->state;
 235        struct drm_device *dev = commit->dev;
 236
 237        /*
 238         * TODO: do fence wait here.
 239         */
 240
 241        /*
 242         * Rockchip crtc support runtime PM, can't update display planes
 243         * when crtc is disabled.
 244         *
 245         * drm_atomic_helper_commit comments detail that:
 246         *     For drivers supporting runtime PM the recommended sequence is
 247         *
 248         *     drm_atomic_helper_commit_modeset_disables(dev, state);
 249         *
 250         *     drm_atomic_helper_commit_modeset_enables(dev, state);
 251         *
 252         *     drm_atomic_helper_commit_planes(dev, state, true);
 253         *
 254         * See the kerneldoc entries for these three functions for more details.
 255         */
 256        drm_atomic_helper_commit_modeset_disables(dev, state);
 257
 258        drm_atomic_helper_commit_modeset_enables(dev, state);
 259
 260        drm_atomic_helper_commit_planes(dev, state, true);
 261
 262        rockchip_atomic_wait_for_complete(dev, state);
 263
 264        drm_atomic_helper_cleanup_planes(dev, state);
 265
 266        drm_atomic_state_free(state);
 267}
 268
 269void rockchip_drm_atomic_work(struct work_struct *work)
 270{
 271        struct rockchip_atomic_commit *commit = container_of(work,
 272                                        struct rockchip_atomic_commit, work);
 273
 274        rockchip_atomic_commit_complete(commit);
 275}
 276
 277int rockchip_drm_atomic_commit(struct drm_device *dev,
 278                               struct drm_atomic_state *state,
 279                               bool async)
 280{
 281        struct rockchip_drm_private *private = dev->dev_private;
 282        struct rockchip_atomic_commit *commit = &private->commit;
 283        int ret;
 284
 285        ret = drm_atomic_helper_prepare_planes(dev, state);
 286        if (ret)
 287                return ret;
 288
 289        /* serialize outstanding asynchronous commits */
 290        mutex_lock(&commit->lock);
 291        flush_work(&commit->work);
 292
 293        drm_atomic_helper_swap_state(dev, state);
 294
 295        commit->dev = dev;
 296        commit->state = state;
 297
 298        if (async)
 299                schedule_work(&commit->work);
 300        else
 301                rockchip_atomic_commit_complete(commit);
 302
 303        mutex_unlock(&commit->lock);
 304
 305        return 0;
 306}
 307
 308static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
 309        .fb_create = rockchip_user_fb_create,
 310        .output_poll_changed = rockchip_drm_output_poll_changed,
 311        .atomic_check = drm_atomic_helper_check,
 312        .atomic_commit = rockchip_drm_atomic_commit,
 313};
 314
 315struct drm_framebuffer *
 316rockchip_drm_framebuffer_init(struct drm_device *dev,
 317                              const struct drm_mode_fb_cmd2 *mode_cmd,
 318                              struct drm_gem_object *obj)
 319{
 320        struct rockchip_drm_fb *rockchip_fb;
 321
 322        rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
 323        if (IS_ERR(rockchip_fb))
 324                return NULL;
 325
 326        return &rockchip_fb->fb;
 327}
 328
 329void rockchip_drm_mode_config_init(struct drm_device *dev)
 330{
 331        dev->mode_config.min_width = 0;
 332        dev->mode_config.min_height = 0;
 333
 334        /*
 335         * set max width and height as default value(4096x4096).
 336         * this value would be used to check framebuffer size limitation
 337         * at drm_mode_addfb().
 338         */
 339        dev->mode_config.max_width = 4096;
 340        dev->mode_config.max_height = 4096;
 341
 342        dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
 343}
 344