1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* exynos_drm_fb.c |
3 | * |
4 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. |
5 | * Authors: |
6 | * Inki Dae <inki.dae@samsung.com> |
7 | * Joonyoung Shim <jy0922.shim@samsung.com> |
8 | * Seung-Woo Kim <sw0312.kim@samsung.com> |
9 | */ |
10 | |
11 | #include <drm/drm_atomic.h> |
12 | #include <drm/drm_atomic_helper.h> |
13 | #include <drm/drm_crtc.h> |
14 | #include <drm/drm_framebuffer.h> |
15 | #include <drm/drm_fourcc.h> |
16 | #include <drm/drm_gem_framebuffer_helper.h> |
17 | #include <drm/drm_probe_helper.h> |
18 | #include <drm/exynos_drm.h> |
19 | |
20 | #include "exynos_drm_crtc.h" |
21 | #include "exynos_drm_drv.h" |
22 | #include "exynos_drm_fb.h" |
23 | #include "exynos_drm_fbdev.h" |
24 | |
25 | static int check_fb_gem_memory_type(struct drm_device *drm_dev, |
26 | struct exynos_drm_gem *exynos_gem) |
27 | { |
28 | unsigned int flags; |
29 | |
30 | /* |
31 | * if exynos drm driver supports iommu then framebuffer can use |
32 | * all the buffer types. |
33 | */ |
34 | if (is_drm_iommu_supported(drm_dev)) |
35 | return 0; |
36 | |
37 | flags = exynos_gem->flags; |
38 | |
39 | /* |
40 | * Physically non-contiguous memory type for framebuffer is not |
41 | * supported without IOMMU. |
42 | */ |
43 | if (IS_NONCONTIG_BUFFER(flags)) { |
44 | DRM_DEV_ERROR(drm_dev->dev, |
45 | "Non-contiguous GEM memory is not supported.\n" ); |
46 | return -EINVAL; |
47 | } |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | static const struct drm_framebuffer_funcs exynos_drm_fb_funcs = { |
53 | .destroy = drm_gem_fb_destroy, |
54 | .create_handle = drm_gem_fb_create_handle, |
55 | }; |
56 | |
57 | struct drm_framebuffer * |
58 | exynos_drm_framebuffer_init(struct drm_device *dev, |
59 | const struct drm_mode_fb_cmd2 *mode_cmd, |
60 | struct exynos_drm_gem **exynos_gem, |
61 | int count) |
62 | { |
63 | struct drm_framebuffer *fb; |
64 | int i; |
65 | int ret; |
66 | |
67 | fb = kzalloc(size: sizeof(*fb), GFP_KERNEL); |
68 | if (!fb) |
69 | return ERR_PTR(error: -ENOMEM); |
70 | |
71 | for (i = 0; i < count; i++) { |
72 | ret = check_fb_gem_memory_type(drm_dev: dev, exynos_gem: exynos_gem[i]); |
73 | if (ret < 0) |
74 | goto err; |
75 | |
76 | fb->obj[i] = &exynos_gem[i]->base; |
77 | } |
78 | |
79 | drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd); |
80 | |
81 | ret = drm_framebuffer_init(dev, fb, funcs: &exynos_drm_fb_funcs); |
82 | if (ret < 0) { |
83 | DRM_DEV_ERROR(dev->dev, |
84 | "failed to initialize framebuffer\n" ); |
85 | goto err; |
86 | } |
87 | |
88 | return fb; |
89 | |
90 | err: |
91 | kfree(objp: fb); |
92 | return ERR_PTR(error: ret); |
93 | } |
94 | |
95 | static struct drm_framebuffer * |
96 | exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, |
97 | const struct drm_mode_fb_cmd2 *mode_cmd) |
98 | { |
99 | const struct drm_format_info *info = drm_get_format_info(dev, mode_cmd); |
100 | struct exynos_drm_gem *exynos_gem[MAX_FB_BUFFER]; |
101 | struct drm_framebuffer *fb; |
102 | int i; |
103 | int ret; |
104 | |
105 | for (i = 0; i < info->num_planes; i++) { |
106 | unsigned int height = (i == 0) ? mode_cmd->height : |
107 | DIV_ROUND_UP(mode_cmd->height, info->vsub); |
108 | unsigned long size = height * mode_cmd->pitches[i] + |
109 | mode_cmd->offsets[i]; |
110 | |
111 | exynos_gem[i] = exynos_drm_gem_get(filp: file_priv, |
112 | gem_handle: mode_cmd->handles[i]); |
113 | if (!exynos_gem[i]) { |
114 | DRM_DEV_ERROR(dev->dev, |
115 | "failed to lookup gem object\n" ); |
116 | ret = -ENOENT; |
117 | goto err; |
118 | } |
119 | |
120 | if (size > exynos_gem[i]->size) { |
121 | i++; |
122 | ret = -EINVAL; |
123 | goto err; |
124 | } |
125 | } |
126 | |
127 | fb = exynos_drm_framebuffer_init(dev, mode_cmd, exynos_gem, count: i); |
128 | if (IS_ERR(ptr: fb)) { |
129 | ret = PTR_ERR(ptr: fb); |
130 | goto err; |
131 | } |
132 | |
133 | return fb; |
134 | |
135 | err: |
136 | while (i--) |
137 | exynos_drm_gem_put(exynos_gem: exynos_gem[i]); |
138 | |
139 | return ERR_PTR(error: ret); |
140 | } |
141 | |
142 | dma_addr_t exynos_drm_fb_dma_addr(struct drm_framebuffer *fb, int index) |
143 | { |
144 | struct exynos_drm_gem *exynos_gem; |
145 | |
146 | if (WARN_ON_ONCE(index >= MAX_FB_BUFFER)) |
147 | return 0; |
148 | |
149 | exynos_gem = to_exynos_gem(fb->obj[index]); |
150 | return exynos_gem->dma_addr + fb->offsets[index]; |
151 | } |
152 | |
153 | static struct drm_mode_config_helper_funcs exynos_drm_mode_config_helpers = { |
154 | .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, |
155 | }; |
156 | |
157 | static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { |
158 | .fb_create = exynos_user_fb_create, |
159 | .atomic_check = drm_atomic_helper_check, |
160 | .atomic_commit = drm_atomic_helper_commit, |
161 | }; |
162 | |
163 | void exynos_drm_mode_config_init(struct drm_device *dev) |
164 | { |
165 | dev->mode_config.min_width = 0; |
166 | dev->mode_config.min_height = 0; |
167 | |
168 | /* |
169 | * set max width and height as default value(4096x4096). |
170 | * this value would be used to check framebuffer size limitation |
171 | * at drm_mode_addfb(). |
172 | */ |
173 | dev->mode_config.max_width = 4096; |
174 | dev->mode_config.max_height = 4096; |
175 | |
176 | dev->mode_config.funcs = &exynos_drm_mode_config_funcs; |
177 | dev->mode_config.helper_private = &exynos_drm_mode_config_helpers; |
178 | |
179 | dev->mode_config.normalize_zpos = true; |
180 | } |
181 | |