]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/gpu/host1x/drm/fb.c
979a3e32b78bbb0f5da3126b6b406eebae6ef497
[karo-tx-linux.git] / drivers / gpu / host1x / drm / fb.c
1 /*
2  * Copyright (C) 2012-2013 Avionic Design GmbH
3  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
4  *
5  * Based on the KMS/FB CMA helpers
6  *   Copyright (C) 2012 Analog Device Inc.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12
13 #include <linux/module.h>
14
15 #include "drm.h"
16 #include "gem.h"
17
18 static inline struct tegra_fb *to_tegra_fb(struct drm_framebuffer *fb)
19 {
20         return container_of(fb, struct tegra_fb, base);
21 }
22
23 static inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper)
24 {
25         return container_of(helper, struct tegra_fbdev, base);
26 }
27
28 struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
29                                     unsigned int index)
30 {
31         struct tegra_fb *fb = to_tegra_fb(framebuffer);
32
33         if (index >= drm_format_num_planes(framebuffer->pixel_format))
34                 return NULL;
35
36         return fb->planes[index];
37 }
38
39 static void tegra_fb_destroy(struct drm_framebuffer *framebuffer)
40 {
41         struct tegra_fb *fb = to_tegra_fb(framebuffer);
42         unsigned int i;
43
44         for (i = 0; i < fb->num_planes; i++) {
45                 struct tegra_bo *bo = fb->planes[i];
46
47                 if (bo)
48                         drm_gem_object_unreference_unlocked(&bo->gem);
49         }
50
51         drm_framebuffer_cleanup(framebuffer);
52         kfree(fb->planes);
53         kfree(fb);
54 }
55
56 static int tegra_fb_create_handle(struct drm_framebuffer *framebuffer,
57                                   struct drm_file *file, unsigned int *handle)
58 {
59         struct tegra_fb *fb = to_tegra_fb(framebuffer);
60
61         return drm_gem_handle_create(file, &fb->planes[0]->gem, handle);
62 }
63
64 static struct drm_framebuffer_funcs tegra_fb_funcs = {
65         .destroy = tegra_fb_destroy,
66         .create_handle = tegra_fb_create_handle,
67 };
68
69 static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm,
70                                        struct drm_mode_fb_cmd2 *mode_cmd,
71                                        struct tegra_bo **planes,
72                                        unsigned int num_planes)
73 {
74         struct tegra_fb *fb;
75         unsigned int i;
76         int err;
77
78         fb = kzalloc(sizeof(*fb), GFP_KERNEL);
79         if (!fb)
80                 return ERR_PTR(-ENOMEM);
81
82         fb->planes = kzalloc(num_planes * sizeof(*planes), GFP_KERNEL);
83         if (!fb->planes)
84                 return ERR_PTR(-ENOMEM);
85
86         fb->num_planes = num_planes;
87
88         drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
89
90         for (i = 0; i < fb->num_planes; i++)
91                 fb->planes[i] = planes[i];
92
93         err = drm_framebuffer_init(drm, &fb->base, &tegra_fb_funcs);
94         if (err < 0) {
95                 dev_err(drm->dev, "failed to initialize framebuffer: %d\n",
96                         err);
97                 kfree(fb->planes);
98                 kfree(fb);
99                 return ERR_PTR(err);
100         }
101
102         return fb;
103 }
104
105 static struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
106                                                struct drm_file *file,
107                                                struct drm_mode_fb_cmd2 *cmd)
108 {
109         unsigned int hsub, vsub, i;
110         struct tegra_bo *planes[4];
111         struct drm_gem_object *gem;
112         struct tegra_fb *fb;
113         int err;
114
115         hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format);
116         vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format);
117
118         for (i = 0; i < drm_format_num_planes(cmd->pixel_format); i++) {
119                 unsigned int width = cmd->width / (i ? hsub : 1);
120                 unsigned int height = cmd->height / (i ? vsub : 1);
121                 unsigned int size, bpp;
122
123                 gem = drm_gem_object_lookup(drm, file, cmd->handles[i]);
124                 if (!gem) {
125                         err = -ENXIO;
126                         goto unreference;
127                 }
128
129                 bpp = drm_format_plane_cpp(cmd->pixel_format, i);
130
131                 size = (height - 1) * cmd->pitches[i] +
132                        width * bpp + cmd->offsets[i];
133
134                 if (gem->size < size) {
135                         err = -EINVAL;
136                         goto unreference;
137                 }
138
139                 planes[i] = to_tegra_bo(gem);
140         }
141
142         fb = tegra_fb_alloc(drm, cmd, planes, i);
143         if (IS_ERR(fb)) {
144                 err = PTR_ERR(fb);
145                 goto unreference;
146         }
147
148         return &fb->base;
149
150 unreference:
151         while (i--)
152                 drm_gem_object_unreference_unlocked(&planes[i]->gem);
153
154         return ERR_PTR(err);
155 }
156
157 static struct fb_ops tegra_fb_ops = {
158         .owner = THIS_MODULE,
159         .fb_fillrect = sys_fillrect,
160         .fb_copyarea = sys_copyarea,
161         .fb_imageblit = sys_imageblit,
162         .fb_check_var = drm_fb_helper_check_var,
163         .fb_set_par = drm_fb_helper_set_par,
164         .fb_blank = drm_fb_helper_blank,
165         .fb_pan_display = drm_fb_helper_pan_display,
166         .fb_setcmap = drm_fb_helper_setcmap,
167 };
168
169 static int tegra_fbdev_probe(struct drm_fb_helper *helper,
170                              struct drm_fb_helper_surface_size *sizes)
171 {
172         struct tegra_fbdev *fbdev = to_tegra_fbdev(helper);
173         struct drm_device *drm = helper->dev;
174         struct drm_mode_fb_cmd2 cmd = { 0 };
175         unsigned int bytes_per_pixel;
176         struct drm_framebuffer *fb;
177         unsigned long offset;
178         struct fb_info *info;
179         struct tegra_bo *bo;
180         size_t size;
181         int err;
182
183         bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
184
185         cmd.width = sizes->surface_width;
186         cmd.height = sizes->surface_height;
187         cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
188         cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
189                                                      sizes->surface_depth);
190
191         size = cmd.pitches[0] * cmd.height;
192
193         bo = tegra_bo_create(drm, size);
194         if (IS_ERR(bo))
195                 return PTR_ERR(bo);
196
197         info = framebuffer_alloc(0, drm->dev);
198         if (!info) {
199                 dev_err(drm->dev, "failed to allocate framebuffer info\n");
200                 tegra_bo_free_object(&bo->gem);
201                 return -ENOMEM;
202         }
203
204         fbdev->fb = tegra_fb_alloc(drm, &cmd, &bo, 1);
205         if (IS_ERR(fbdev->fb)) {
206                 dev_err(drm->dev, "failed to allocate DRM framebuffer\n");
207                 err = PTR_ERR(fbdev->fb);
208                 goto release;
209         }
210
211         fb = &fbdev->fb->base;
212         helper->fb = fb;
213         helper->fbdev = info;
214
215         info->par = helper;
216         info->flags = FBINFO_FLAG_DEFAULT;
217         info->fbops = &tegra_fb_ops;
218
219         err = fb_alloc_cmap(&info->cmap, 256, 0);
220         if (err < 0) {
221                 dev_err(drm->dev, "failed to allocate color map: %d\n", err);
222                 goto destroy;
223         }
224
225         drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
226         drm_fb_helper_fill_var(info, helper, fb->width, fb->height);
227
228         offset = info->var.xoffset * bytes_per_pixel +
229                  info->var.yoffset * fb->pitches[0];
230
231         drm->mode_config.fb_base = (resource_size_t)bo->paddr;
232         info->screen_base = bo->vaddr + offset;
233         info->screen_size = size;
234         info->fix.smem_start = (unsigned long)(bo->paddr + offset);
235         info->fix.smem_len = size;
236
237         return 0;
238
239 destroy:
240         drm_framebuffer_unregister_private(fb);
241         tegra_fb_destroy(fb);
242 release:
243         framebuffer_release(info);
244         return err;
245 }
246
247 static struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
248         .fb_probe = tegra_fbdev_probe,
249 };
250
251 static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
252                                               unsigned int preferred_bpp,
253                                               unsigned int num_crtc,
254                                               unsigned int max_connectors)
255 {
256         struct drm_fb_helper *helper;
257         struct tegra_fbdev *fbdev;
258         int err;
259
260         fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
261         if (!fbdev) {
262                 dev_err(drm->dev, "failed to allocate DRM fbdev\n");
263                 return ERR_PTR(-ENOMEM);
264         }
265
266         fbdev->base.funcs = &tegra_fb_helper_funcs;
267         helper = &fbdev->base;
268
269         err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
270         if (err < 0) {
271                 dev_err(drm->dev, "failed to initialize DRM FB helper\n");
272                 goto free;
273         }
274
275         err = drm_fb_helper_single_add_all_connectors(&fbdev->base);
276         if (err < 0) {
277                 dev_err(drm->dev, "failed to add connectors\n");
278                 goto fini;
279         }
280
281         drm_helper_disable_unused_functions(drm);
282
283         err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp);
284         if (err < 0) {
285                 dev_err(drm->dev, "failed to set initial configuration\n");
286                 goto fini;
287         }
288
289         return fbdev;
290
291 fini:
292         drm_fb_helper_fini(&fbdev->base);
293 free:
294         kfree(fbdev);
295         return ERR_PTR(err);
296 }
297
298 static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
299 {
300         struct fb_info *info = fbdev->base.fbdev;
301
302         if (info) {
303                 int err;
304
305                 err = unregister_framebuffer(info);
306                 if (err < 0)
307                         DRM_DEBUG_KMS("failed to unregister framebuffer\n");
308
309                 if (info->cmap.len)
310                         fb_dealloc_cmap(&info->cmap);
311
312                 framebuffer_release(info);
313         }
314
315         if (fbdev->fb) {
316                 drm_framebuffer_unregister_private(&fbdev->fb->base);
317                 tegra_fb_destroy(&fbdev->fb->base);
318         }
319
320         drm_fb_helper_fini(&fbdev->base);
321         kfree(fbdev);
322 }
323
324 static void tegra_fb_output_poll_changed(struct drm_device *drm)
325 {
326         struct host1x_drm *host1x = drm->dev_private;
327
328         if (host1x->fbdev)
329                 drm_fb_helper_hotplug_event(&host1x->fbdev->base);
330 }
331
332 static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
333         .fb_create = tegra_fb_create,
334         .output_poll_changed = tegra_fb_output_poll_changed,
335 };
336
337 int tegra_drm_fb_init(struct drm_device *drm)
338 {
339         struct host1x_drm *host1x = drm->dev_private;
340         struct tegra_fbdev *fbdev;
341
342         drm->mode_config.min_width = 0;
343         drm->mode_config.min_height = 0;
344
345         drm->mode_config.max_width = 4096;
346         drm->mode_config.max_height = 4096;
347
348         drm->mode_config.funcs = &tegra_drm_mode_funcs;
349
350         fbdev = tegra_fbdev_create(drm, 32, drm->mode_config.num_crtc,
351                                    drm->mode_config.num_connector);
352         if (IS_ERR(fbdev))
353                 return PTR_ERR(fbdev);
354
355         host1x->fbdev = fbdev;
356
357         return 0;
358 }
359
360 void tegra_drm_fb_exit(struct drm_device *drm)
361 {
362         struct host1x_drm *host1x = drm->dev_private;
363
364         tegra_fbdev_free(host1x->fbdev);
365 }
366
367 void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
368 {
369         if (fbdev) {
370                 drm_modeset_lock_all(fbdev->base.dev);
371                 drm_fb_helper_restore_fbdev_mode(&fbdev->base);
372                 drm_modeset_unlock_all(fbdev->base.dev);
373         }
374 }