1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright@ Samsung Electronics Co. LTD
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 /*!
19 * \file libscaler-m2m1shot.cpp
20 * \brief source file for Scaler HAL
21 * \author Cho KyongHo <pullip.cho@samsung.com>
22 * \date 2014/05/08
23 *
24 * <b>Revision History: </b>
25 * - 2014.05.08 : Cho KyongHo (pullip.cho@samsung.com) \n
26 * Create
27 */
28 #include <cstring>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sys/ioctl.h>
32 #include <sys/mman.h>
33
34 #include <exynos_scaler.h>
35
36 #include "libscaler-common.h"
37 #include "libscaler-m2m1shot.h"
38 #include "libscaler-swscaler.h"
39
40 using namespace std;
41
42 const char *dev_base_name[] = {
43 "/dev/m2m1shot_scaler0",
44 "/dev/m2m1shot_scaler1",
45 "/dev/m2m1shot_scaler2",
46 "/dev/m2m1shot_scaler3",
47 };
48
49 struct PixFormat {
50 unsigned int pixfmt;
51 char planes;
52 char bit_pp[3];
53 };
54
55 const static PixFormat g_pixfmt_table[] = {
56 {V4L2_PIX_FMT_RGB32, 1, {32, 0, 0}, },
57 {V4L2_PIX_FMT_BGR32, 1, {32, 0, 0}, },
58 {V4L2_PIX_FMT_RGB565, 1, {16, 0, 0}, },
59 {V4L2_PIX_FMT_RGB555X, 1, {16, 0, 0}, },
60 {V4L2_PIX_FMT_RGB444, 1, {16, 0, 0}, },
61 {V4L2_PIX_FMT_YUYV, 1, {16, 0, 0}, },
62 {V4L2_PIX_FMT_YVYU, 1, {16, 0, 0}, },
63 {V4L2_PIX_FMT_UYVY, 1, {16, 0, 0}, },
64 {V4L2_PIX_FMT_NV16, 1, {16, 0, 0}, },
65 {V4L2_PIX_FMT_NV61, 1, {16, 0, 0}, },
66 {V4L2_PIX_FMT_YUV420, 1, {12, 0, 0}, },
67 {V4L2_PIX_FMT_YVU420, 1, {12, 0, 0}, },
68 {V4L2_PIX_FMT_NV12M, 2, {8, 4, 0}, },
69 {V4L2_PIX_FMT_NV21M, 2, {8, 4, 0}, },
70 {v4l2_fourcc('V', 'M', '1', '2'), 2, {8, 4, 0}, },
71 {V4L2_PIX_FMT_NV12, 1, {12, 0, 0}, },
72 {V4L2_PIX_FMT_NV21, 1, {12, 0, 0}, },
73 {v4l2_fourcc('N', 'M', '2', '1'), 2, {8, 4, 0}, },
74 {V4L2_PIX_FMT_YUV420M, 3, {8, 2, 2}, },
75 {V4L2_PIX_FMT_YVU420M, 3, {8, 2, 2}, },
76 {V4L2_PIX_FMT_NV12M_P010, 2, {16, 8, 0}, },
77 {V4L2_PIX_FMT_NV24, 1, {24, 0, 0}, },
78 {V4L2_PIX_FMT_NV42, 1, {24, 0, 0}, },
79 };
80
81
CScalerM2M1SHOT(int devid,int __UNUSED__ drm)82 CScalerM2M1SHOT::CScalerM2M1SHOT(int devid, int __UNUSED__ drm) : m_iFD(-1)
83 {
84 memset(&m_task, 0, sizeof(m_task));
85
86 if ((devid < 0) || (devid > 3)) { // instance number must be between 0 ~ 3
87 SC_LOGE("Invalid device instance ID %d", devid);
88 return;
89 }
90
91 m_iFD = open(dev_base_name[devid], O_RDWR);
92 if (m_iFD < 0) {
93 SC_LOGERR("Failed to open '%s'", dev_base_name[devid]);
94 } else {
95 // default 3 planes not to miss any buffer address
96 m_task.buf_out.num_planes = 3;
97 m_task.buf_cap.num_planes = 3;
98 }
99 }
100
~CScalerM2M1SHOT()101 CScalerM2M1SHOT::~CScalerM2M1SHOT()
102 {
103 if (m_iFD >= 0)
104 close(m_iFD);
105 }
106
Run()107 bool CScalerM2M1SHOT::Run()
108 {
109 int ret;
110
111 if (LibScaler::UnderOne16thScaling(
112 m_task.fmt_out.crop.width, m_task.fmt_out.crop.height,
113 m_task.fmt_cap.crop.width, m_task.fmt_cap.crop.height,
114 m_task.op.rotate))
115 return RunSWScaling();
116
117 ret = ioctl(m_iFD, M2M1SHOT_IOC_PROCESS, &m_task);
118 if (ret < 0) {
119 SC_LOGERR("Failed to process the given M2M1SHOT task");
120 return false;
121 }
122
123 return true;
124 }
125
126 #define SCALER_EXT_SIZE 512
SetFormat(m2m1shot_pix_format & fmt,m2m1shot_buffer & buf,unsigned int width,unsigned int height,unsigned int v4l2_fmt)127 bool CScalerM2M1SHOT::SetFormat(m2m1shot_pix_format &fmt, m2m1shot_buffer &buf,
128 unsigned int width, unsigned int height, unsigned int v4l2_fmt) {
129 const PixFormat *pixfmt = NULL;
130
131 fmt.width = width;
132 fmt.height = height;
133 fmt.fmt = v4l2_fmt;
134
135 for (size_t i = 0; i < ARRSIZE(g_pixfmt_table); i++) {
136 if (g_pixfmt_table[i].pixfmt == v4l2_fmt) {
137 pixfmt = &g_pixfmt_table[i];
138 break;
139 }
140 }
141
142 if (!pixfmt) {
143 SC_LOGE("Format %#x is not supported", v4l2_fmt);
144 return false;
145 }
146
147 for (int i = 0; i < pixfmt->planes; i++) {
148 if (((pixfmt->bit_pp[i] * width) % 8) != 0) {
149 SC_LOGE("Plane %d of format %#x must have even width", i, v4l2_fmt);
150 return false;
151 }
152 buf.plane[i].len = (pixfmt->bit_pp[i] * width * height) / 8;
153 }
154
155 if (pixfmt->pixfmt == V4L2_PIX_FMT_YVU420) {
156 unsigned int y_size = width * height;
157 unsigned int c_span = ALIGN(width / 2, 16);
158 buf.plane[0].len = y_size + (c_span * height / 2) * 2;
159 }
160
161 #ifdef SCALER_ALIGN_RESTRICTION
162 for (int i = 0; i < pixfmt->planes; i++)
163 buf.plane[i].len += (i == 0) ? SCALER_EXT_SIZE : SCALER_EXT_SIZE / 2;
164 #endif
165
166 buf.num_planes = pixfmt->planes;
167
168 return true;
169 }
170
SetCrop(m2m1shot_pix_format & fmt,unsigned int l,unsigned int t,unsigned int w,unsigned int h)171 bool CScalerM2M1SHOT::SetCrop(m2m1shot_pix_format &fmt,
172 unsigned int l, unsigned int t, unsigned int w, unsigned int h) {
173 if (fmt.width <= l) {
174 SC_LOGE("crop left %d is larger than image width %d", l, fmt.width);
175 return false;
176 }
177 if (fmt.height <= t) {
178 SC_LOGE("crop top %d is larger than image height %d", t, fmt.height);
179 return false;
180 }
181 if (fmt.width < (l + w)) {
182 SC_LOGE("crop width %d@%d exceeds image width %d", w, l, fmt.width);
183 return false;
184 }
185 if (fmt.height < (t + h)) {
186 SC_LOGE("crop height %d@%d exceeds image height %d", h, t, fmt.height);
187 return false;
188 }
189
190 fmt.crop.left = l;
191 fmt.crop.top = t;
192 fmt.crop.width = w;
193 fmt.crop.height = h;
194
195 return true;
196 }
197
SetAddr(m2m1shot_buffer & buf,void * addr[SC_NUM_OF_PLANES],int mem_type)198 bool CScalerM2M1SHOT::SetAddr(
199 m2m1shot_buffer &buf, void *addr[SC_NUM_OF_PLANES], int mem_type) {
200 if (mem_type == V4L2_MEMORY_DMABUF) {
201 buf.type = M2M1SHOT_BUFFER_DMABUF;
202 for (int i = 0; i < buf.num_planes; i++)
203 buf.plane[i].fd = static_cast<__s32>(reinterpret_cast<long>(addr[i]));
204 } else if (mem_type == V4L2_MEMORY_USERPTR) {
205 buf.type = M2M1SHOT_BUFFER_USERPTR;
206 for (int i = 0; i < buf.num_planes; i++)
207 buf.plane[i].userptr = reinterpret_cast<unsigned long>(addr[i]);
208 } else {
209 SC_LOGE("Unknown buffer type %d", mem_type);
210 return false;
211 }
212
213 return true;
214 }
215
SetRotate(int rot,int hflip,int vflip)216 bool CScalerM2M1SHOT::SetRotate(int rot, int hflip, int vflip) {
217 if ((rot % 90) != 0) {
218 SC_LOGE("Rotation degree %d must be multiple of 90", rot);
219 return false;
220 }
221
222 rot = rot % 360;
223 if (rot < 0)
224 rot = 360 + rot;
225
226 m_task.op.rotate = rot;
227 m_task.op.op &= ~(M2M1SHOT_OP_FLIP_HORI | M2M1SHOT_OP_FLIP_VIRT);
228 if (hflip)
229 m_task.op.op |= M2M1SHOT_OP_FLIP_HORI;
230 if (vflip)
231 m_task.op.op |= M2M1SHOT_OP_FLIP_VIRT;
232
233 return true;
234 }
235
GetBuffer(m2m1shot_buffer & buf,char * addr[])236 static bool GetBuffer(m2m1shot_buffer &buf, char *addr[])
237 {
238 for (int i = 0; i < buf.num_planes; i++) {
239 if (buf.type == M2M1SHOT_BUFFER_DMABUF) {
240 addr[i] = reinterpret_cast<char *>(mmap(NULL, buf.plane[i].len,
241 PROT_READ | PROT_WRITE, MAP_SHARED,
242 buf.plane[i].fd, 0));
243 if (addr[i] == MAP_FAILED) {
244 SC_LOGE("Failed to map FD %d", buf.plane[i].fd);
245 while (i-- > 0)
246 munmap(addr[i], buf.plane[i].len);
247 return false;
248 }
249 } else {
250 addr[i] = reinterpret_cast<char *>(buf.plane[i].userptr);
251 }
252 }
253
254 return true;
255 }
256
PutBuffer(m2m1shot_buffer & buf,char * addr[])257 static void PutBuffer(m2m1shot_buffer &buf, char *addr[])
258 {
259 for (int i = 0; i < buf.num_planes; i++) {
260 if (buf.type == M2M1SHOT_BUFFER_DMABUF)
261 munmap(addr[i], buf.plane[i].len);
262 }
263 }
264
RunSWScaling()265 bool CScalerM2M1SHOT::RunSWScaling()
266 {
267 if (m_task.fmt_cap.fmt != m_task.fmt_out.fmt) {
268 SC_LOGE("Source and target image format must be the same");
269 return false;
270 }
271
272 if (m_task.op.rotate != 0) {
273 SC_LOGE("Rotation is not allowed for S/W Scaling");
274 return false;
275 }
276
277 SC_LOGI("Running S/W Scaler: %dx%d -> %dx%d",
278 m_task.fmt_out.crop.width, m_task.fmt_out.crop.height,
279 m_task.fmt_cap.crop.width, m_task.fmt_cap.crop.height);
280
281 CScalerSW *swsc;
282 char *src[3], *dst[3];
283
284 switch (m_task.fmt_cap.fmt) {
285 case V4L2_PIX_FMT_YUYV:
286 case V4L2_PIX_FMT_YVYU:
287 if (!GetBuffer(m_task.buf_out, src))
288 return false;
289
290 if (!GetBuffer(m_task.buf_cap, dst)) {
291 PutBuffer(m_task.buf_out, src);
292 return false;
293 }
294
295 swsc = new CScalerSW_YUYV(src[0], dst[0]);
296 break;
297 case V4L2_PIX_FMT_NV12M:
298 case V4L2_PIX_FMT_NV21M:
299 case V4L2_PIX_FMT_NV12:
300 case V4L2_PIX_FMT_NV21:
301 if (!GetBuffer(m_task.buf_out, src))
302 return false;
303
304 if (!GetBuffer(m_task.buf_cap, dst)) {
305 PutBuffer(m_task.buf_out, src);
306 return false;
307 }
308
309 if (m_task.buf_out.num_planes == 1)
310 src[1] = src[0] + m_task.fmt_out.width * m_task.fmt_out.height;
311
312 if (m_task.buf_cap.num_planes == 1)
313 dst[1] = dst[0] + m_task.fmt_cap.width * m_task.fmt_cap.height;
314
315 swsc = new CScalerSW_NV12(src[0], src[1], dst[0], dst[1]);
316 break;
317 case V4L2_PIX_FMT_UYVY: // TODO: UYVY is not implemented yet.
318 default:
319 SC_LOGE("Format %x is not supported", m_task.fmt_out.fmt);
320 return false;
321 }
322
323 if (swsc == NULL) {
324 SC_LOGE("Failed to allocate SW Scaler");
325 PutBuffer(m_task.buf_out, src);
326 PutBuffer(m_task.buf_cap, dst);
327 return false;
328 }
329
330 swsc->SetSrcRect(m_task.fmt_out.crop.left, m_task.fmt_out.crop.top,
331 m_task.fmt_out.crop.width, m_task.fmt_out.crop.height,
332 m_task.fmt_out.width);
333
334 swsc->SetDstRect(m_task.fmt_cap.crop.left, m_task.fmt_cap.crop.top,
335 m_task.fmt_cap.crop.width, m_task.fmt_cap.crop.height,
336 m_task.fmt_cap.width);
337
338 bool ret = swsc->Scale();
339
340 delete swsc;
341
342 PutBuffer(m_task.buf_out, src);
343 PutBuffer(m_task.buf_cap, dst);
344
345 return ret;
346 }
347