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