1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
4 * Author:Mark Yao <mark.yao@rock-chips.com>
5 */
6
7 #include <linux/kernel.h>
8
9 #include <drm/drm.h>
10 #include <drm/drm_atomic.h>
11 #include <drm/drm_damage_helper.h>
12 #include <drm/drm_fb_helper.h>
13 #include <drm/drm_fourcc.h>
14 #include <drm/drm_framebuffer.h>
15 #include <drm/drm_gem_framebuffer_helper.h>
16 #include <drm/drm_probe_helper.h>
17
18 #include "rockchip_drm_drv.h"
19 #include "rockchip_drm_fb.h"
20 #include "rockchip_drm_gem.h"
21
22 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
23 .destroy = drm_gem_fb_destroy,
24 .create_handle = drm_gem_fb_create_handle,
25 .dirty = drm_atomic_helper_dirtyfb,
26 };
27
28 static struct drm_framebuffer *
rockchip_fb_alloc(struct drm_device * dev,const struct drm_mode_fb_cmd2 * mode_cmd,struct drm_gem_object ** obj,unsigned int num_planes)29 rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
30 struct drm_gem_object **obj, unsigned int num_planes)
31 {
32 struct drm_framebuffer *fb;
33 int ret;
34 int i;
35
36 fb = kzalloc(sizeof(*fb), GFP_KERNEL);
37 if (!fb)
38 return ERR_PTR(-ENOMEM);
39
40 drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
41
42 for (i = 0; i < num_planes; i++)
43 fb->obj[i] = obj[i];
44
45 ret = drm_framebuffer_init(dev, fb, &rockchip_drm_fb_funcs);
46 if (ret) {
47 DRM_DEV_ERROR(dev->dev,
48 "Failed to initialize framebuffer: %d\n",
49 ret);
50 kfree(fb);
51 return ERR_PTR(ret);
52 }
53
54 return fb;
55 }
56
57 static const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = {
58 .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
59 };
60
61 static struct drm_framebuffer *
rockchip_fb_create(struct drm_device * dev,struct drm_file * file,const struct drm_mode_fb_cmd2 * mode_cmd)62 rockchip_fb_create(struct drm_device *dev, struct drm_file *file,
63 const struct drm_mode_fb_cmd2 *mode_cmd)
64 {
65 struct drm_afbc_framebuffer *afbc_fb;
66 const struct drm_format_info *info;
67 int ret;
68
69 info = drm_get_format_info(dev, mode_cmd);
70 if (!info)
71 return ERR_PTR(-ENOMEM);
72
73 afbc_fb = kzalloc(sizeof(*afbc_fb), GFP_KERNEL);
74 if (!afbc_fb)
75 return ERR_PTR(-ENOMEM);
76
77 ret = drm_gem_fb_init_with_funcs(dev, &afbc_fb->base, file, mode_cmd,
78 &rockchip_drm_fb_funcs);
79 if (ret) {
80 kfree(afbc_fb);
81 return ERR_PTR(ret);
82 }
83
84 if (drm_is_afbc(mode_cmd->modifier[0])) {
85 int ret, i;
86
87 ret = drm_gem_fb_afbc_init(dev, mode_cmd, afbc_fb);
88 if (ret) {
89 struct drm_gem_object **obj = afbc_fb->base.obj;
90
91 for (i = 0; i < info->num_planes; ++i)
92 drm_gem_object_put(obj[i]);
93
94 kfree(afbc_fb);
95 return ERR_PTR(ret);
96 }
97 }
98
99 return &afbc_fb->base;
100 }
101
102 static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
103 .fb_create = rockchip_fb_create,
104 .output_poll_changed = drm_fb_helper_output_poll_changed,
105 .atomic_check = drm_atomic_helper_check,
106 .atomic_commit = drm_atomic_helper_commit,
107 };
108
109 struct drm_framebuffer *
rockchip_drm_framebuffer_init(struct drm_device * dev,const struct drm_mode_fb_cmd2 * mode_cmd,struct drm_gem_object * obj)110 rockchip_drm_framebuffer_init(struct drm_device *dev,
111 const struct drm_mode_fb_cmd2 *mode_cmd,
112 struct drm_gem_object *obj)
113 {
114 struct drm_framebuffer *fb;
115
116 fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
117 if (IS_ERR(fb))
118 return ERR_CAST(fb);
119
120 return fb;
121 }
122
rockchip_drm_mode_config_init(struct drm_device * dev)123 void rockchip_drm_mode_config_init(struct drm_device *dev)
124 {
125 dev->mode_config.min_width = 0;
126 dev->mode_config.min_height = 0;
127
128 /*
129 * set max width and height as default value(4096x4096).
130 * this value would be used to check framebuffer size limitation
131 * at drm_mode_addfb().
132 */
133 dev->mode_config.max_width = 4096;
134 dev->mode_config.max_height = 4096;
135
136 dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
137 dev->mode_config.helper_private = &rockchip_mode_config_helpers;
138
139 dev->mode_config.normalize_zpos = true;
140 }
141