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 #include <linux/devfreq.h>
9
10 #include <drm/drm.h>
11 #include <drm/drm_atomic.h>
12 #include <drm/drm_damage_helper.h>
13 #include <drm/drm_fb_helper.h>
14 #include <drm/drm_fourcc.h>
15 #include <drm/drm_gem_framebuffer_helper.h>
16 #include <drm/drm_probe_helper.h>
17 #include <soc/rockchip/rockchip_dmc.h>
18
19 #include "rockchip_drm_drv.h"
20 #include "rockchip_drm_fb.h"
21 #include "rockchip_drm_gem.h"
22 #include "rockchip_drm_logo.h"
23
is_rockchip_logo_fb(struct drm_framebuffer * fb)24 static bool is_rockchip_logo_fb(struct drm_framebuffer *fb)
25 {
26 return fb->flags & ROCKCHIP_DRM_MODE_LOGO_FB ? true : false;
27 }
28
rockchip_drm_fb_destroy(struct drm_framebuffer * fb)29 static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
30 {
31 int i = 0;
32
33 drm_framebuffer_cleanup(fb);
34
35 if (is_rockchip_logo_fb(fb)) {
36 struct rockchip_drm_logo_fb *rockchip_logo_fb = to_rockchip_logo_fb(fb);
37
38 #ifndef MODULE
39 rockchip_free_loader_memory(fb->dev);
40 #endif
41 kfree(rockchip_logo_fb);
42 } else {
43 for (i = 0; i < 0x4; i++) {
44 if (fb->obj[i]) {
45 drm_gem_object_put(fb->obj[i]);
46 }
47 }
48
49 kfree(fb);
50 }
51 }
52
53 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
54 .destroy = rockchip_drm_fb_destroy,
55 .create_handle = drm_gem_fb_create_handle,
56 .dirty = drm_atomic_helper_dirtyfb,
57 };
58
rockchip_fb_alloc(struct drm_device * dev,const struct drm_mode_fb_cmd2 * mode_cmd,struct drm_gem_object ** obj,unsigned int num_planes)59 static struct drm_framebuffer *rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
60 struct drm_gem_object **obj, unsigned int num_planes)
61 {
62 struct drm_framebuffer *fb;
63 int ret;
64 int i;
65
66 fb = kzalloc(sizeof(*fb), GFP_KERNEL);
67 if (!fb) {
68 return ERR_PTR(-ENOMEM);
69 }
70
71 drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
72
73 for (i = 0; i < num_planes; i++) {
74 fb->obj[i] = obj[i];
75 }
76
77 ret = drm_framebuffer_init(dev, fb, &rockchip_drm_fb_funcs);
78 if (ret) {
79 DRM_DEV_ERROR(dev->dev, "Failed to initialize framebuffer: %d\n", ret);
80 kfree(fb);
81 return ERR_PTR(ret);
82 }
83
84 return fb;
85 }
86
rockchip_drm_logo_fb_alloc(struct drm_device * dev,const struct drm_mode_fb_cmd2 * mode_cmd,struct rockchip_logo * logo)87 struct drm_framebuffer *rockchip_drm_logo_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
88 struct rockchip_logo *logo)
89 {
90 int ret = 0;
91 struct rockchip_drm_logo_fb *rockchip_logo_fb;
92 struct drm_framebuffer *fb;
93
94 rockchip_logo_fb = kzalloc(sizeof(*rockchip_logo_fb), GFP_KERNEL);
95 if (!rockchip_logo_fb) {
96 return ERR_PTR(-ENOMEM);
97 }
98 fb = &rockchip_logo_fb->fb;
99
100 drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
101
102 ret = drm_framebuffer_init(dev, fb, &rockchip_drm_fb_funcs);
103 if (ret) {
104 DRM_DEV_ERROR(dev->dev, "Failed to initialize rockchip logo fb: %d\n", ret);
105 kfree(rockchip_logo_fb);
106 return ERR_PTR(ret);
107 }
108
109 fb->flags |= ROCKCHIP_DRM_MODE_LOGO_FB;
110 rockchip_logo_fb->logo = logo;
111 rockchip_logo_fb->fb.obj[0] = &rockchip_logo_fb->rk_obj.base;
112 rockchip_logo_fb->rk_obj.dma_addr = logo->dma_addr;
113 rockchip_logo_fb->rk_obj.kvaddr = logo->kvaddr;
114 logo->count++;
115
116 return &rockchip_logo_fb->fb;
117 }
118
rockchip_drm_bandwidth_atomic_check(struct drm_device * dev,struct drm_atomic_state * state,struct dmcfreq_vop_info * vop_bw_info)119 static int rockchip_drm_bandwidth_atomic_check(struct drm_device *dev, struct drm_atomic_state *state,
120 struct dmcfreq_vop_info *vop_bw_info)
121 {
122 struct rockchip_drm_private *priv = dev->dev_private;
123 struct drm_crtc_state *old_crtc_state;
124 const struct rockchip_crtc_funcs *funcs;
125 struct drm_crtc *crtc;
126 int i;
127
128 vop_bw_info->line_bw_mbyte = 0;
129 vop_bw_info->frame_bw_mbyte = 0;
130 vop_bw_info->plane_num = 0;
131
132 for_each_old_crtc_in_state(state, crtc, old_crtc_state, i)
133 {
134 funcs = priv->crtc_funcs[drm_crtc_index(crtc)];
135 if (funcs && funcs->bandwidth) {
136 funcs->bandwidth(crtc, old_crtc_state, vop_bw_info);
137 }
138 }
139
140 return 0;
141 }
142
143 /**
144 * rockchip_drm_atomic_helper_commit_tail_rpm - commit atomic update to hardware
145 * @old_state: new modeset state to be committed
146 *
147 * This is an alternative implementation for the
148 * &drm_mode_config_helper_funcs.atomic_commit_tail hook, for drivers
149 * that support runtime_pm or need the CRTC to be enabled to perform a
150 * commit. Otherwise, one should use the default implementation
151 * drm_atomic_helper_commit_tail().
152 */
rockchip_drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state * old_state)153 static void rockchip_drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state *old_state)
154 {
155 struct drm_device *dev = old_state->dev;
156 struct rockchip_drm_private *prv = dev->dev_private;
157 struct dmcfreq_vop_info vop_bw_info;
158
159 drm_atomic_helper_commit_modeset_disables(dev, old_state);
160
161 drm_atomic_helper_commit_modeset_enables(dev, old_state);
162
163 rockchip_drm_bandwidth_atomic_check(dev, old_state, &vop_bw_info);
164
165 rockchip_dmcfreq_vop_bandwidth_update(&vop_bw_info);
166
167 mutex_lock(&prv->ovl_lock);
168 drm_atomic_helper_commit_planes(dev, old_state, DRM_PLANE_COMMIT_ACTIVE_ONLY);
169 mutex_unlock(&prv->ovl_lock);
170
171 drm_atomic_helper_fake_vblank(old_state);
172
173 drm_atomic_helper_commit_hw_done(old_state);
174
175 drm_atomic_helper_wait_for_vblanks(dev, old_state);
176
177 drm_atomic_helper_cleanup_planes(dev, old_state);
178 }
179
180 static const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = {
181 .atomic_commit_tail = rockchip_drm_atomic_helper_commit_tail_rpm,
182 };
183
rockchip_fb_create(struct drm_device * dev,struct drm_file * file,const struct drm_mode_fb_cmd2 * mode_cmd)184 static struct drm_framebuffer *rockchip_fb_create(struct drm_device *dev, struct drm_file *file,
185 const struct drm_mode_fb_cmd2 *mode_cmd)
186 {
187 struct drm_afbc_framebuffer *afbc_fb;
188 const struct drm_format_info *info;
189 int ret;
190
191 info = drm_get_format_info(dev, mode_cmd);
192 if (!info) {
193 return ERR_PTR(-ENOMEM);
194 }
195
196 afbc_fb = kzalloc(sizeof(*afbc_fb), GFP_KERNEL);
197 if (!afbc_fb) {
198 return ERR_PTR(-ENOMEM);
199 }
200
201 ret = drm_gem_fb_init_with_funcs(dev, &afbc_fb->base, file, mode_cmd, &rockchip_drm_fb_funcs);
202 if (ret) {
203 kfree(afbc_fb);
204 return ERR_PTR(ret);
205 }
206
207 if (drm_is_afbc(mode_cmd->modifier[0])) {
208 int i;
209
210 ret = drm_gem_fb_afbc_init(dev, mode_cmd, afbc_fb);
211 if (ret) {
212 struct drm_gem_object **obj = afbc_fb->base.obj;
213
214 for (i = 0; i < info->num_planes; ++i) {
215 drm_gem_object_put(obj[i]);
216 }
217
218 kfree(afbc_fb);
219 return ERR_PTR(ret);
220 }
221 }
222
223 return &afbc_fb->base;
224 }
225
rockchip_drm_output_poll_changed(struct drm_device * dev)226 static void rockchip_drm_output_poll_changed(struct drm_device *dev)
227 {
228 struct rockchip_drm_private *private = dev->dev_private;
229 struct drm_fb_helper *fb_helper = private->fbdev_helper;
230
231 if (fb_helper && dev->mode_config.poll_enabled && !private->loader_protect) {
232 drm_fb_helper_hotplug_event(fb_helper);
233 }
234 }
235
236 static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
237 .fb_create = rockchip_fb_create,
238 .output_poll_changed = rockchip_drm_output_poll_changed,
239 .atomic_check = drm_atomic_helper_check,
240 .atomic_commit = drm_atomic_helper_commit,
241 };
242
rockchip_drm_framebuffer_init(struct drm_device * dev,const struct drm_mode_fb_cmd2 * mode_cmd,struct drm_gem_object * obj)243 struct drm_framebuffer *rockchip_drm_framebuffer_init(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
244 struct drm_gem_object *obj)
245 {
246 struct drm_framebuffer *fb;
247
248 fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
249 if (IS_ERR(fb)) {
250 return ERR_CAST(fb);
251 }
252
253 return fb;
254 }
255
rockchip_drm_mode_config_init(struct drm_device * dev)256 void rockchip_drm_mode_config_init(struct drm_device *dev)
257 {
258 dev->mode_config.min_width = 0;
259 dev->mode_config.min_height = 0;
260
261 /*
262 * set max width and height as default value(16384x16384).
263 * this value would be used to check framebuffer size limitation
264 * at drm_mode_addfb().
265 */
266 dev->mode_config.max_width = 0x4000;
267 dev->mode_config.max_height = 0x4000;
268 dev->mode_config.async_page_flip = true;
269
270 dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
271 dev->mode_config.helper_private = &rockchip_mode_config_helpers;
272 }
273