• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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