Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / drivers / gpu / drm / msm / msm_fb.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2013 Red Hat
4  * Author: Rob Clark <robdclark@gmail.com>
5  */
6
7 #include <drm/drm_crtc.h>
8 #include <drm/drm_damage_helper.h>
9 #include <drm/drm_gem_framebuffer_helper.h>
10 #include <drm/drm_probe_helper.h>
11
12 #include "msm_drv.h"
13 #include "msm_kms.h"
14 #include "msm_gem.h"
15
16 struct msm_framebuffer {
17         struct drm_framebuffer base;
18         const struct msm_format *format;
19 };
20 #define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base)
21
22 static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
23                 const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos);
24
25 static const struct drm_framebuffer_funcs msm_framebuffer_funcs = {
26         .create_handle = drm_gem_fb_create_handle,
27         .destroy = drm_gem_fb_destroy,
28         .dirty = drm_atomic_helper_dirtyfb,
29 };
30
31 #ifdef CONFIG_DEBUG_FS
32 void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
33 {
34         int i, n = fb->format->num_planes;
35
36         seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n",
37                         fb->width, fb->height, (char *)&fb->format->format,
38                         drm_framebuffer_read_refcount(fb), fb->base.id);
39
40         for (i = 0; i < n; i++) {
41                 seq_printf(m, "   %d: offset=%d pitch=%d, obj: ",
42                                 i, fb->offsets[i], fb->pitches[i]);
43                 msm_gem_describe(fb->obj[i], m);
44         }
45 }
46 #endif
47
48 /* prepare/pin all the fb's bo's for scanout.  Note that it is not valid
49  * to prepare an fb more multiple different initiator 'id's.  But that
50  * should be fine, since only the scanout (mdpN) side of things needs
51  * this, the gpu doesn't care about fb's.
52  */
53 int msm_framebuffer_prepare(struct drm_framebuffer *fb,
54                 struct msm_gem_address_space *aspace)
55 {
56         int ret, i, n = fb->format->num_planes;
57         uint64_t iova;
58
59         for (i = 0; i < n; i++) {
60                 ret = msm_gem_get_and_pin_iova(fb->obj[i], aspace, &iova);
61                 DBG("FB[%u]: iova[%d]: %08llx (%d)", fb->base.id, i, iova, ret);
62                 if (ret)
63                         return ret;
64         }
65
66         return 0;
67 }
68
69 void msm_framebuffer_cleanup(struct drm_framebuffer *fb,
70                 struct msm_gem_address_space *aspace)
71 {
72         int i, n = fb->format->num_planes;
73
74         for (i = 0; i < n; i++)
75                 msm_gem_unpin_iova(fb->obj[i], aspace);
76 }
77
78 uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb,
79                 struct msm_gem_address_space *aspace, int plane)
80 {
81         if (!fb->obj[plane])
82                 return 0;
83         return msm_gem_iova(fb->obj[plane], aspace) + fb->offsets[plane];
84 }
85
86 struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
87 {
88         return drm_gem_fb_get_obj(fb, plane);
89 }
90
91 const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb)
92 {
93         struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
94         return msm_fb->format;
95 }
96
97 struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
98                 struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd)
99 {
100         const struct drm_format_info *info = drm_get_format_info(dev,
101                                                                  mode_cmd);
102         struct drm_gem_object *bos[4] = {0};
103         struct drm_framebuffer *fb;
104         int ret, i, n = info->num_planes;
105
106         for (i = 0; i < n; i++) {
107                 bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
108                 if (!bos[i]) {
109                         ret = -ENXIO;
110                         goto out_unref;
111                 }
112         }
113
114         fb = msm_framebuffer_init(dev, mode_cmd, bos);
115         if (IS_ERR(fb)) {
116                 ret = PTR_ERR(fb);
117                 goto out_unref;
118         }
119
120         return fb;
121
122 out_unref:
123         for (i = 0; i < n; i++)
124                 drm_gem_object_put_unlocked(bos[i]);
125         return ERR_PTR(ret);
126 }
127
128 static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
129                 const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
130 {
131         const struct drm_format_info *info = drm_get_format_info(dev,
132                                                                  mode_cmd);
133         struct msm_drm_private *priv = dev->dev_private;
134         struct msm_kms *kms = priv->kms;
135         struct msm_framebuffer *msm_fb = NULL;
136         struct drm_framebuffer *fb;
137         const struct msm_format *format;
138         int ret, i, n;
139
140         DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
141                         dev, mode_cmd, mode_cmd->width, mode_cmd->height,
142                         (char *)&mode_cmd->pixel_format);
143
144         n = info->num_planes;
145         format = kms->funcs->get_format(kms, mode_cmd->pixel_format,
146                         mode_cmd->modifier[0]);
147         if (!format) {
148                 DRM_DEV_ERROR(dev->dev, "unsupported pixel format: %4.4s\n",
149                                 (char *)&mode_cmd->pixel_format);
150                 ret = -EINVAL;
151                 goto fail;
152         }
153
154         msm_fb = kzalloc(sizeof(*msm_fb), GFP_KERNEL);
155         if (!msm_fb) {
156                 ret = -ENOMEM;
157                 goto fail;
158         }
159
160         fb = &msm_fb->base;
161
162         msm_fb->format = format;
163
164         if (n > ARRAY_SIZE(fb->obj)) {
165                 ret = -EINVAL;
166                 goto fail;
167         }
168
169         for (i = 0; i < n; i++) {
170                 unsigned int width = mode_cmd->width / (i ? info->hsub : 1);
171                 unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
172                 unsigned int min_size;
173
174                 min_size = (height - 1) * mode_cmd->pitches[i]
175                          + width * info->cpp[i]
176                          + mode_cmd->offsets[i];
177
178                 if (bos[i]->size < min_size) {
179                         ret = -EINVAL;
180                         goto fail;
181                 }
182
183                 msm_fb->base.obj[i] = bos[i];
184         }
185
186         drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
187
188         ret = drm_framebuffer_init(dev, fb, &msm_framebuffer_funcs);
189         if (ret) {
190                 DRM_DEV_ERROR(dev->dev, "framebuffer init failed: %d\n", ret);
191                 goto fail;
192         }
193
194         DBG("create: FB ID: %d (%p)", fb->base.id, fb);
195
196         return fb;
197
198 fail:
199         kfree(msm_fb);
200
201         return ERR_PTR(ret);
202 }
203
204 struct drm_framebuffer *
205 msm_alloc_stolen_fb(struct drm_device *dev, int w, int h, int p, uint32_t format)
206 {
207         struct drm_mode_fb_cmd2 mode_cmd = {
208                 .pixel_format = format,
209                 .width = w,
210                 .height = h,
211                 .pitches = { p },
212         };
213         struct drm_gem_object *bo;
214         struct drm_framebuffer *fb;
215         int size;
216
217         /* allocate backing bo */
218         size = mode_cmd.pitches[0] * mode_cmd.height;
219         DBG("allocating %d bytes for fb %d", size, dev->primary->index);
220         bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN);
221         if (IS_ERR(bo)) {
222                 dev_warn(dev->dev, "could not allocate stolen bo\n");
223                 /* try regular bo: */
224                 bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC);
225         }
226         if (IS_ERR(bo)) {
227                 DRM_DEV_ERROR(dev->dev, "failed to allocate buffer object\n");
228                 return ERR_CAST(bo);
229         }
230
231         msm_gem_object_set_name(bo, "stolenfb");
232
233         fb = msm_framebuffer_init(dev, &mode_cmd, &bo);
234         if (IS_ERR(fb)) {
235                 DRM_DEV_ERROR(dev->dev, "failed to allocate fb\n");
236                 /* note: if fb creation failed, we can't rely on fb destroy
237                  * to unref the bo:
238                  */
239                 drm_gem_object_put_unlocked(bo);
240                 return ERR_CAST(fb);
241         }
242
243         return fb;
244 }