2 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
3 * Author:Mark Yao <mark.yao@rock-chips.com>
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #include <linux/kernel.h>
18 #include <drm/drm_atomic.h>
19 #include <drm/drm_fb_helper.h>
20 #include <drm/drm_crtc_helper.h>
22 #include "rockchip_drm_drv.h"
23 #include "rockchip_drm_gem.h"
25 #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
27 struct rockchip_drm_fb
{
28 struct drm_framebuffer fb
;
29 struct drm_gem_object
*obj
[ROCKCHIP_MAX_FB_BUFFER
];
32 struct drm_gem_object
*rockchip_fb_get_gem_obj(struct drm_framebuffer
*fb
,
35 struct rockchip_drm_fb
*rk_fb
= to_rockchip_fb(fb
);
37 if (plane
>= ROCKCHIP_MAX_FB_BUFFER
)
40 return rk_fb
->obj
[plane
];
42 EXPORT_SYMBOL_GPL(rockchip_fb_get_gem_obj
);
44 static void rockchip_drm_fb_destroy(struct drm_framebuffer
*fb
)
46 struct rockchip_drm_fb
*rockchip_fb
= to_rockchip_fb(fb
);
47 struct drm_gem_object
*obj
;
50 for (i
= 0; i
< ROCKCHIP_MAX_FB_BUFFER
; i
++) {
51 obj
= rockchip_fb
->obj
[i
];
53 drm_gem_object_unreference_unlocked(obj
);
56 drm_framebuffer_cleanup(fb
);
60 static int rockchip_drm_fb_create_handle(struct drm_framebuffer
*fb
,
61 struct drm_file
*file_priv
,
64 struct rockchip_drm_fb
*rockchip_fb
= to_rockchip_fb(fb
);
66 return drm_gem_handle_create(file_priv
,
67 rockchip_fb
->obj
[0], handle
);
70 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs
= {
71 .destroy
= rockchip_drm_fb_destroy
,
72 .create_handle
= rockchip_drm_fb_create_handle
,
75 static struct rockchip_drm_fb
*
76 rockchip_fb_alloc(struct drm_device
*dev
, const struct drm_mode_fb_cmd2
*mode_cmd
,
77 struct drm_gem_object
**obj
, unsigned int num_planes
)
79 struct rockchip_drm_fb
*rockchip_fb
;
83 rockchip_fb
= kzalloc(sizeof(*rockchip_fb
), GFP_KERNEL
);
85 return ERR_PTR(-ENOMEM
);
87 drm_helper_mode_fill_fb_struct(&rockchip_fb
->fb
, mode_cmd
);
89 for (i
= 0; i
< num_planes
; i
++)
90 rockchip_fb
->obj
[i
] = obj
[i
];
92 ret
= drm_framebuffer_init(dev
, &rockchip_fb
->fb
,
93 &rockchip_drm_fb_funcs
);
95 dev_err(dev
->dev
, "Failed to initialize framebuffer: %d\n",
104 static struct drm_framebuffer
*
105 rockchip_user_fb_create(struct drm_device
*dev
, struct drm_file
*file_priv
,
106 const struct drm_mode_fb_cmd2
*mode_cmd
)
108 struct rockchip_drm_fb
*rockchip_fb
;
109 struct drm_gem_object
*objs
[ROCKCHIP_MAX_FB_BUFFER
];
110 struct drm_gem_object
*obj
;
117 hsub
= drm_format_horz_chroma_subsampling(mode_cmd
->pixel_format
);
118 vsub
= drm_format_vert_chroma_subsampling(mode_cmd
->pixel_format
);
119 num_planes
= min(drm_format_num_planes(mode_cmd
->pixel_format
),
120 ROCKCHIP_MAX_FB_BUFFER
);
122 for (i
= 0; i
< num_planes
; i
++) {
123 unsigned int width
= mode_cmd
->width
/ (i
? hsub
: 1);
124 unsigned int height
= mode_cmd
->height
/ (i
? vsub
: 1);
125 unsigned int min_size
;
127 obj
= drm_gem_object_lookup(dev
, file_priv
,
128 mode_cmd
->handles
[i
]);
130 dev_err(dev
->dev
, "Failed to lookup GEM object\n");
132 goto err_gem_object_unreference
;
135 min_size
= (height
- 1) * mode_cmd
->pitches
[i
] +
136 mode_cmd
->offsets
[i
] +
137 width
* drm_format_plane_cpp(mode_cmd
->pixel_format
, i
);
139 if (obj
->size
< min_size
) {
140 drm_gem_object_unreference_unlocked(obj
);
142 goto err_gem_object_unreference
;
147 rockchip_fb
= rockchip_fb_alloc(dev
, mode_cmd
, objs
, i
);
148 if (IS_ERR(rockchip_fb
)) {
149 ret
= PTR_ERR(rockchip_fb
);
150 goto err_gem_object_unreference
;
153 return &rockchip_fb
->fb
;
155 err_gem_object_unreference
:
156 for (i
--; i
>= 0; i
--)
157 drm_gem_object_unreference_unlocked(objs
[i
]);
161 static void rockchip_drm_output_poll_changed(struct drm_device
*dev
)
163 struct rockchip_drm_private
*private = dev
->dev_private
;
164 struct drm_fb_helper
*fb_helper
= &private->fbdev_helper
;
167 drm_fb_helper_hotplug_event(fb_helper
);
170 static void rockchip_crtc_wait_for_update(struct drm_crtc
*crtc
)
172 struct rockchip_drm_private
*priv
= crtc
->dev
->dev_private
;
173 int pipe
= drm_crtc_index(crtc
);
174 const struct rockchip_crtc_funcs
*crtc_funcs
= priv
->crtc_funcs
[pipe
];
176 if (crtc_funcs
&& crtc_funcs
->wait_for_update
)
177 crtc_funcs
->wait_for_update(crtc
);
181 rockchip_atomic_wait_for_complete(struct drm_atomic_state
*old_state
)
183 struct drm_crtc_state
*old_crtc_state
;
184 struct drm_crtc
*crtc
;
187 for_each_crtc_in_state(old_state
, crtc
, old_crtc_state
, i
) {
188 /* No one cares about the old state, so abuse it for tracking
189 * and store whether we hold a vblank reference (and should do a
190 * vblank wait) in the ->enable boolean.
192 old_crtc_state
->enable
= false;
194 if (!crtc
->state
->active
)
197 ret
= drm_crtc_vblank_get(crtc
);
201 old_crtc_state
->enable
= true;
204 for_each_crtc_in_state(old_state
, crtc
, old_crtc_state
, i
) {
205 if (!old_crtc_state
->enable
)
208 rockchip_crtc_wait_for_update(crtc
);
209 drm_crtc_vblank_put(crtc
);
214 rockchip_atomic_commit_complete(struct rockchip_atomic_commit
*commit
)
216 struct drm_atomic_state
*state
= commit
->state
;
217 struct drm_device
*dev
= commit
->dev
;
220 * TODO: do fence wait here.
224 * Rockchip crtc support runtime PM, can't update display planes
225 * when crtc is disabled.
227 * drm_atomic_helper_commit comments detail that:
228 * For drivers supporting runtime PM the recommended sequence is
230 * drm_atomic_helper_commit_modeset_disables(dev, state);
232 * drm_atomic_helper_commit_modeset_enables(dev, state);
234 * drm_atomic_helper_commit_planes(dev, state, true);
236 * See the kerneldoc entries for these three functions for more details.
238 drm_atomic_helper_commit_modeset_disables(dev
, state
);
240 drm_atomic_helper_commit_modeset_enables(dev
, state
);
242 drm_atomic_helper_commit_planes(dev
, state
, true);
244 rockchip_atomic_wait_for_complete(state
);
246 drm_atomic_helper_cleanup_planes(dev
, state
);
248 drm_atomic_state_free(state
);
251 void rockchip_drm_atomic_work(struct work_struct
*work
)
253 struct rockchip_atomic_commit
*commit
= container_of(work
,
254 struct rockchip_atomic_commit
, work
);
256 rockchip_atomic_commit_complete(commit
);
259 int rockchip_drm_atomic_commit(struct drm_device
*dev
,
260 struct drm_atomic_state
*state
,
263 struct rockchip_drm_private
*private = dev
->dev_private
;
264 struct rockchip_atomic_commit
*commit
= &private->commit
;
267 ret
= drm_atomic_helper_prepare_planes(dev
, state
);
271 /* serialize outstanding asynchronous commits */
272 mutex_lock(&commit
->lock
);
273 flush_work(&commit
->work
);
275 drm_atomic_helper_swap_state(dev
, state
);
278 commit
->state
= state
;
281 schedule_work(&commit
->work
);
283 rockchip_atomic_commit_complete(commit
);
285 mutex_unlock(&commit
->lock
);
290 static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs
= {
291 .fb_create
= rockchip_user_fb_create
,
292 .output_poll_changed
= rockchip_drm_output_poll_changed
,
293 .atomic_check
= drm_atomic_helper_check
,
294 .atomic_commit
= rockchip_drm_atomic_commit
,
297 struct drm_framebuffer
*
298 rockchip_drm_framebuffer_init(struct drm_device
*dev
,
299 const struct drm_mode_fb_cmd2
*mode_cmd
,
300 struct drm_gem_object
*obj
)
302 struct rockchip_drm_fb
*rockchip_fb
;
304 rockchip_fb
= rockchip_fb_alloc(dev
, mode_cmd
, &obj
, 1);
305 if (IS_ERR(rockchip_fb
))
308 return &rockchip_fb
->fb
;
311 void rockchip_drm_mode_config_init(struct drm_device
*dev
)
313 dev
->mode_config
.min_width
= 0;
314 dev
->mode_config
.min_height
= 0;
317 * set max width and height as default value(4096x4096).
318 * this value would be used to check framebuffer size limitation
319 * at drm_mode_addfb().
321 dev
->mode_config
.max_width
= 4096;
322 dev
->mode_config
.max_height
= 4096;
324 dev
->mode_config
.funcs
= &rockchip_drm_mode_config_funcs
;