Commit | Line | Data |
---|---|---|
ae4df11a LP |
1 | /* |
2 | * Copyright (c) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
3 | * | |
4 | * DRM core format related functions | |
5 | * | |
6 | * Permission to use, copy, modify, distribute, and sell this software and its | |
7 | * documentation for any purpose is hereby granted without fee, provided that | |
8 | * the above copyright notice appear in all copies and that both that copyright | |
9 | * notice and this permission notice appear in supporting documentation, and | |
10 | * that the name of the copyright holders not be used in advertising or | |
11 | * publicity pertaining to distribution of the software without specific, | |
12 | * written prior permission. The copyright holders make no representations | |
13 | * about the suitability of this software for any purpose. It is provided "as | |
14 | * is" without express or implied warranty. | |
15 | * | |
16 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | |
17 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | |
18 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | |
19 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | |
20 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | |
21 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |
22 | * OF THIS SOFTWARE. | |
23 | */ | |
24 | ||
25 | #include <linux/bug.h> | |
26 | #include <linux/ctype.h> | |
27 | #include <linux/export.h> | |
28 | #include <linux/kernel.h> | |
29 | ||
30 | #include <drm/drmP.h> | |
31 | #include <drm/drm_fourcc.h> | |
32 | ||
33 | static char printable_char(int c) | |
34 | { | |
35 | return isascii(c) && isprint(c) ? c : '?'; | |
36 | } | |
37 | ||
ec5e3047 DV |
38 | /** |
39 | * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description | |
40 | * @bpp: bits per pixels | |
41 | * @depth: bit depth per pixel | |
42 | * | |
43 | * Computes a drm fourcc pixel format code for the given @bpp/@depth values. | |
44 | * Useful in fbdev emulation code, since that deals in those values. | |
45 | */ | |
46 | uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) | |
47 | { | |
48 | uint32_t fmt; | |
49 | ||
50 | switch (bpp) { | |
51 | case 8: | |
52 | fmt = DRM_FORMAT_C8; | |
53 | break; | |
54 | case 16: | |
55 | if (depth == 15) | |
56 | fmt = DRM_FORMAT_XRGB1555; | |
57 | else | |
58 | fmt = DRM_FORMAT_RGB565; | |
59 | break; | |
60 | case 24: | |
61 | fmt = DRM_FORMAT_RGB888; | |
62 | break; | |
63 | case 32: | |
64 | if (depth == 24) | |
65 | fmt = DRM_FORMAT_XRGB8888; | |
66 | else if (depth == 30) | |
67 | fmt = DRM_FORMAT_XRGB2101010; | |
68 | else | |
69 | fmt = DRM_FORMAT_ARGB8888; | |
70 | break; | |
71 | default: | |
72 | DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n"); | |
73 | fmt = DRM_FORMAT_XRGB8888; | |
74 | break; | |
75 | } | |
76 | ||
77 | return fmt; | |
78 | } | |
79 | EXPORT_SYMBOL(drm_mode_legacy_fb_format); | |
80 | ||
ae4df11a | 81 | /** |
b3c11ac2 | 82 | * drm_get_format_name - fill a string with a drm fourcc format's name |
ae4df11a | 83 | * @format: format to compute name of |
b3c11ac2 | 84 | * @buf: caller-supplied buffer |
ae4df11a | 85 | */ |
b3c11ac2 | 86 | const char *drm_get_format_name(uint32_t format, struct drm_format_name_buf *buf) |
ae4df11a | 87 | { |
b3c11ac2 | 88 | snprintf(buf->str, sizeof(buf->str), |
ae4df11a LP |
89 | "%c%c%c%c %s-endian (0x%08x)", |
90 | printable_char(format & 0xff), | |
91 | printable_char((format >> 8) & 0xff), | |
92 | printable_char((format >> 16) & 0xff), | |
93 | printable_char((format >> 24) & 0x7f), | |
94 | format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little", | |
95 | format); | |
96 | ||
b3c11ac2 | 97 | return buf->str; |
ae4df11a LP |
98 | } |
99 | EXPORT_SYMBOL(drm_get_format_name); | |
100 | ||
333d2da5 LP |
101 | /* |
102 | * Internal function to query information for a given format. See | |
103 | * drm_format_info() for the public API. | |
84770cc2 | 104 | */ |
333d2da5 | 105 | const struct drm_format_info *__drm_format_info(u32 format) |
84770cc2 LP |
106 | { |
107 | static const struct drm_format_info formats[] = { | |
108 | { .format = DRM_FORMAT_C8, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
109 | { .format = DRM_FORMAT_RGB332, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
110 | { .format = DRM_FORMAT_BGR233, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
111 | { .format = DRM_FORMAT_XRGB4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
112 | { .format = DRM_FORMAT_XBGR4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
113 | { .format = DRM_FORMAT_RGBX4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
114 | { .format = DRM_FORMAT_BGRX4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
115 | { .format = DRM_FORMAT_ARGB4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
116 | { .format = DRM_FORMAT_ABGR4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
117 | { .format = DRM_FORMAT_RGBA4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
118 | { .format = DRM_FORMAT_BGRA4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
119 | { .format = DRM_FORMAT_XRGB1555, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
120 | { .format = DRM_FORMAT_XBGR1555, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
121 | { .format = DRM_FORMAT_RGBX5551, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
122 | { .format = DRM_FORMAT_BGRX5551, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
123 | { .format = DRM_FORMAT_ARGB1555, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
124 | { .format = DRM_FORMAT_ABGR1555, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
125 | { .format = DRM_FORMAT_RGBA5551, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
126 | { .format = DRM_FORMAT_BGRA5551, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
127 | { .format = DRM_FORMAT_RGB565, .depth = 16, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
128 | { .format = DRM_FORMAT_BGR565, .depth = 16, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
129 | { .format = DRM_FORMAT_RGB888, .depth = 24, .num_planes = 1, .cpp = { 3, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
130 | { .format = DRM_FORMAT_BGR888, .depth = 24, .num_planes = 1, .cpp = { 3, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
131 | { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
132 | { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
133 | { .format = DRM_FORMAT_RGBX8888, .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
134 | { .format = DRM_FORMAT_BGRX8888, .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
135 | { .format = DRM_FORMAT_XRGB2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
136 | { .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
137 | { .format = DRM_FORMAT_RGBX1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
138 | { .format = DRM_FORMAT_BGRX1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
139 | { .format = DRM_FORMAT_ARGB2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
140 | { .format = DRM_FORMAT_ABGR2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
141 | { .format = DRM_FORMAT_RGBA1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
142 | { .format = DRM_FORMAT_BGRA1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
143 | { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
144 | { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
145 | { .format = DRM_FORMAT_RGBA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
146 | { .format = DRM_FORMAT_BGRA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
147 | { .format = DRM_FORMAT_YUV410, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 4, .vsub = 4 }, | |
148 | { .format = DRM_FORMAT_YVU410, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 4, .vsub = 4 }, | |
149 | { .format = DRM_FORMAT_YUV411, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 4, .vsub = 1 }, | |
150 | { .format = DRM_FORMAT_YVU411, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 4, .vsub = 1 }, | |
151 | { .format = DRM_FORMAT_YUV420, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 2, .vsub = 2 }, | |
152 | { .format = DRM_FORMAT_YVU420, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 2, .vsub = 2 }, | |
153 | { .format = DRM_FORMAT_YUV422, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 2, .vsub = 1 }, | |
154 | { .format = DRM_FORMAT_YVU422, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 2, .vsub = 1 }, | |
155 | { .format = DRM_FORMAT_YUV444, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 1, .vsub = 1 }, | |
156 | { .format = DRM_FORMAT_YVU444, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 1, .vsub = 1 }, | |
157 | { .format = DRM_FORMAT_NV12, .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 2, .vsub = 2 }, | |
158 | { .format = DRM_FORMAT_NV21, .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 2, .vsub = 2 }, | |
159 | { .format = DRM_FORMAT_NV16, .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 2, .vsub = 1 }, | |
160 | { .format = DRM_FORMAT_NV61, .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 2, .vsub = 1 }, | |
161 | { .format = DRM_FORMAT_NV24, .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 1, .vsub = 1 }, | |
162 | { .format = DRM_FORMAT_NV42, .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 1, .vsub = 1 }, | |
163 | { .format = DRM_FORMAT_YUYV, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1 }, | |
164 | { .format = DRM_FORMAT_YVYU, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1 }, | |
165 | { .format = DRM_FORMAT_UYVY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1 }, | |
166 | { .format = DRM_FORMAT_VYUY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1 }, | |
167 | { .format = DRM_FORMAT_AYUV, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 }, | |
168 | }; | |
169 | ||
170 | unsigned int i; | |
171 | ||
172 | for (i = 0; i < ARRAY_SIZE(formats); ++i) { | |
173 | if (formats[i].format == format) | |
174 | return &formats[i]; | |
175 | } | |
176 | ||
177 | return NULL; | |
178 | } | |
333d2da5 LP |
179 | |
180 | /** | |
181 | * drm_format_info - query information for a given format | |
182 | * @format: pixel format (DRM_FORMAT_*) | |
183 | * | |
184 | * The caller should only pass a supported pixel format to this function. | |
185 | * Unsupported pixel formats will generate a warning in the kernel log. | |
186 | * | |
187 | * Returns: | |
188 | * The instance of struct drm_format_info that describes the pixel format, or | |
189 | * NULL if the format is unsupported. | |
190 | */ | |
191 | const struct drm_format_info *drm_format_info(u32 format) | |
192 | { | |
193 | const struct drm_format_info *info; | |
194 | ||
195 | info = __drm_format_info(format); | |
196 | WARN_ON(!info); | |
197 | return info; | |
198 | } | |
84770cc2 LP |
199 | EXPORT_SYMBOL(drm_format_info); |
200 | ||
ae4df11a LP |
201 | /** |
202 | * drm_format_num_planes - get the number of planes for format | |
203 | * @format: pixel format (DRM_FORMAT_*) | |
204 | * | |
205 | * Returns: | |
206 | * The number of planes used by the specified pixel format. | |
207 | */ | |
208 | int drm_format_num_planes(uint32_t format) | |
209 | { | |
86c238aa LP |
210 | const struct drm_format_info *info; |
211 | ||
212 | info = drm_format_info(format); | |
213 | return info ? info->num_planes : 1; | |
ae4df11a LP |
214 | } |
215 | EXPORT_SYMBOL(drm_format_num_planes); | |
216 | ||
217 | /** | |
218 | * drm_format_plane_cpp - determine the bytes per pixel value | |
219 | * @format: pixel format (DRM_FORMAT_*) | |
220 | * @plane: plane index | |
221 | * | |
222 | * Returns: | |
223 | * The bytes per pixel value for the specified plane. | |
224 | */ | |
225 | int drm_format_plane_cpp(uint32_t format, int plane) | |
226 | { | |
86c238aa | 227 | const struct drm_format_info *info; |
ae4df11a | 228 | |
86c238aa LP |
229 | info = drm_format_info(format); |
230 | if (!info || plane >= info->num_planes) | |
ae4df11a LP |
231 | return 0; |
232 | ||
86c238aa | 233 | return info->cpp[plane]; |
ae4df11a LP |
234 | } |
235 | EXPORT_SYMBOL(drm_format_plane_cpp); | |
236 | ||
237 | /** | |
238 | * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor | |
239 | * @format: pixel format (DRM_FORMAT_*) | |
240 | * | |
241 | * Returns: | |
242 | * The horizontal chroma subsampling factor for the | |
243 | * specified pixel format. | |
244 | */ | |
245 | int drm_format_horz_chroma_subsampling(uint32_t format) | |
246 | { | |
86c238aa LP |
247 | const struct drm_format_info *info; |
248 | ||
249 | info = drm_format_info(format); | |
250 | return info ? info->hsub : 1; | |
ae4df11a LP |
251 | } |
252 | EXPORT_SYMBOL(drm_format_horz_chroma_subsampling); | |
253 | ||
254 | /** | |
255 | * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor | |
256 | * @format: pixel format (DRM_FORMAT_*) | |
257 | * | |
258 | * Returns: | |
259 | * The vertical chroma subsampling factor for the | |
260 | * specified pixel format. | |
261 | */ | |
262 | int drm_format_vert_chroma_subsampling(uint32_t format) | |
263 | { | |
86c238aa LP |
264 | const struct drm_format_info *info; |
265 | ||
266 | info = drm_format_info(format); | |
267 | return info ? info->vsub : 1; | |
ae4df11a LP |
268 | } |
269 | EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); | |
270 | ||
271 | /** | |
272 | * drm_format_plane_width - width of the plane given the first plane | |
273 | * @width: width of the first plane | |
274 | * @format: pixel format | |
275 | * @plane: plane index | |
276 | * | |
277 | * Returns: | |
278 | * The width of @plane, given that the width of the first plane is @width. | |
279 | */ | |
280 | int drm_format_plane_width(int width, uint32_t format, int plane) | |
281 | { | |
86c238aa LP |
282 | const struct drm_format_info *info; |
283 | ||
284 | info = drm_format_info(format); | |
285 | if (!info || plane >= info->num_planes) | |
ae4df11a LP |
286 | return 0; |
287 | ||
288 | if (plane == 0) | |
289 | return width; | |
290 | ||
86c238aa | 291 | return width / info->hsub; |
ae4df11a LP |
292 | } |
293 | EXPORT_SYMBOL(drm_format_plane_width); | |
294 | ||
295 | /** | |
296 | * drm_format_plane_height - height of the plane given the first plane | |
297 | * @height: height of the first plane | |
298 | * @format: pixel format | |
299 | * @plane: plane index | |
300 | * | |
301 | * Returns: | |
302 | * The height of @plane, given that the height of the first plane is @height. | |
303 | */ | |
304 | int drm_format_plane_height(int height, uint32_t format, int plane) | |
305 | { | |
86c238aa LP |
306 | const struct drm_format_info *info; |
307 | ||
308 | info = drm_format_info(format); | |
309 | if (!info || plane >= info->num_planes) | |
ae4df11a LP |
310 | return 0; |
311 | ||
312 | if (plane == 0) | |
313 | return height; | |
314 | ||
86c238aa | 315 | return height / info->vsub; |
ae4df11a LP |
316 | } |
317 | EXPORT_SYMBOL(drm_format_plane_height); |