1 /*
2 *
3 * SPDX-License-Identifier: GPL-2.0
4 *
5 * Copyright (C) 2011-2018 ARM or its affiliates
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20 #include <linux/version.h>
21 #include <linux/module.h>
22 #include <linux/slab.h>
23 #include <linux/mutex.h>
24 #include <linux/videodev2.h>
25 #include <media/videobuf2-core.h>
26 #include <media/videobuf2-vmalloc.h>
27 #include <media/v4l2-ioctl.h>
28 #include <media/v4l2-fh.h>
29 #include <media/v4l2-event.h>
30
31 #include "acamera_logger.h"
32
33 #include "isp-v4l2-common.h"
34 #include "isp-v4l2.h"
35 #include "isp-v4l2-ctrl.h"
36 #include "isp-v4l2-stream.h"
37 #include "isp-vb2.h"
38 #include "fw-interface.h"
39 #include <linux/dma-mapping.h>
40 #include <linux/of_reserved_mem.h>
41 #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
42 #include <linux/dma-contiguous.h>
43 #else
44 #include <linux/dma-map-ops.h>
45 #endif
46 #include "system_am_sc.h"
47
48 #define ISP_V4L2_NUM_INPUTS 1
49
50
51 /* isp_v4l2_dev_t to destroy video device */
52 static isp_v4l2_dev_t *g_isp_v4l2_devs[FIRMWARE_CONTEXT_NUMBER];
53 static isp_v4l2_dev_t *g_isp_v4l2_dev;
54 void *isp_kaddr = NULL;
55 resource_size_t isp_paddr = 0;
56 #define SIZE_1M (1024 * 1024UL)
57 #define DEFAULT_TEMPER_BUFFER_SIZE 16
58 #define DEFAULT_TEMPER_LINE_OFFSET 3840*3
59 #define DEFAULT_TEMPER_FRAME_NUM 1
60 #define DEFAULT_TEMPER_FRAME_SIZE 3840*2160*6
61 #define ENFORCE_TEMPER3_DISABLE 0
62 #define ENFORCE_TEMPER3_ENABLE 3
63 unsigned int temper_line_offset = DEFAULT_TEMPER_LINE_OFFSET;
64 unsigned int temper_frame_num = DEFAULT_TEMPER_FRAME_NUM;
65 unsigned int temper_frame_size = DEFAULT_TEMPER_FRAME_SIZE;
66 unsigned int temper3 = 1;
67 module_param(temper3, uint, 0664);
68 MODULE_PARM_DESC(temper3, "\n temper3 enable\n");
69 /* ----------------------------------------------------------------
70 * V4L2 file handle structures and functions
71 * : implementing multi stream
72 */
73 #define fh_to_private( __fh ) \
74 container_of( __fh, struct isp_v4l2_fh, fh )
75
76 struct isp_v4l2_fh {
77 struct v4l2_fh fh;
78 unsigned int stream_id;
79 struct vb2_queue vb2_q;
80 };
81
isp_v4l2_fh_open(struct file * file)82 static int isp_v4l2_fh_open( struct file *file )
83 {
84 isp_v4l2_dev_t *dev = video_drvdata( file );
85 struct isp_v4l2_fh *sp;
86 int i;
87
88 sp = kzalloc( sizeof( struct isp_v4l2_fh ), GFP_KERNEL );
89 if ( !sp )
90 return -ENOMEM;
91
92 unsigned int stream_opened = atomic_read( &dev->opened );
93 /* update open counter */
94
95 if ( stream_opened > V4L2_STREAM_TYPE_MAX ) {
96 LOG( LOG_CRIT, "too many open streams." );
97 #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
98 kzfree( sp );
99 #else
100 kfree_sensitive( sp );
101 #endif
102 return -EBUSY;
103 }
104
105 file->private_data = &sp->fh;
106
107 if ( mutex_lock_interruptible( &dev->file_lock ) )
108 LOG( LOG_CRIT, "mutex_lock_interruptible failed.\n" );
109 for ( i = 0; i < V4L2_STREAM_TYPE_MAX; i++ ) {
110 if ( ( dev->stream_mask & ( 1 << i ) ) == 0 ) {
111 dev->stream_mask |= ( 1 << i );
112 sp->stream_id = i;
113 break;
114 }
115 }
116 mutex_unlock( &dev->file_lock );
117
118 v4l2_fh_init( &sp->fh, &dev->video_dev );
119 v4l2_fh_add( &sp->fh );
120
121 return 0;
122 }
123
isp_v4l2_fh_release(struct file * file)124 static int isp_v4l2_fh_release( struct file *file )
125 {
126 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
127
128 if ( sp ) {
129 v4l2_fh_del( &sp->fh );
130 v4l2_fh_exit( &sp->fh );
131 }
132
133 #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
134 kzfree( sp );
135 #else
136 kfree_sensitive( sp );
137 #endif
138
139 return 0;
140 }
141
142
143 /* ----------------------------------------------------------------
144 * V4L2 file operations
145 */
isp_v4l2_fop_open(struct file * file)146 static int isp_v4l2_fop_open( struct file *file )
147 {
148 int rc = 0;
149 isp_v4l2_dev_t *dev = video_drvdata( file );
150 struct isp_v4l2_fh *sp;
151
152 atomic_add( 1, &dev->opened );
153 /* open file header */
154 rc = isp_v4l2_fh_open( file );
155 if ( rc < 0 ) {
156 LOG( LOG_ERR, "Error, file handle open fail (rc=%d)", rc );
157 goto fh_open_fail;
158 }
159 sp = fh_to_private( file->private_data );
160
161 LOG( LOG_INFO, "isp_v4l2: %s: called for sid:%d.", __func__, sp->stream_id );
162 /* init stream */
163 isp_v4l2_stream_init( &dev->pstreams[sp->stream_id], sp->stream_id, dev->ctx_id );
164 if ( sp->stream_id == 0 ) {
165 // stream_id 0 is a full resolution
166 dev->stream_id_index[V4L2_STREAM_TYPE_FR] = sp->stream_id;
167 acamera_api_dma_buff_queue_reset(dev->ctx_id, dma_fr);
168 }
169 #if ISP_HAS_DS1
170 else if ( sp->stream_id == V4L2_STREAM_TYPE_DS1 ) {
171 dev->stream_id_index[V4L2_STREAM_TYPE_DS1] = sp->stream_id;
172 acamera_api_dma_buff_queue_reset(dev->ctx_id, dma_ds1);
173 }
174 #endif
175 #if ISP_HAS_DS2
176 else if (sp->stream_id == V4L2_STREAM_TYPE_DS2) {
177 dev->stream_id_index[V4L2_STREAM_TYPE_DS2] = sp->stream_id;
178 }
179 #endif
180
181 /* init vb2 queue */
182
183 rc = isp_vb2_queue_init( &sp->vb2_q, &dev->mlock, dev->pstreams[sp->stream_id], dev->v4l2_dev->dev );
184 if ( rc < 0 ) {
185 LOG( LOG_ERR, "Error, vb2 queue init fail (rc=%d)", rc );
186 goto vb2_q_fail;
187 }
188 if (sp->stream_id == V4L2_STREAM_TYPE_FR ||
189 sp->stream_id == V4L2_STREAM_TYPE_DS1) {
190
191 sp->vb2_q.dev = g_isp_v4l2_devs[dev->ctx_id]->pdev;
192 }
193 #if ISP_HAS_DS2
194 else if (sp->stream_id == V4L2_STREAM_TYPE_DS2) {
195 sp->vb2_q.dev = g_isp_v4l2_devs[dev->ctx_id]->pdev;
196 }
197 #endif
198
199 g_isp_v4l2_dev = g_isp_v4l2_devs[dev->ctx_id];
200 fw_intf_isp_set_current_ctx_id(dev->ctx_id);
201
202 dev->pstreams[sp->stream_id]->vb2_q = &sp->vb2_q;
203 /* init fh_ptr */
204 if ( mutex_lock_interruptible( &dev->notify_lock ) )
205 LOG( LOG_CRIT, "mutex_lock_interruptible failed.\n" );
206 dev->fh_ptr[sp->stream_id] = &( sp->fh );
207 mutex_unlock( &dev->notify_lock );
208
209 return rc;
210
211 vb2_q_fail:
212 isp_v4l2_stream_deinit( dev->pstreams[sp->stream_id], atomic_read(&dev->stream_on_cnt) );
213
214 //too_many_stream:
215 isp_v4l2_fh_release( file );
216
217 fh_open_fail:
218 atomic_sub( 1, &dev->opened );
219 return rc;
220 }
221
isp_v4l2_fop_close(struct file * file)222 static int isp_v4l2_fop_close( struct file *file )
223 {
224 isp_v4l2_dev_t *dev = video_drvdata( file );
225 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
226 isp_v4l2_stream_t *pstream = dev->pstreams[sp->stream_id];
227 int open_counter;
228
229 LOG( LOG_INFO, "isp_v4l2: %s: called for sid:%d.", __func__, sp->stream_id );
230
231 /* deinit fh_ptr */
232 if ( mutex_lock_interruptible( &dev->notify_lock ) )
233 LOG( LOG_CRIT, "mutex_lock_interruptible failed.\n" );
234 dev->fh_ptr[sp->stream_id] = NULL;
235 mutex_unlock( &dev->notify_lock );
236
237 /* deinit stream */
238 if ( pstream ) {
239 if ( pstream->stream_type < V4L2_STREAM_TYPE_MAX )
240 dev->stream_id_index[pstream->stream_type] = -1;
241 if (pstream->stream_started)
242 {
243 isp_v4l2_stream_type_t type = pstream->stream_type;
244 isp_v4l2_stream_deinit( pstream, atomic_read(&dev->stream_on_cnt) );
245 if (atomic_read(&dev->stream_on_cnt) && ( type != V4L2_STREAM_TYPE_META))
246 atomic_sub_return( 1, &dev->stream_on_cnt );
247 dev->pstreams[sp->stream_id] = NULL;
248 }
249 if ( dev->pstreams[sp->stream_id] ) {
250 #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
251 kzfree( dev->pstreams[sp->stream_id] );
252 #else
253 kfree_sensitive( dev->pstreams[sp->stream_id] );
254 #endif
255 dev->pstreams[sp->stream_id] = NULL;
256 }
257 }
258
259 /* release vb2 queue */
260 if ( sp->vb2_q.lock )
261 mutex_lock( sp->vb2_q.lock );
262
263 isp_vb2_queue_release( &sp->vb2_q, pstream );
264
265 if ( sp->vb2_q.lock )
266 mutex_unlock( sp->vb2_q.lock );
267
268 if ( mutex_lock_interruptible( &dev->file_lock ) )
269 LOG( LOG_CRIT, "mutex_lock_interruptible failed.\n" );
270
271 dev->stream_mask &= ~( 1 << sp->stream_id );
272 mutex_unlock( &dev->file_lock );
273 open_counter = atomic_sub_return( 1, &dev->opened );
274
275 /* release file handle */
276 isp_v4l2_fh_release( file );
277
278 return 0;
279 }
280
isp_v4l2_fop_read(struct file * filep,char __user * buf,size_t count,loff_t * ppos)281 static ssize_t isp_v4l2_fop_read( struct file *filep,
282 char __user *buf, size_t count, loff_t *ppos )
283 {
284 struct isp_v4l2_fh *sp = fh_to_private( filep->private_data );
285 int rc = 0;
286
287 rc = vb2_read( &sp->vb2_q, buf, count, ppos, filep->f_flags & O_NONBLOCK );
288
289 return rc;
290 }
291
isp_v4l2_fop_poll(struct file * filep,struct poll_table_struct * wait)292 static unsigned int isp_v4l2_fop_poll( struct file *filep,
293 struct poll_table_struct *wait )
294 {
295 struct isp_v4l2_fh *sp = fh_to_private( filep->private_data );
296 int rc = 0;
297
298 if ( sp->vb2_q.lock && mutex_lock_interruptible( sp->vb2_q.lock ) )
299 return POLLERR;
300
301 rc = vb2_poll( &sp->vb2_q, filep, wait );
302
303 if ( sp->vb2_q.lock )
304 mutex_unlock( sp->vb2_q.lock );
305
306 return rc;
307 }
308
isp_v4l2_fop_mmap(struct file * file,struct vm_area_struct * vma)309 static int isp_v4l2_fop_mmap( struct file *file, struct vm_area_struct *vma )
310 {
311 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
312 int rc = 0;
313
314 rc = vb2_mmap( &sp->vb2_q, vma );
315
316 return rc;
317 }
318
319 static const struct v4l2_file_operations isp_v4l2_fops = {
320 .owner = THIS_MODULE,
321 .open = isp_v4l2_fop_open,
322 .release = isp_v4l2_fop_close,
323 .read = isp_v4l2_fop_read,
324 .poll = isp_v4l2_fop_poll,
325 .unlocked_ioctl = video_ioctl2,
326 .mmap = isp_v4l2_fop_mmap,
327 };
328
329
330 /* ----------------------------------------------------------------
331 * V4L2 ioctl operations
332 */
isp_v4l2_querycap(struct file * file,void * priv,struct v4l2_capability * cap)333 static int isp_v4l2_querycap( struct file *file, void *priv, struct v4l2_capability *cap )
334 {
335 isp_v4l2_dev_t *dev = video_drvdata( file );
336
337 LOG( LOG_DEBUG, "dev: %p, file: %p, priv: %p.\n", dev, file, priv );
338
339 strcpy( cap->driver, "ARM-camera-isp" );
340 strcpy( cap->card, "juno R2" );
341 snprintf( cap->bus_info, sizeof( cap->bus_info ), "platform:%s", dev->v4l2_dev->name );
342
343 /* V4L2_CAP_VIDEO_CAPTURE_MPLANE */
344
345 cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
346 cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
347
348 return 0;
349 }
350
351
352 /* format related, will be moved to isp-v4l2-stream.c */
isp_v4l2_g_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)353 static int isp_v4l2_g_fmt_vid_cap( struct file *file, void *priv, struct v4l2_format *f )
354 {
355 isp_v4l2_dev_t *dev = video_drvdata( file );
356 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
357 isp_v4l2_stream_t *pstream = dev->pstreams[sp->stream_id];
358
359 LOG( LOG_DEBUG, "isp_v4l2_g_fmt_vid_cap sid:%d", sp->stream_id );
360
361 isp_v4l2_stream_get_format( pstream, f );
362
363 LOG( LOG_DEBUG, "v4l2_format: type: %u, w: %u, h: %u, pixelformat: 0x%x, field: %u, colorspace: %u, sizeimage: %u, bytesperline: %u, flags: %u.\n",
364 f->type,
365 f->fmt.pix.width,
366 f->fmt.pix.height,
367 f->fmt.pix.pixelformat,
368 f->fmt.pix.field,
369 f->fmt.pix.colorspace,
370 f->fmt.pix.sizeimage,
371 f->fmt.pix.bytesperline,
372 f->fmt.pix.flags );
373
374 return 0;
375 }
376
isp_v4l2_enum_fmt_vid_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)377 static int isp_v4l2_enum_fmt_vid_cap( struct file *file, void *priv, struct v4l2_fmtdesc *f )
378 {
379 isp_v4l2_dev_t *dev = video_drvdata( file );
380 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
381 isp_v4l2_stream_t *pstream = dev->pstreams[sp->stream_id];
382
383 return isp_v4l2_stream_enum_format( pstream, f );
384 }
385
isp_v4l2_try_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)386 static int isp_v4l2_try_fmt_vid_cap( struct file *file, void *priv, struct v4l2_format *f )
387 {
388 isp_v4l2_dev_t *dev = video_drvdata( file );
389 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
390 isp_v4l2_stream_t *pstream = dev->pstreams[sp->stream_id];
391 LOG( LOG_CRIT, "isp_v4l2_try_fmt_vid_cap" );
392 return isp_v4l2_stream_try_format( pstream, f );
393 }
394
isp_v4l2_s_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)395 static int isp_v4l2_s_fmt_vid_cap( struct file *file, void *priv, struct v4l2_format *f )
396 {
397 isp_v4l2_dev_t *dev = video_drvdata( file );
398 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
399 isp_v4l2_stream_t *pstream = dev->pstreams[sp->stream_id];
400 struct vb2_queue *q = &sp->vb2_q;
401 int rc = 0;
402 LOG( LOG_CRIT, "isp_v4l2_s_fmt_vid_cap sid:%d", sp->stream_id );
403 if ( vb2_is_busy( q ) )
404 return -EBUSY;
405
406 rc = isp_v4l2_stream_set_format( pstream, f );
407 if ( rc < 0 ) {
408 LOG( LOG_ERR, "set format failed." );
409 return rc;
410 }
411
412 /* update stream pointer index */
413 dev->stream_id_index[pstream->stream_type] = pstream->stream_id;
414
415 return 0;
416 }
417
isp_v4l2_enum_framesizes(struct file * file,void * priv,struct v4l2_frmsizeenum * fsize)418 static int isp_v4l2_enum_framesizes( struct file *file, void *priv, struct v4l2_frmsizeenum *fsize )
419 {
420 isp_v4l2_dev_t *dev = video_drvdata( file );
421 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
422 isp_v4l2_stream_t *pstream = dev->pstreams[sp->stream_id];
423
424 return isp_v4l2_stream_enum_framesizes( pstream, fsize );
425 }
426
427
428 /* Per-stream control operations */
isp_v4l2_is_q_busy(struct vb2_queue * queue,struct file * file)429 static inline bool isp_v4l2_is_q_busy( struct vb2_queue *queue, struct file *file )
430 {
431 return queue->owner && queue->owner != file->private_data;
432 }
433
isp_v4l2_streamon(struct file * file,void * priv,enum v4l2_buf_type i)434 static int isp_v4l2_streamon( struct file *file, void *priv, enum v4l2_buf_type i )
435 {
436 isp_v4l2_dev_t *dev = video_drvdata( file );
437 struct isp_v4l2_fh *sp = fh_to_private( priv );
438 isp_v4l2_stream_t *pstream = dev->pstreams[sp->stream_id];
439 int rc = 0;
440
441 if ( isp_v4l2_is_q_busy( &sp->vb2_q, file ) )
442 return -EBUSY;
443 else
444 if (pstream->stream_started)
445 return -EBUSY;
446
447 rc = vb2_streamon( &sp->vb2_q, i );
448 if ( rc != 0 ) {
449 LOG( LOG_ERR, "fail to vb2_streamon. (rc=%d)", rc );
450 return rc;
451 }
452
453 /* Start hardware */
454 rc = isp_v4l2_stream_on( pstream );
455 if ( rc != 0 ) {
456 LOG( LOG_ERR, "fail to isp_stream_on. (stream_id = %d, rc=%d)", sp->stream_id, rc );
457 isp_v4l2_stream_off( pstream, atomic_read(&dev->stream_on_cnt) );
458 return rc;
459 }
460
461 if (pstream->stream_type != V4L2_STREAM_TYPE_META)
462 atomic_add( 1, &dev->stream_on_cnt );
463
464 return rc;
465 }
466
isp_v4l2_streamoff(struct file * file,void * priv,enum v4l2_buf_type i)467 static int isp_v4l2_streamoff( struct file *file, void *priv, enum v4l2_buf_type i )
468 {
469 isp_v4l2_dev_t *dev = video_drvdata( file );
470 struct isp_v4l2_fh *sp = fh_to_private( priv );
471 isp_v4l2_stream_t *pstream = dev->pstreams[sp->stream_id];
472 int rc = 0;
473
474 if ( isp_v4l2_is_q_busy( &sp->vb2_q, file ) )
475 return -EBUSY;
476
477 /* Stop hardware */
478 isp_v4l2_stream_off( pstream, atomic_read(&dev->stream_on_cnt) );
479
480 /* vb streamoff */
481 rc = vb2_streamoff( &sp->vb2_q, i );
482
483 if (atomic_read(&dev->stream_on_cnt) && (pstream->stream_type != V4L2_STREAM_TYPE_META))
484 atomic_sub_return( 1, &dev->stream_on_cnt );
485
486 return rc;
487 }
488
489
490 /* input control */
isp_v4l2_enum_input(struct file * file,void * fh,struct v4l2_input * input)491 static int isp_v4l2_enum_input( struct file *file, void *fh, struct v4l2_input *input )
492 {
493 /* currently only support general camera input */
494 if ( input->index > 0 )
495 return -EINVAL;
496
497 strlcpy( input->name, "camera", sizeof( input->name ) );
498 input->type = V4L2_INPUT_TYPE_CAMERA;
499
500 return 0;
501 }
502
isp_v4l2_g_input(struct file * file,void * fh,unsigned int * input)503 static int isp_v4l2_g_input( struct file *file, void *fh, unsigned int *input )
504 {
505 /* currently only support general camera input */
506 *input = 0;
507
508 return 0;
509 }
510
isp_v4l2_s_input(struct file * file,void * fh,unsigned int input)511 static int isp_v4l2_s_input( struct file *file, void *fh, unsigned int input )
512 {
513 /* currently only support general camera input */
514 return input == 0 ? 0 : -EINVAL;
515 }
516
517
518 /* vb2 customization for multi-stream support */
isp_v4l2_reqbufs(struct file * file,void * priv,struct v4l2_requestbuffers * p)519 static int isp_v4l2_reqbufs( struct file *file, void *priv,
520 struct v4l2_requestbuffers *p )
521 {
522 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
523 int rc = 0;
524
525 LOG( LOG_DEBUG, "(stream_id = %d, ownermatch=%d)", sp->stream_id, isp_v4l2_is_q_busy( &sp->vb2_q, file ) );
526 if ( isp_v4l2_is_q_busy( &sp->vb2_q, file ) )
527 return -EBUSY;
528
529 rc = vb2_reqbufs( &sp->vb2_q, p );
530 if ( rc == 0 )
531 sp->vb2_q.owner = p->count ? file->private_data : NULL;
532
533 #if ISP_HAS_DS2
534 if (sp->stream_id == V4L2_STREAM_TYPE_DS2) {
535 am_sc_set_buf_num(p->count);
536 am_sc_system_init();
537 }
538 #endif
539
540 LOG( LOG_DEBUG, "sid:%d reqbuf p->type:%d p->memory %d p->count %d rc %d", sp->stream_id, p->type, p->memory, p->count, rc );
541 return rc;
542 }
543
isp_v4l2_querybuf(struct file * file,void * priv,struct v4l2_buffer * p)544 static int isp_v4l2_querybuf( struct file *file, void *priv, struct v4l2_buffer *p )
545 {
546 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
547 int rc = 0;
548
549 rc = vb2_querybuf( &sp->vb2_q, p );
550
551 LOG( LOG_DEBUG, "sid:%d querybuf p->type:%d p->index:%d , rc %d",
552 sp->stream_id, p->type, p->index, rc );
553
554 return rc;
555 }
556
isp_v4l2_qbuf(struct file * file,void * priv,struct v4l2_buffer * p)557 static int isp_v4l2_qbuf( struct file *file, void *priv, struct v4l2_buffer *p )
558 {
559 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
560 int rc = 0;
561
562 LOG( LOG_DEBUG, "(stream_id = %d, ownermatch=%d)", sp->stream_id, isp_v4l2_is_q_busy( &sp->vb2_q, file ) );
563 if ( isp_v4l2_is_q_busy( &sp->vb2_q, file ) ) {
564 return -EBUSY;
565 }
566
567 #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
568 rc = vb2_qbuf( &sp->vb2_q, p );
569 #else
570 struct video_device *vdev = video_devdata(file);
571 rc = vb2_qbuf( &sp->vb2_q, vdev->v4l2_dev->mdev, p );
572 #endif
573
574 LOG( LOG_DEBUG, "sid:%d qbuf p->type:%d p->index:%d, rc %d", sp->stream_id, p->type, p->index, rc );
575 return rc;
576 }
577
isp_v4l2_dqbuf(struct file * file,void * priv,struct v4l2_buffer * p)578 static int isp_v4l2_dqbuf( struct file *file, void *priv, struct v4l2_buffer *p )
579 {
580 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
581 int rc = 0;
582 LOG( LOG_DEBUG, "(stream_id = %d, ownermatch=%d)", sp->stream_id, isp_v4l2_is_q_busy( &sp->vb2_q, file ) );
583 if ( isp_v4l2_is_q_busy( &sp->vb2_q, file ) )
584 return -EBUSY;
585
586 rc = vb2_dqbuf( &sp->vb2_q, p, file->f_flags & O_NONBLOCK );
587 LOG( LOG_DEBUG, "sid:%d qbuf p->type:%d p->index:%d, rc %d", sp->stream_id, p->type, p->index, rc );
588 return rc;
589 }
590
591 #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
isp_v4l2_cropcap(struct file * file,void * fh,struct v4l2_cropcap * cap)592 static int isp_v4l2_cropcap(struct file *file, void *fh,
593 struct v4l2_cropcap *cap)
594 {
595 int ret = -1;
596 isp_v4l2_dev_t *dev = video_drvdata(file);
597 struct isp_v4l2_fh *sp = fh_to_private(fh);
598 isp_v4l2_stream_t *pstream = dev->pstreams[sp->stream_id];
599
600 if (pstream->stream_type == V4L2_STREAM_TYPE_FR ||
601 pstream->stream_type == V4L2_STREAM_TYPE_DS1)
602 ret = isp_v4l2_get_cropcap(pstream, cap);
603 else
604 LOG(LOG_ERR, "Error support this stream type: %d", pstream->stream_type);
605
606 return ret;
607 }
608
isp_v4l2_g_crop(struct file * file,void * fh,struct v4l2_crop * crop)609 static int isp_v4l2_g_crop(struct file *file, void *fh,
610 struct v4l2_crop *crop)
611 {
612 int ret = -1;
613 isp_v4l2_dev_t *dev = video_drvdata(file);
614 struct isp_v4l2_fh *sp = fh_to_private(fh);
615 isp_v4l2_stream_t *pstream = dev->pstreams[sp->stream_id];
616
617 if (pstream->stream_type == V4L2_STREAM_TYPE_FR ||
618 pstream->stream_type == V4L2_STREAM_TYPE_DS1)
619 ret = isp_v4l2_get_crop(pstream, crop);
620 else
621 LOG(LOG_ERR, "Error support this stream type: %d", pstream->stream_type);
622
623 return ret;
624 }
625
isp_v4l2_s_crop(struct file * file,void * fh,const struct v4l2_crop * crop)626 static int isp_v4l2_s_crop(struct file *file, void *fh,
627 const struct v4l2_crop *crop)
628 {
629 int ret = -1;
630 isp_v4l2_dev_t *dev = video_drvdata(file);
631 struct isp_v4l2_fh *sp = fh_to_private(fh);
632 isp_v4l2_stream_t *pstream = dev->pstreams[sp->stream_id];
633
634 if (pstream->stream_type == V4L2_STREAM_TYPE_FR ||
635 pstream->stream_type == V4L2_STREAM_TYPE_DS1)
636 ret = isp_v4l2_set_crop(pstream, crop);
637 else
638 LOG(LOG_ERR, "Error support this stream type: %d", pstream->stream_type);
639
640 return ret;
641 }
642 #else
isp_v4l2_g_pixelaspect(struct file * file,void * fh,int buf_type,struct v4l2_fract * aspect)643 static int isp_v4l2_g_pixelaspect(struct file *file, void *fh,
644 int buf_type, struct v4l2_fract *aspect)
645 {
646 int ret = -1;
647 isp_v4l2_dev_t *dev = video_drvdata(file);
648 struct isp_v4l2_fh *sp = fh_to_private(fh);
649 isp_v4l2_stream_t *pstream = dev->pstreams[sp->stream_id];
650
651 if (pstream->stream_type == V4L2_STREAM_TYPE_FR ||
652 pstream->stream_type == V4L2_STREAM_TYPE_DS1) {
653 struct v4l2_crop crop;
654 ret = isp_v4l2_get_crop(pstream, &crop);
655 if (!ret) {
656 aspect->numerator = crop.c.width;
657 aspect->denominator = crop.c.height;
658 }
659 pr_info("%s: aspect = (%u, %u), ret=%d\n", __func__, aspect->numerator, aspect->denominator, ret);
660 } else
661 LOG(LOG_ERR, "isp_v4l2_g_pixelaspect Error support this stream type: %d", pstream->stream_type);
662
663 return ret;
664 }
665 #endif
666
isp_v4l2_expbuf(struct file * file,void * priv,struct v4l2_exportbuffer * ex_buf)667 static int isp_v4l2_expbuf(struct file *file, void *priv, struct v4l2_exportbuffer *ex_buf)
668 {
669 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
670
671 if (sp == NULL || ex_buf == NULL) {
672 LOG(LOG_ERR, "Error invalid input param");
673 return -EINVAL;
674 }
675
676 return vb2_expbuf(&sp->vb2_q, ex_buf);
677 }
678
isp_v4l2_enum_frameintervals(struct file * file,void * fh,struct v4l2_frmivalenum * fival)679 int isp_v4l2_enum_frameintervals(struct file *file, void *fh,
680 struct v4l2_frmivalenum *fival)
681 {
682 isp_v4l2_dev_t *dev = video_drvdata( file );
683 struct isp_v4l2_fh *sp = fh_to_private( file->private_data );
684 isp_v4l2_stream_t *pstream = dev->pstreams[sp->stream_id];
685
686 return isp_v4l2_stream_enum_frameintervals( pstream, fival);
687 }
688
689
690 static const struct v4l2_ioctl_ops isp_v4l2_ioctl_ops = {
691 .vidioc_querycap = isp_v4l2_querycap,
692
693 /* Per-stream config operations */
694 .vidioc_g_fmt_vid_cap_mplane = isp_v4l2_g_fmt_vid_cap,
695 #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
696 .vidioc_enum_fmt_vid_cap_mplane = isp_v4l2_enum_fmt_vid_cap,
697 #else
698 .vidioc_enum_fmt_sdr_cap = isp_v4l2_enum_fmt_vid_cap,
699 #endif
700 .vidioc_try_fmt_vid_cap_mplane = isp_v4l2_try_fmt_vid_cap,
701 .vidioc_s_fmt_vid_cap_mplane = isp_v4l2_s_fmt_vid_cap,
702
703 .vidioc_g_fmt_vid_cap = isp_v4l2_g_fmt_vid_cap,
704 .vidioc_enum_fmt_vid_cap = isp_v4l2_enum_fmt_vid_cap,
705 .vidioc_try_fmt_vid_cap = isp_v4l2_try_fmt_vid_cap,
706 .vidioc_s_fmt_vid_cap = isp_v4l2_s_fmt_vid_cap,
707 .vidioc_enum_framesizes = isp_v4l2_enum_framesizes,
708
709 /* Per-stream control operations */
710 .vidioc_streamon = isp_v4l2_streamon,
711 .vidioc_streamoff = isp_v4l2_streamoff,
712
713 /* input control */
714 .vidioc_enum_input = isp_v4l2_enum_input,
715 .vidioc_g_input = isp_v4l2_g_input,
716 .vidioc_s_input = isp_v4l2_s_input,
717
718 /* vb2 customization for multi-stream support */
719 .vidioc_reqbufs = isp_v4l2_reqbufs,
720
721 .vidioc_querybuf = isp_v4l2_querybuf,
722 .vidioc_qbuf = isp_v4l2_qbuf,
723 .vidioc_dqbuf = isp_v4l2_dqbuf,
724
725 /* v4l2 event ioctls */
726 .vidioc_log_status = v4l2_ctrl_log_status,
727 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
728 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
729
730 /* crop ioctls */
731 #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
732 .vidioc_cropcap = isp_v4l2_cropcap,
733 .vidioc_g_crop = isp_v4l2_g_crop,
734 .vidioc_s_crop = isp_v4l2_s_crop,
735 #else
736 .vidioc_g_pixelaspect = isp_v4l2_g_pixelaspect,
737 #endif
738
739 .vidioc_expbuf = isp_v4l2_expbuf,
740 #ifdef CONFIG_ANDROID_OS
741 .vidioc_enum_frameintervals = isp_v4l2_enum_frameintervals,
742 #endif
743 };
744
isp_cma_alloc(struct platform_device * pdev,unsigned long size)745 static int isp_cma_alloc(struct platform_device *pdev, unsigned long size)
746 {
747 struct page *cma_pages = NULL;
748 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))
749 cma_pages = dma_alloc_from_contiguous(
750 &(pdev->dev), size >> PAGE_SHIFT, 0, false);
751 #else
752 cma_pages = dma_alloc_from_contiguous(
753 &(pdev->dev), size >> PAGE_SHIFT, 0);
754 #endif
755 if (cma_pages) {
756 isp_paddr = page_to_phys(cma_pages);
757 } else {
758 LOG(LOG_ERR, "Failed alloc cma pages.\n");
759 return -1;
760 }
761 isp_kaddr = (void *)cma_pages;
762
763 return 0;
764 }
765
isp_cma_free(struct platform_device * pdev,void * kaddr,unsigned long size)766 static void isp_cma_free(struct platform_device *pdev, void *kaddr, unsigned long size)
767 {
768 struct page *cma_pages = NULL;
769 bool rc = false;
770
771 if (pdev == NULL || kaddr == NULL) {
772 LOG(LOG_ERR, "Error input param\n");
773 return;
774 }
775
776 cma_pages = kaddr;
777
778 rc = dma_release_from_contiguous(&(pdev->dev), cma_pages, size >> PAGE_SHIFT);
779 if (rc == false) {
780 LOG(LOG_ERR, "Failed to release cma buffer\n");
781 return;
782 }
783 }
784
isp_v4l2_init_dev(uint32_t ctx_id,struct v4l2_device * v4l2_dev)785 static int isp_v4l2_init_dev( uint32_t ctx_id, struct v4l2_device *v4l2_dev )
786 {
787 isp_v4l2_dev_t *dev;
788 struct video_device *vfd;
789 v4l2_std_id tvnorms_cap = 0;
790 int rc = 0;
791 int i;
792
793 if ( ctx_id >= FIRMWARE_CONTEXT_NUMBER ) {
794 LOG( LOG_ERR, "Invalid ctx numbr: %d, max: %d.", ctx_id, FIRMWARE_CONTEXT_NUMBER - 1 );
795 return -EINVAL;
796 }
797
798 /* allocate main isp_v4l2 state structure */
799 dev = kzalloc( sizeof( *dev ), GFP_KERNEL );
800 if ( !dev )
801 return -ENOMEM;
802
803 memset( dev, 0x0, sizeof( isp_v4l2_dev_t ) );
804
805 /* register v4l2_device */
806
807 dev->v4l2_dev = v4l2_dev;
808 dev->ctx_id = ctx_id;
809
810 /* init v4l2 controls */
811 dev->isp_v4l2_ctrl.v4l2_dev = dev->v4l2_dev;
812 dev->isp_v4l2_ctrl.video_dev = &dev->video_dev;
813 rc = isp_v4l2_ctrl_init( ctx_id, &dev->isp_v4l2_ctrl );
814 if ( rc )
815 goto free_dev;
816
817 /* initialize locks */
818 mutex_init( &dev->mlock );
819 mutex_init( &dev->notify_lock );
820 mutex_init( &dev->file_lock);
821
822 /* initialize stream id table */
823 for ( i = 0; i < V4L2_STREAM_TYPE_MAX; i++ ) {
824 dev->stream_id_index[i] = -1;
825 }
826
827 /* initialize open counter */
828 atomic_set( &dev->stream_on_cnt, 0 );
829 atomic_set( &dev->opened, 0 );
830
831 /* finally start creating the device nodes */
832 vfd = &dev->video_dev;
833 strlcpy( vfd->name, "isp_v4l2-vid-cap", sizeof( vfd->name ) );
834 vfd->fops = &isp_v4l2_fops;
835 vfd->ioctl_ops = &isp_v4l2_ioctl_ops;
836 vfd->release = video_device_release_empty;
837 vfd->v4l2_dev = dev->v4l2_dev;
838 vfd->queue = NULL; // queue will be customized in file handle
839 vfd->tvnorms = tvnorms_cap;
840 vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
841 #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
842 vfd->vfl_type = VFL_TYPE_GRABBER;
843 #else
844 vfd->vfl_type = VFL_TYPE_VIDEO;
845 #endif
846
847 /*
848 * Provide a mutex to v4l2 core. It will be used to protect
849 * all fops and v4l2 ioctls.
850 */
851 vfd->lock = &dev->mlock;
852 video_set_drvdata( vfd, dev );
853
854 /* store dev pointer to destroy later and find stream */
855 g_isp_v4l2_devs[ctx_id] = dev;
856
857 /* videoX start number, -1 is autodetect */
858 #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
859 rc = video_register_device( vfd, VFL_TYPE_GRABBER, VIDEO_NODE_NUM );
860 #else
861 rc = video_register_device( vfd, VFL_TYPE_VIDEO, VIDEO_NODE_NUM );
862 #endif
863 if ( rc < 0 )
864 goto unreg_dev;
865
866 LOG( LOG_INFO, "V4L2 capture device registered as %s.",
867 video_device_node_name( vfd ) );
868
869
870 return rc;
871
872 unreg_dev:
873 video_unregister_device( &dev->video_dev );
874 isp_v4l2_ctrl_deinit( &dev->isp_v4l2_ctrl );
875
876 free_dev:
877 g_isp_v4l2_devs[ctx_id] = NULL;
878 kfree( dev );
879
880 return rc;
881 }
882
isp_v4l2_destroy_dev(int ctx_id)883 static void isp_v4l2_destroy_dev( int ctx_id )
884 {
885 if ( g_isp_v4l2_devs[ctx_id] ) {
886 LOG( LOG_INFO, "unregistering %s.", video_device_node_name( &g_isp_v4l2_devs[ctx_id]->video_dev ) );
887
888 /* unregister video device */
889 video_unregister_device( &g_isp_v4l2_devs[ctx_id]->video_dev );
890
891 isp_v4l2_ctrl_deinit( &g_isp_v4l2_devs[ctx_id]->isp_v4l2_ctrl );
892
893 kfree( g_isp_v4l2_devs[ctx_id] );
894 g_isp_v4l2_devs[ctx_id] = NULL;
895 } else {
896 LOG( LOG_INFO, "isp_v4l2_dev ctx_id: %d is NULL, skip.", ctx_id );
897 }
898 }
899
900 /* ----------------------------------------------------------------
901 * V4L2 external interface for probe
902 */
isp_v4l2_create_instance(struct v4l2_device * v4l2_dev,struct platform_device * pdev,uint32_t hw_isp_addr)903 int isp_v4l2_create_instance( struct v4l2_device *v4l2_dev, struct platform_device *pdev, uint32_t hw_isp_addr )
904 {
905 uint32_t ctx_id;
906 uint32_t ret_value;
907 unsigned int temper_buf_size;
908 int rc = 0;
909
910 if ( v4l2_dev == NULL ) {
911 LOG( LOG_ERR, "Invalid parameter" );
912 return -EINVAL;
913 }
914
915 rc = of_property_read_u32(pdev->dev.of_node, "temper-frame-num",
916 &temper_frame_num);
917 if ( rc != 0 ) {
918 temper_frame_num = DEFAULT_TEMPER_FRAME_NUM * temper3;
919 }
920 else
921 {
922 if ( temper3 == ENFORCE_TEMPER3_DISABLE )
923 temper_frame_num = DEFAULT_TEMPER_FRAME_NUM;
924 else if ( temper3 == ENFORCE_TEMPER3_ENABLE )
925 temper_frame_num = DEFAULT_TEMPER_FRAME_NUM * 2;
926 }
927
928 rc = of_property_read_u32(pdev->dev.of_node, "temper-frame-size",
929 &temper_frame_size);
930 if ( rc != 0 ) {
931 temper_frame_size = DEFAULT_TEMPER_FRAME_SIZE;
932 }
933
934 rc = of_property_read_u32(pdev->dev.of_node, "temper-buf-size",
935 &temper_buf_size);
936 if ( rc != 0 ) {
937 LOG(LOG_ERR, "failed to get temper-buf-size from dts, use default value\n");
938 temper_buf_size = DEFAULT_TEMPER_BUFFER_SIZE;
939 }
940
941 if ( temper_buf_size < (( temper_frame_num * temper_frame_size) / SIZE_1M) )
942 temper_buf_size = (temper_frame_num * temper_frame_size) / SIZE_1M;
943
944 rc = isp_cma_alloc(pdev, temper_buf_size * SIZE_1M);
945 if (rc < 0)
946 return rc;
947
948 rc = of_property_read_u32(pdev->dev.of_node, "temper-line-offset", &temper_line_offset);
949 if (rc != 0) {
950 LOG(LOG_ERR, "failed to get temper_line_offset from dts, use default value\n");
951 temper_line_offset = DEFAULT_TEMPER_LINE_OFFSET;
952 }
953
954 /* initialize isp */
955 rc = fw_intf_isp_init(hw_isp_addr);
956 if ( rc < 0 )
957 goto free_cma;
958
959 /* initialize stream related resources to prepare for streaming.
960 * It should be called after sensor initialized.
961 */
962 for ( ctx_id = 0; ctx_id < FIRMWARE_CONTEXT_NUMBER; ctx_id++ ) {
963 rc = isp_v4l2_stream_init_static_resources( pdev, ctx_id );
964 if ( rc < 0 )
965 goto deinit_fw_intf;
966 }
967
968 /* check sensor devices */
969 for ( ctx_id = 0; ctx_id < FIRMWARE_CONTEXT_NUMBER; ctx_id++ ) {
970 rc = acamera_command(ctx_id, TSENSOR, SENSOR_HWID, 0, COMMAND_GET, &ret_value);
971 if ( rc ) {
972 LOG( LOG_CRIT, "isp_v4l2_init ctx_id: %d failed.", ctx_id );
973 rc = 0;
974 goto deinit_fw_intf;
975 }
976 }
977
978 /* initialize v4l2 layer devices */
979 for ( ctx_id = 0; ctx_id < FIRMWARE_CONTEXT_NUMBER; ctx_id++ ) {
980 rc = isp_v4l2_init_dev( ctx_id, v4l2_dev );
981 if ( rc ) {
982 LOG( LOG_ERR, "isp_v4l2_init ctx_id: %d failed.", ctx_id );
983 goto unreg_dev;
984 }
985 g_isp_v4l2_devs[ctx_id]->pdev = &pdev->dev;
986 }
987
988 g_isp_v4l2_dev = g_isp_v4l2_devs[0];
989 g_isp_v4l2_dev->temper_buf_size = temper_buf_size;
990
991 return 0;
992
993 unreg_dev:
994 for ( ctx_id = 0; ctx_id < FIRMWARE_CONTEXT_NUMBER; ctx_id++ ) {
995 isp_v4l2_destroy_dev( ctx_id );
996 }
997
998 deinit_fw_intf:
999 fw_intf_isp_deinit();
1000
1001 free_cma:
1002 isp_cma_free(pdev, isp_kaddr, temper_buf_size * SIZE_1M);
1003
1004 return rc;
1005 }
1006
isp_v4l2_destroy_instance(struct platform_device * pdev)1007 void isp_v4l2_destroy_instance( struct platform_device *pdev )
1008 {
1009 if ( g_isp_v4l2_devs[0] ) {
1010 int ctx_id;
1011
1012 /* deinitialize firmware & stream resources */
1013 fw_intf_isp_deinit();
1014 isp_v4l2_stream_deinit_static_resources(pdev);
1015
1016 isp_cma_free(pdev, isp_kaddr,
1017 (g_isp_v4l2_dev->temper_buf_size) * SIZE_1M);
1018
1019 for ( ctx_id = 0; ctx_id < FIRMWARE_CONTEXT_NUMBER; ctx_id++ ) {
1020 isp_v4l2_destroy_dev( ctx_id );
1021 }
1022 }
1023 }
1024
1025
1026 /* ----------------------------------------------------------------
1027 * stream finder utility function
1028 */
isp_v4l2_find_stream(isp_v4l2_stream_t ** ppstream,int ctx_number,isp_v4l2_stream_type_t stream_type)1029 int isp_v4l2_find_stream( isp_v4l2_stream_t **ppstream,
1030 int ctx_number, isp_v4l2_stream_type_t stream_type )
1031 {
1032 int stream_id;
1033
1034 *ppstream = NULL;
1035
1036 if ( g_isp_v4l2_dev == NULL ) {
1037 return -EBUSY;
1038 }
1039
1040 if ( stream_type >= V4L2_STREAM_TYPE_MAX || stream_type < 0 ) {
1041 return -EINVAL;
1042 }
1043
1044 stream_id = g_isp_v4l2_devs[ctx_number]->stream_id_index[stream_type];
1045 if ( stream_id < 0 || stream_id >= V4L2_STREAM_TYPE_MAX || g_isp_v4l2_dev->pstreams[stream_id] == NULL ) {
1046 return -EBUSY;
1047 }
1048
1049 *ppstream = g_isp_v4l2_devs[ctx_number]->pstreams[stream_id];
1050
1051 return 0;
1052 }
1053
isp_v4l2_get_dev(uint32_t ctx_number)1054 isp_v4l2_dev_t *isp_v4l2_get_dev( uint32_t ctx_number )
1055 {
1056 return g_isp_v4l2_devs[ctx_number];
1057 }
1058
1059 /* ----------------------------------------------------------------
1060 * event notifier utility function
1061 */
isp_v4l2_notify_event(int ctx_id,int stream_id,uint32_t event_type)1062 int isp_v4l2_notify_event( int ctx_id, int stream_id, uint32_t event_type )
1063 {
1064 struct v4l2_event event;
1065
1066 if ( g_isp_v4l2_devs[ctx_id] == NULL ) {
1067 return -EBUSY;
1068 }
1069
1070 if ( mutex_lock_interruptible( &g_isp_v4l2_devs[ctx_id]->notify_lock ) )
1071 LOG( LOG_CRIT, "mutex_lock_interruptible failed.\n" );
1072 if ( g_isp_v4l2_devs[ctx_id]->fh_ptr[stream_id] == NULL ) {
1073 LOG( LOG_ERR, "Error, no fh_ptr exists for stream id %d (event_type = %d)", stream_id, event_type );
1074 mutex_unlock( &g_isp_v4l2_devs[ctx_id]->notify_lock );
1075 return -EINVAL;
1076 }
1077
1078 memset( &event, 0, sizeof( event ) );
1079 event.type = event_type;
1080
1081 v4l2_event_queue_fh( g_isp_v4l2_devs[ctx_id]->fh_ptr[stream_id], &event );
1082 mutex_unlock( &g_isp_v4l2_devs[ctx_id]->notify_lock );
1083
1084 return 0;
1085 }
1086