0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/console.h>
0012 #include <linux/dma-mapping.h>
0013 #include <linux/vmalloc.h>
0014
0015 #include <drm/drm_crtc.h>
0016 #include <drm/drm_fb_helper.h>
0017 #include <drm/drm_fourcc.h>
0018 #include <drm/drm_framebuffer.h>
0019 #include <drm/drm_prime.h>
0020 #include <drm/drm_probe_helper.h>
0021 #include <drm/exynos_drm.h>
0022
0023 #include "exynos_drm_drv.h"
0024 #include "exynos_drm_fb.h"
0025 #include "exynos_drm_fbdev.h"
0026
0027 #define MAX_CONNECTOR 4
0028 #define PREFERRED_BPP 32
0029
0030 #define to_exynos_fbdev(x) container_of(x, struct exynos_drm_fbdev,\
0031 drm_fb_helper)
0032
0033 struct exynos_drm_fbdev {
0034 struct drm_fb_helper drm_fb_helper;
0035 struct exynos_drm_gem *exynos_gem;
0036 };
0037
0038 static int exynos_drm_fb_mmap(struct fb_info *info,
0039 struct vm_area_struct *vma)
0040 {
0041 struct drm_fb_helper *helper = info->par;
0042 struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(helper);
0043 struct exynos_drm_gem *exynos_gem = exynos_fbd->exynos_gem;
0044
0045 return drm_gem_prime_mmap(&exynos_gem->base, vma);
0046 }
0047
0048 static const struct fb_ops exynos_drm_fb_ops = {
0049 .owner = THIS_MODULE,
0050 DRM_FB_HELPER_DEFAULT_OPS,
0051 .fb_mmap = exynos_drm_fb_mmap,
0052 .fb_fillrect = drm_fb_helper_cfb_fillrect,
0053 .fb_copyarea = drm_fb_helper_cfb_copyarea,
0054 .fb_imageblit = drm_fb_helper_cfb_imageblit,
0055 };
0056
0057 static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
0058 struct drm_fb_helper_surface_size *sizes,
0059 struct exynos_drm_gem *exynos_gem)
0060 {
0061 struct fb_info *fbi;
0062 struct drm_framebuffer *fb = helper->fb;
0063 unsigned int size = fb->width * fb->height * fb->format->cpp[0];
0064 unsigned long offset;
0065
0066 fbi = drm_fb_helper_alloc_fbi(helper);
0067 if (IS_ERR(fbi)) {
0068 DRM_DEV_ERROR(to_dma_dev(helper->dev),
0069 "failed to allocate fb info.\n");
0070 return PTR_ERR(fbi);
0071 }
0072
0073 fbi->fbops = &exynos_drm_fb_ops;
0074
0075 drm_fb_helper_fill_info(fbi, helper, sizes);
0076
0077 offset = fbi->var.xoffset * fb->format->cpp[0];
0078 offset += fbi->var.yoffset * fb->pitches[0];
0079
0080 fbi->screen_buffer = exynos_gem->kvaddr + offset;
0081 fbi->screen_size = size;
0082 fbi->fix.smem_len = size;
0083
0084 return 0;
0085 }
0086
0087 static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
0088 struct drm_fb_helper_surface_size *sizes)
0089 {
0090 struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
0091 struct exynos_drm_gem *exynos_gem;
0092 struct drm_device *dev = helper->dev;
0093 struct drm_mode_fb_cmd2 mode_cmd = { 0 };
0094 unsigned long size;
0095 int ret;
0096
0097 DRM_DEV_DEBUG_KMS(dev->dev,
0098 "surface width(%d), height(%d) and bpp(%d\n",
0099 sizes->surface_width, sizes->surface_height,
0100 sizes->surface_bpp);
0101
0102 mode_cmd.width = sizes->surface_width;
0103 mode_cmd.height = sizes->surface_height;
0104 mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
0105 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
0106 sizes->surface_depth);
0107
0108 size = mode_cmd.pitches[0] * mode_cmd.height;
0109
0110 exynos_gem = exynos_drm_gem_create(dev, EXYNOS_BO_WC, size, true);
0111 if (IS_ERR(exynos_gem))
0112 return PTR_ERR(exynos_gem);
0113
0114 exynos_fbdev->exynos_gem = exynos_gem;
0115
0116 helper->fb =
0117 exynos_drm_framebuffer_init(dev, &mode_cmd, &exynos_gem, 1);
0118 if (IS_ERR(helper->fb)) {
0119 DRM_DEV_ERROR(dev->dev, "failed to create drm framebuffer.\n");
0120 ret = PTR_ERR(helper->fb);
0121 goto err_destroy_gem;
0122 }
0123
0124 ret = exynos_drm_fbdev_update(helper, sizes, exynos_gem);
0125 if (ret < 0)
0126 goto err_destroy_framebuffer;
0127
0128 return ret;
0129
0130 err_destroy_framebuffer:
0131 drm_framebuffer_cleanup(helper->fb);
0132 err_destroy_gem:
0133 exynos_drm_gem_destroy(exynos_gem);
0134
0135
0136
0137
0138
0139
0140
0141 return ret;
0142 }
0143
0144 static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
0145 .fb_probe = exynos_drm_fbdev_create,
0146 };
0147
0148 int exynos_drm_fbdev_init(struct drm_device *dev)
0149 {
0150 struct exynos_drm_fbdev *fbdev;
0151 struct exynos_drm_private *private = dev->dev_private;
0152 struct drm_fb_helper *helper;
0153 int ret;
0154
0155 if (!dev->mode_config.num_crtc)
0156 return 0;
0157
0158 fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
0159 if (!fbdev)
0160 return -ENOMEM;
0161
0162 private->fb_helper = helper = &fbdev->drm_fb_helper;
0163
0164 drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
0165
0166 ret = drm_fb_helper_init(dev, helper);
0167 if (ret < 0) {
0168 DRM_DEV_ERROR(dev->dev,
0169 "failed to initialize drm fb helper.\n");
0170 goto err_init;
0171 }
0172
0173 ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
0174 if (ret < 0) {
0175 DRM_DEV_ERROR(dev->dev,
0176 "failed to set up hw configuration.\n");
0177 goto err_setup;
0178 }
0179
0180 return 0;
0181
0182 err_setup:
0183 drm_fb_helper_fini(helper);
0184
0185 err_init:
0186 private->fb_helper = NULL;
0187 kfree(fbdev);
0188
0189 return ret;
0190 }
0191
0192 static void exynos_drm_fbdev_destroy(struct drm_device *dev,
0193 struct drm_fb_helper *fb_helper)
0194 {
0195 struct drm_framebuffer *fb;
0196
0197
0198 if (fb_helper->fb && fb_helper->fb->funcs) {
0199 fb = fb_helper->fb;
0200 if (fb)
0201 drm_framebuffer_remove(fb);
0202 }
0203
0204 drm_fb_helper_unregister_fbi(fb_helper);
0205
0206 drm_fb_helper_fini(fb_helper);
0207 }
0208
0209 void exynos_drm_fbdev_fini(struct drm_device *dev)
0210 {
0211 struct exynos_drm_private *private = dev->dev_private;
0212 struct exynos_drm_fbdev *fbdev;
0213
0214 if (!private || !private->fb_helper)
0215 return;
0216
0217 fbdev = to_exynos_fbdev(private->fb_helper);
0218
0219 exynos_drm_fbdev_destroy(dev, private->fb_helper);
0220 kfree(fbdev);
0221 private->fb_helper = NULL;
0222 }
0223