Package version up to 3.0.6
[platform/adaptation/nexell/libtdm-nexell.git] / src / tdm_nexell_pp.c
1 /**************************************************************************
2
3 libtdm_nexell
4
5 Copyright 2017 Samsung Electronics co., Ltd. All Rights Reserved.
6
7 Contact: SooChan Lim <sc1.lim@samsung.com>
8
9 Permission is hereby granted, free of charge, to any person obtaining a
10 copy of this software and associated documentation files (the
11 "Software"), to deal in the Software without restriction, including
12 without limitation the rights to use, copy, modify, merge, publish,
13 distribute, sub license, and/or sell copies of the Software, and to
14 permit persons to whom the Software is furnished to do so, subject to
15 the following conditions:
16
17 The above copyright notice and this permission notice (including the
18 next paragraph) shall be included in all copies or substantial portions
19 of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
24 IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
25 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
29 **************************************************************************/
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include "tdm_backend_nexell.h"
36
37 #include <linux/media-bus-format.h>
38 #include <errno.h>
39
40 typedef struct _tdm_nexell_pp_buffer {
41         tbm_surface_h src;
42         tbm_surface_h dst;
43
44         struct list_head link;
45 } tdm_nexell_pp_buffer;
46
47 typedef struct _tdm_nexell_pp_data {
48         tdm_nexell_display *display_data;
49
50         hal_tdm_info_pp info;
51
52         struct list_head pending_buffer_list;
53
54         hal_tdm_pp_done_handler done_func;
55         void *done_user_data;
56
57         struct list_head link;
58 } tdm_nexell_pp_data;
59
60 #define MAX_PLANE_NUM 3
61
62 struct rect {
63         int x;
64         int y;
65         int width;
66         int height;
67 };
68
69 typedef struct _tdm_nexell_scaler_context {
70         int src_plane_num;
71         int src_fds[MAX_PLANE_NUM];
72         int src_stride[MAX_PLANE_NUM];
73         unsigned int src_width;
74         unsigned int src_height;
75         unsigned int src_code;
76
77         int dst_plane_num;
78         int dst_fds[MAX_PLANE_NUM];
79         int dst_stride[MAX_PLANE_NUM];
80         unsigned int dst_width;
81         unsigned int dst_height;
82         unsigned int dst_code;
83
84         struct rect     crop;
85 } tdm_nexell_scaler_context;
86
87 static tbm_format pp_formats[] = {
88         TBM_FORMAT_YUV420
89 };
90
91 #define NUM_PP_FORMAT   (sizeof(pp_formats) / sizeof(pp_formats[0]))
92 #define IOC_NX_MAGIC    0x6e78 /* nx */
93 enum    {
94         IOCTL_SCALER_SET_AND_RUN = _IO(IOC_NX_MAGIC, 1),
95 };
96
97 #ifndef ALIGN
98 #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
99 #endif
100
101 static int pp_list_init;
102 static struct list_head pp_list;
103
104 static int
105 _tdm_drm_ioctl(int fd, unsigned long request, void *arg)
106 {
107         int ret;
108
109         do {
110                 ret = ioctl(fd, request, arg);
111         } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
112
113         return ret;
114 }
115
116 static int
117 _tdm_gem_to_dmafd(int drm_fd, int gem_fd)
118 {
119         int ret;
120         struct drm_prime_handle arg = {0, };
121
122         arg.handle = gem_fd;
123         ret = _tdm_drm_ioctl(drm_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &arg);
124         if (0 != ret) {
125                 return -1;
126         }
127         return arg.fd;
128 }
129
130 static hal_tdm_error
131 _tdm_nexell_pp_convert(tdm_nexell_pp_buffer *buffer, hal_tdm_info_pp *info, int drm_fd, int scaler_fd)
132 {
133         tdm_nexell_scaler_context ctx;
134         unsigned int src_y_stride;
135         unsigned int src_c_stride;
136         unsigned int dst_y_stride;
137         unsigned int dst_c_stride;
138         tbm_bo bo_src = NULL;
139         tbm_bo bo_dst = NULL;
140         __u32 handle_src;
141         __u32 handle_dst;
142
143         bo_src = tbm_surface_internal_get_bo(buffer->src, 0);
144         TDM_BACKEND_RETURN_VAL_IF_FAIL(bo_src != NULL, HAL_TDM_ERROR_OPERATION_FAILED);
145         handle_src = (__u32)tbm_bo_get_handle(bo_src, TBM_DEVICE_DEFAULT).u32;
146         ctx.src_fds[0] = _tdm_gem_to_dmafd(drm_fd, handle_src);
147         TDM_BACKEND_RETURN_VAL_IF_FAIL(ctx.src_fds[0] >= 0, HAL_TDM_ERROR_OPERATION_FAILED);
148
149         ctx.src_fds[1] = ctx.src_fds[0];
150         ctx.src_fds[2] = ctx.src_fds[0];
151
152         bo_dst = tbm_surface_internal_get_bo(buffer->dst, 0);
153         if (bo_dst == NULL) {
154                 close(ctx.src_fds[0]);
155                 return HAL_TDM_ERROR_OPERATION_FAILED;
156         }
157         handle_dst = (__u32)tbm_bo_get_handle(bo_dst, TBM_DEVICE_DEFAULT).u32;
158         ctx.dst_fds[0] = _tdm_gem_to_dmafd(drm_fd, handle_dst);
159         if (ctx.dst_fds[0] < 0) {
160                 close(ctx.src_fds[0]);
161                 return HAL_TDM_ERROR_OPERATION_FAILED;
162         }
163
164         ctx.dst_fds[1] = ctx.dst_fds[0];
165         ctx.dst_fds[2] = ctx.dst_fds[0];
166
167         src_y_stride = ALIGN(info->src_config.size.h, 32);
168         src_c_stride = ALIGN(src_y_stride >> 1, 16);
169         dst_y_stride = ALIGN(info->dst_config.size.h, 8);
170         dst_c_stride = ALIGN(dst_y_stride >> 1, 4);
171
172         ctx.src_plane_num = 1;
173         ctx.src_width = info->src_config.size.h;
174         ctx.src_height = info->src_config.size.v;
175         ctx.src_code = MEDIA_BUS_FMT_YUYV8_2X8;
176         ctx.src_stride[0] = src_y_stride;
177         ctx.src_stride[1] = src_c_stride;
178         ctx.src_stride[2] = src_c_stride;
179
180         ctx.dst_plane_num = 1;
181         ctx.dst_width = info->dst_config.size.h;
182         ctx.dst_height = info->dst_config.size.v;
183         ctx.dst_code = MEDIA_BUS_FMT_YUYV8_2X8;
184         ctx.dst_stride[0] = dst_y_stride;
185         ctx.dst_stride[1] = dst_c_stride;
186         ctx.dst_stride[2] = dst_c_stride;
187
188         ctx.crop.x = info->src_config.pos.x;
189         ctx.crop.y = info->src_config.pos.y;
190         ctx.crop.width = info->src_config.pos.w;
191         ctx.crop.height = info->src_config.pos.h;
192
193         TDM_BACKEND_DBG("pp %p(%d, %d). src(%dx%d, (%d,%d,%d) (%d,%d,%d)) dst(%dx%d, (%d,%d,%d) (%d,%d,%d)) crop(%dx%d, %dx%d)", info, drm_fd, scaler_fd,
194                         ctx.src_width, ctx.src_height, ctx.src_stride[0], ctx.src_stride[1], ctx.src_stride[2], ctx.src_fds[0], ctx.src_fds[1], ctx.src_fds[2],
195                         ctx.dst_width, ctx.dst_height, ctx.dst_stride[0], ctx.dst_stride[1], ctx.dst_stride[2], ctx.dst_fds[0], ctx.dst_fds[1], ctx.dst_fds[2],
196                         ctx.crop.x, ctx.crop.y, ctx.crop.width, ctx.crop.height);
197
198         if (ioctl(scaler_fd, IOCTL_SCALER_SET_AND_RUN, &ctx) < 0) {
199                 TDM_BACKEND_ERR("IOCTL_SCALER_SET_AND_RUN failed");
200         }
201
202         close(ctx.src_fds[0]);
203         close(ctx.dst_fds[0]);
204
205         return HAL_TDM_ERROR_NONE;
206 }
207
208 hal_tdm_error
209 tdm_nexell_pp_get_capability(tdm_nexell_display *display_data, hal_tdm_caps_pp *caps)
210 {
211         int i;
212
213         if (!caps) {
214                 TDM_BACKEND_ERR("invalid params");
215                 return HAL_TDM_ERROR_INVALID_PARAMETER;
216         }
217
218         if (display_data->scaler_fd < 0 ) {
219                 TDM_BACKEND_ERR("no scaler_fd. not support pp.");
220                 return HAL_TDM_ERROR_BAD_MODULE;
221         }
222
223         caps->capabilities = HAL_TDM_PP_CAPABILITY_SYNC | HAL_TDM_PP_CAPABILITY_SCANOUT | HAL_TDM_PP_CAPABILITY_NO_TRANSFORM_ROTATION;
224
225         caps->format_count = NUM_PP_FORMAT;
226
227         /* will be freed in frontend */
228         caps->formats = calloc(1, sizeof pp_formats);
229         if (!caps->formats) {
230                 TDM_BACKEND_ERR("alloc failed");
231                 return HAL_TDM_ERROR_OUT_OF_MEMORY;
232         }
233         for (i = 0; i < caps->format_count; i++)
234                 caps->formats[i] = pp_formats[i];
235
236         caps->min_w = 16;
237         caps->min_h = 8;
238         caps->max_w = -1;   /* not defined */
239         caps->max_h = -1;
240         caps->preferred_align = 32;
241
242         return HAL_TDM_ERROR_NONE;
243 }
244
245 hal_tdm_pp *
246 tdm_nexell_pp_create(tdm_nexell_display *display_data, hal_tdm_error *error)
247 {
248         tdm_nexell_pp_data *pp_data = NULL;
249
250         if (display_data->scaler_fd < 0 ) {
251                 TDM_BACKEND_ERR("no scaler_fd.");
252                 if (error)
253                         *error = HAL_TDM_ERROR_BAD_MODULE;
254                 return NULL;
255         }
256
257         pp_data = calloc(1, sizeof(tdm_nexell_pp_data));
258         if (!pp_data) {
259                 TDM_BACKEND_ERR("alloc failed");
260                 if (error)
261                         *error = HAL_TDM_ERROR_OUT_OF_MEMORY;
262                 return NULL;
263         }
264
265         pp_data->display_data = display_data;
266
267         LIST_INITHEAD(&pp_data->pending_buffer_list);
268
269         if (!pp_list_init) {
270                 pp_list_init = 1;
271                 LIST_INITHEAD(&pp_list);
272         }
273         LIST_ADDTAIL(&pp_data->link, &pp_list);
274
275         return pp_data;
276 }
277
278 void
279 nexell_pp_destroy(hal_tdm_pp *pp)
280 {
281         tdm_nexell_pp_data *pp_data = pp;
282         tdm_nexell_pp_buffer *b = NULL, *bb = NULL;
283
284         if (!pp_data)
285                 return;
286
287         LIST_FOR_EACH_ENTRY_SAFE(b, bb, &pp_data->pending_buffer_list, link) {
288                 LIST_DEL(&b->link);
289                 free(b);
290         }
291
292         LIST_DEL(&pp_data->link);
293
294         free(pp_data);
295 }
296
297 hal_tdm_error
298 nexell_pp_set_info(hal_tdm_pp *pp, hal_tdm_info_pp *info)
299 {
300         tdm_nexell_pp_data *pp_data = pp;
301
302         TDM_BACKEND_RETURN_VAL_IF_FAIL(pp_data, HAL_TDM_ERROR_INVALID_PARAMETER);
303         TDM_BACKEND_RETURN_VAL_IF_FAIL(info, HAL_TDM_ERROR_INVALID_PARAMETER);
304
305         pp_data->info = *info;
306
307         return HAL_TDM_ERROR_NONE;
308 }
309
310 hal_tdm_error
311 nexell_pp_attach(hal_tdm_pp *pp, tbm_surface_h src, tbm_surface_h dst)
312 {
313         tdm_nexell_pp_data *pp_data = pp;
314         tdm_nexell_pp_buffer *buffer;
315
316         TDM_BACKEND_RETURN_VAL_IF_FAIL(pp_data, HAL_TDM_ERROR_INVALID_PARAMETER);
317         TDM_BACKEND_RETURN_VAL_IF_FAIL(src, HAL_TDM_ERROR_INVALID_PARAMETER);
318         TDM_BACKEND_RETURN_VAL_IF_FAIL(dst, HAL_TDM_ERROR_INVALID_PARAMETER);
319
320         buffer = calloc(1, sizeof(tdm_nexell_pp_buffer));
321         if (!buffer) {
322                 TDM_BACKEND_ERR("alloc failed");
323                 return HAL_TDM_ERROR_NONE;
324         }
325
326         LIST_ADDTAIL(&buffer->link, &pp_data->pending_buffer_list);
327
328         buffer->src = src;
329         buffer->dst = dst;
330
331         return HAL_TDM_ERROR_NONE;
332 }
333
334 hal_tdm_error
335 nexell_pp_commit(hal_tdm_pp *pp)
336 {
337         tdm_nexell_pp_data *pp_data = pp;
338         tdm_nexell_pp_buffer *b = NULL, *bb = NULL;
339         tdm_nexell_display *display_data;
340         hal_tdm_error ret;
341
342         TDM_BACKEND_RETURN_VAL_IF_FAIL(pp_data, HAL_TDM_ERROR_INVALID_PARAMETER);
343
344         display_data = pp_data->display_data;
345
346         LIST_FOR_EACH_ENTRY_SAFE(b, bb, &pp_data->pending_buffer_list, link) {
347                 LIST_DEL(&b->link);
348
349                 ret = _tdm_nexell_pp_convert(b, &pp_data->info, display_data->drm_fd, display_data->scaler_fd);
350                 TDM_BACKEND_RETURN_VAL_IF_FAIL(ret == HAL_TDM_ERROR_NONE, ret);
351
352                 if (pp_data->done_func)
353                         pp_data->done_func(pp_data,
354                                            b->src,
355                                            b->dst,
356                                            pp_data->done_user_data);
357                 free(b);
358         }
359
360         return HAL_TDM_ERROR_NONE;
361 }
362
363 hal_tdm_error
364 nexell_pp_set_done_handler(hal_tdm_pp *pp, hal_tdm_pp_done_handler func, void *user_data)
365 {
366         tdm_nexell_pp_data *pp_data = pp;
367
368         TDM_BACKEND_RETURN_VAL_IF_FAIL(pp_data, HAL_TDM_ERROR_INVALID_PARAMETER);
369         TDM_BACKEND_RETURN_VAL_IF_FAIL(func, HAL_TDM_ERROR_INVALID_PARAMETER);
370
371         pp_data->done_func = func;
372         pp_data->done_user_data = user_data;
373
374         return HAL_TDM_ERROR_NONE;
375 }