Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / gpu / drm / rockchip / rockchip_drm_fb.c
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
new file mode 100644 (file)
index 0000000..77d5289
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <drm/drm.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
+
+struct rockchip_drm_fb {
+       struct drm_framebuffer fb;
+       struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
+};
+
+struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
+                                              unsigned int plane)
+{
+       struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
+
+       if (plane >= ROCKCHIP_MAX_FB_BUFFER)
+               return NULL;
+
+       return rk_fb->obj[plane];
+}
+EXPORT_SYMBOL_GPL(rockchip_fb_get_gem_obj);
+
+static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+       struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+       struct drm_gem_object *obj;
+       int i;
+
+       for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
+               obj = rockchip_fb->obj[i];
+               if (obj)
+                       drm_gem_object_unreference_unlocked(obj);
+       }
+
+       drm_framebuffer_cleanup(fb);
+       kfree(rockchip_fb);
+}
+
+static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
+                                        struct drm_file *file_priv,
+                                        unsigned int *handle)
+{
+       struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+
+       return drm_gem_handle_create(file_priv,
+                                    rockchip_fb->obj[0], handle);
+}
+
+static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
+       .destroy        = rockchip_drm_fb_destroy,
+       .create_handle  = rockchip_drm_fb_create_handle,
+};
+
+static struct rockchip_drm_fb *
+rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
+                 struct drm_gem_object **obj, unsigned int num_planes)
+{
+       struct rockchip_drm_fb *rockchip_fb;
+       int ret;
+       int i;
+
+       rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
+       if (!rockchip_fb)
+               return ERR_PTR(-ENOMEM);
+
+       drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
+
+       for (i = 0; i < num_planes; i++)
+               rockchip_fb->obj[i] = obj[i];
+
+       ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
+                                  &rockchip_drm_fb_funcs);
+       if (ret) {
+               dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
+                       ret);
+               kfree(rockchip_fb);
+               return ERR_PTR(ret);
+       }
+
+       return rockchip_fb;
+}
+
+static struct drm_framebuffer *
+rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+                       struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       struct rockchip_drm_fb *rockchip_fb;
+       struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
+       struct drm_gem_object *obj;
+       unsigned int hsub;
+       unsigned int vsub;
+       int num_planes;
+       int ret;
+       int i;
+
+       hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
+       vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
+       num_planes = min(drm_format_num_planes(mode_cmd->pixel_format),
+                        ROCKCHIP_MAX_FB_BUFFER);
+
+       for (i = 0; i < num_planes; i++) {
+               unsigned int width = mode_cmd->width / (i ? hsub : 1);
+               unsigned int height = mode_cmd->height / (i ? vsub : 1);
+               unsigned int min_size;
+
+               obj = drm_gem_object_lookup(dev, file_priv,
+                                           mode_cmd->handles[i]);
+               if (!obj) {
+                       dev_err(dev->dev, "Failed to lookup GEM object\n");
+                       ret = -ENXIO;
+                       goto err_gem_object_unreference;
+               }
+
+               min_size = (height - 1) * mode_cmd->pitches[i] +
+                       mode_cmd->offsets[i] +
+                       width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
+
+               if (obj->size < min_size) {
+                       drm_gem_object_unreference_unlocked(obj);
+                       ret = -EINVAL;
+                       goto err_gem_object_unreference;
+               }
+               objs[i] = obj;
+       }
+
+       rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
+       if (IS_ERR(rockchip_fb)) {
+               ret = PTR_ERR(rockchip_fb);
+               goto err_gem_object_unreference;
+       }
+
+       return &rockchip_fb->fb;
+
+err_gem_object_unreference:
+       for (i--; i >= 0; i--)
+               drm_gem_object_unreference_unlocked(objs[i]);
+       return ERR_PTR(ret);
+}
+
+static void rockchip_drm_output_poll_changed(struct drm_device *dev)
+{
+       struct rockchip_drm_private *private = dev->dev_private;
+       struct drm_fb_helper *fb_helper = &private->fbdev_helper;
+
+       drm_fb_helper_hotplug_event(fb_helper);
+}
+
+static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
+       .fb_create = rockchip_user_fb_create,
+       .output_poll_changed = rockchip_drm_output_poll_changed,
+};
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+                             struct drm_mode_fb_cmd2 *mode_cmd,
+                             struct drm_gem_object *obj)
+{
+       struct rockchip_drm_fb *rockchip_fb;
+
+       rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
+       if (IS_ERR(rockchip_fb))
+               return NULL;
+
+       return &rockchip_fb->fb;
+}
+
+void rockchip_drm_mode_config_init(struct drm_device *dev)
+{
+       dev->mode_config.min_width = 0;
+       dev->mode_config.min_height = 0;
+
+       /*
+        * set max width and height as default value(4096x4096).
+        * this value would be used to check framebuffer size limitation
+        * at drm_mode_addfb().
+        */
+       dev->mode_config.max_width = 4096;
+       dev->mode_config.max_height = 4096;
+
+       dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
+}