1 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2 /*
3 * Copyright (c) 2022 Rockchip Electronics Co., Ltd.
4 * Author: Sandy Huang <hjc@rock-chips.com>
5 */
6 #include <linux/dma-buf-cache.h>
7 #include <linux/fdtable.h>
8 #include <drm/drm_atomic_uapi.h>
9
10 #include "../drm_internal.h"
11 #include "rockchip_drm_direct_show.h"
12
13 static int drm_ds_debug;
14 #define DRM_DS_DBG(format, ...) do { \
15 if (drm_ds_debug) \
16 pr_info("DRM_DS: %s(%d): " format, __func__, __LINE__, ## __VA_ARGS__); \
17 } while (0)
18
19 #define DRM_DS_ERR(format, ...) \
20 pr_info("ERR: DRM_DS: %s(%d): " format, __func__, __LINE__, ## __VA_ARGS__)
21
rockchip_drm_get_dev(void)22 struct drm_device *rockchip_drm_get_dev(void)
23 {
24 int i;
25 char *name = "rockchip";
26
27 for (i = 0; i < 64; i++) {
28 struct drm_minor *minor;
29
30 minor = drm_minor_acquire(i + DRM_MINOR_PRIMARY);
31 if (IS_ERR(minor))
32 continue;
33 if (!minor->dev || !minor->dev->driver ||
34 !minor->dev->driver->name)
35 continue;
36 if (!name)
37 return minor->dev;
38 if (!strcmp(name, minor->dev->driver->name))
39 return minor->dev;
40 }
41
42 return NULL;
43 }
44
rockchip_drm_direct_show_alloc_fb(struct drm_device * drm,struct rockchip_drm_direct_show_buffer * buffer)45 static int rockchip_drm_direct_show_alloc_fb(struct drm_device *drm,
46 struct rockchip_drm_direct_show_buffer *buffer)
47 {
48 struct drm_gem_object *obj;
49 struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
50 struct drm_mode_fb_cmd2 mode_cmd = { 0 };
51 struct drm_framebuffer *fb;
52 const struct drm_format_info *format_info = drm_format_info(buffer->pixel_format);
53
54 mode_cmd.offsets[0] = 0;
55 mode_cmd.width = buffer->width;
56 mode_cmd.height = buffer->height;
57 mode_cmd.pitches[0] = buffer->pitch[0];
58 mode_cmd.pixel_format = buffer->pixel_format;
59 obj = &buffer->rk_gem_obj->base;
60 objs[0] = obj;
61
62 if (format_info->is_yuv) {
63 mode_cmd.offsets[1] = buffer->width * buffer->height;
64 mode_cmd.pitches[1] = mode_cmd.pitches[0];
65 objs[1] = obj;
66 }
67
68 fb = rockchip_fb_alloc(drm, &mode_cmd, objs, format_info->num_planes);
69 if (IS_ERR_OR_NULL(fb))
70 return -ENOMEM;
71
72 buffer->fb = fb;
73
74 return 0;
75 }
76
rockchip_drm_direct_show_alloc_buffer(struct drm_device * drm,struct rockchip_drm_direct_show_buffer * buffer)77 int rockchip_drm_direct_show_alloc_buffer(struct drm_device *drm,
78 struct rockchip_drm_direct_show_buffer *buffer)
79 {
80 u32 min_pitch;
81 const struct drm_format_info *format_info;
82 struct drm_mode_create_dumb args;
83 struct rockchip_gem_object *rk_obj;
84 struct drm_gem_object *obj;
85 struct dma_buf *dmabuf;
86 int dmabuf_fd;
87
88 args.width = buffer->width;
89 args.height = buffer->height;
90 format_info = drm_format_info(buffer->pixel_format);
91 args.bpp = rockchip_drm_get_bpp(format_info);
92 min_pitch = args.width * DIV_ROUND_UP(args.bpp, 8);
93 args.pitch = ALIGN(min_pitch, 64);
94 args.size = args.pitch * args.height;
95 args.flags = buffer->flag;
96
97 if (format_info->is_yuv) {
98 int bpp = 0;
99
100 bpp = format_info->cpp[1] * 8;
101 min_pitch = args.width * DIV_ROUND_UP(bpp, 8);
102 min_pitch = ALIGN(min_pitch, 64);
103 args.size += min_pitch * args.height / format_info->hsub / format_info->vsub;
104 }
105 /* create a gem obj with kmap flag */
106 rk_obj = rockchip_gem_create_object(drm, args.size, true, args.flags);
107 if (IS_ERR(rk_obj)) {
108 DRM_DS_ERR("create rk_obj failed\n");
109 return -ENOMEM;
110 }
111 obj = &rk_obj->base;
112
113 buffer->bpp = args.bpp;
114 buffer->pitch[0] = args.pitch;
115 buffer->vir_addr[0] = rk_obj->kvaddr;
116 buffer->phy_addr[0] = rk_obj->dma_handle;
117 buffer->rk_gem_obj = rk_obj;
118 if (format_info->is_yuv) {
119 buffer->vir_addr[1] = buffer->vir_addr[0] + buffer->width * buffer->height;
120 buffer->pitch[1] = buffer->pitch[0];
121 buffer->phy_addr[1] = buffer->phy_addr[0] + buffer->width * buffer->height;
122 }
123
124 /* to get drm fb */
125 rockchip_drm_direct_show_alloc_fb(drm, buffer);
126
127 /* to get dma buffer fd */
128 mutex_lock(&drm->object_name_lock);
129 dmabuf = drm->driver->gem_prime_export(obj, 0);
130 if (IS_ERR(dmabuf)) {
131 mutex_unlock(&drm->object_name_lock);
132 goto err_gem_free;
133 }
134 obj->dma_buf = dmabuf;
135 get_dma_buf(obj->dma_buf);
136 drm_gem_dmabuf_release(obj->dma_buf);
137 mutex_unlock(&drm->object_name_lock);
138
139 dmabuf_fd = dma_buf_fd(dmabuf, 0);
140 if (dmabuf_fd < 0) {
141 DRM_DS_ERR("failed dma_buf_fd, ret %d\n", dmabuf_fd);
142 goto err_free_dmabuf;
143 }
144 buffer->dmabuf_fd = dmabuf_fd;
145
146 DRM_DS_DBG("alloc buffer: 0x%p, dma buf fd:%d, args.pitch:%d\n", buffer->rk_gem_obj, dmabuf_fd, args.pitch);
147
148 return 0;
149
150 err_free_dmabuf:
151 dma_buf_put(dmabuf);
152 err_gem_free:
153 drm_gem_object_put(&rk_obj->base);
154
155 return -ENOMEM;
156 }
157
rockchip_drm_direct_show_free_buffer(struct drm_device * drm,struct rockchip_drm_direct_show_buffer * buffer)158 void rockchip_drm_direct_show_free_buffer(struct drm_device *drm,
159 struct rockchip_drm_direct_show_buffer *buffer)
160 {
161 struct drm_gem_object *obj = &buffer->rk_gem_obj->base;
162
163 DRM_DS_DBG("free buffer: 0x%p\n", buffer->rk_gem_obj);
164
165 mutex_lock(&drm->object_name_lock);
166 if (obj->dma_buf) {
167 dma_buf_put(obj->dma_buf);
168 obj->dma_buf = NULL;
169 }
170 mutex_unlock(&drm->object_name_lock);
171
172 drm_gem_object_put(obj);
173 }
174
rockchip_drm_direct_show_get_plane(struct drm_device * drm,const char * name)175 struct drm_plane *rockchip_drm_direct_show_get_plane(struct drm_device *drm, const char *name)
176 {
177 struct drm_plane *plane;
178
179 drm_for_each_plane(plane, drm) {
180 if (!strncmp(plane->name, name, DRM_PROP_NAME_LEN))
181 break;
182 }
183 if (!plane) {
184 DRM_DS_ERR("failed to find plane:%s!\n", name);
185 return NULL;
186 }
187
188 DRM_DS_DBG("get plane[%s] success\n", plane->name);
189
190 return plane;
191 }
192
rockchip_drm_direct_show_get_crtc(struct drm_device * drm,const char * name)193 struct drm_crtc *rockchip_drm_direct_show_get_crtc(struct drm_device *drm, const char *name)
194 {
195 struct drm_crtc *crtc = NULL;
196 bool crtc_active = false;
197
198 drm_for_each_crtc(crtc, drm) {
199 if (name == NULL) {
200 if (crtc->state && crtc->state->active) {
201 crtc_active = true;
202 break;
203 }
204 } else {
205 if (crtc->state && crtc->state->active &&
206 !strncmp(crtc->name, name, DRM_PROP_NAME_LEN)) {
207 crtc_active = true;
208 break;
209 }
210 }
211 }
212
213 if (crtc_active == false) {
214 DRM_DS_ERR("failed to find active crtc\n");
215 return NULL;
216 }
217 DRM_DS_DBG("get crtc[%s] success\n", crtc->name);
218
219 return crtc;
220 }
221
222 static int
rockchip_drm_direct_show_set_property_value(struct drm_mode_object * obj,struct drm_property * property,uint64_t val)223 rockchip_drm_direct_show_set_property_value(struct drm_mode_object *obj,
224 struct drm_property *property,
225 uint64_t val)
226 {
227 int i;
228
229 for (i = 0; i < obj->properties->count; i++) {
230 if (obj->properties->properties[i] == property) {
231 obj->properties->values[i] = val;
232 return 0;
233 }
234 }
235
236 return -EINVAL;
237 }
238
239 static struct drm_property *
rockchip_drm_direct_show_find_prop(struct drm_device * dev,struct drm_mode_object * obj,char * prop_name)240 rockchip_drm_direct_show_find_prop(struct drm_device *dev,
241 struct drm_mode_object *obj,
242 char *prop_name)
243 {
244 int i = 0;
245
246 if (!obj->properties)
247 return NULL;
248
249 for (i = 0; i < obj->properties->count; i++) {
250 struct drm_property *prop = obj->properties->properties[i];
251
252 if (!strncmp(prop->name, prop_name, DRM_PROP_NAME_LEN))
253 return prop;
254 }
255
256 return NULL;
257 }
258
rockchip_drm_direct_show_commit(struct drm_device * drm,struct rockchip_drm_direct_show_commit_info * commit_info)259 int rockchip_drm_direct_show_commit(struct drm_device *drm,
260 struct rockchip_drm_direct_show_commit_info *commit_info)
261 {
262 int ret = 0;
263 struct drm_plane *plane = commit_info->plane;
264 struct drm_crtc *crtc = commit_info->crtc;
265 struct drm_framebuffer *fb = commit_info->buffer->fb;
266 struct drm_mode_config *conf = &drm->mode_config;
267 struct drm_property *zpos_prop;
268
269 /*setplane overlay zpos top*/
270 zpos_prop = rockchip_drm_direct_show_find_prop(drm, &plane->base, "zpos");
271 if (!zpos_prop)
272 DRM_DS_ERR("failed to find plane zpos prop, ret:%d\n", ret);
273
274 drm_modeset_lock_all(drm);
275 /* set the max zpos value */
276 if (commit_info->top_zpos && zpos_prop) {
277 ret = rockchip_drm_direct_show_set_property_value(&plane->base,
278 zpos_prop,
279 zpos_prop->values[1]);
280 if (ret)
281 DRM_DS_ERR("failed to set plane zpos prop, ret:%d\n", ret);
282 plane->state->zpos = zpos_prop->values[1];
283 }
284 ret = plane->funcs->update_plane(plane, crtc, fb,
285 commit_info->dst_x, commit_info->dst_y,
286 commit_info->dst_w, commit_info->dst_h,
287 commit_info->src_x << 16,
288 commit_info->src_y << 16,
289 commit_info->src_w << 16,
290 commit_info->src_h << 16,
291 conf->acquire_ctx);
292 drm_modeset_unlock_all(drm);
293
294 if (ret)
295 return ret;
296
297 DRM_DS_DBG("commit success: plane[%s], crtc[%s], src[%dx%d@%dx%d], dst[%dx%d@%dx%d]\n",
298 plane->name, crtc->name,
299 commit_info->src_w, commit_info->src_h,
300 commit_info->src_x, commit_info->src_y,
301 commit_info->dst_w, commit_info->dst_h,
302 commit_info->dst_x, commit_info->dst_y);
303
304 return ret;
305 }
306
rockchip_drm_direct_show_disable_plane(struct drm_device * drm,struct drm_plane * plane)307 int rockchip_drm_direct_show_disable_plane(struct drm_device *drm, struct drm_plane *plane)
308 {
309 int ret = 0;
310 struct drm_mode_config *conf = &drm->mode_config;
311
312 DRM_DS_DBG("disable plane: %s\n", plane->name);
313 drm_modeset_lock_all(drm);
314 ret = plane->funcs->disable_plane(plane, conf->acquire_ctx);
315 drm_modeset_unlock_all(drm);
316
317 return ret;
318 }
319