2013-06-04 18:22:30 +04:00
/*
* vsp1_video . c - - R - Car VSP1 Video Node
*
2015-03-15 17:33:07 +03:00
* Copyright ( C ) 2013 - 2015 Renesas Electronics Corporation
2013-06-04 18:22:30 +04:00
*
* Contact : Laurent Pinchart ( laurent . pinchart @ ideasonboard . com )
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/list.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/slab.h>
# include <linux/v4l2-mediabus.h>
# include <linux/videodev2.h>
2015-08-02 20:15:23 +03:00
# include <linux/wait.h>
2013-06-04 18:22:30 +04:00
# include <media/media-entity.h>
# include <media/v4l2-dev.h>
# include <media/v4l2-fh.h>
# include <media/v4l2-ioctl.h>
# include <media/v4l2-subdev.h>
2015-09-22 16:30:29 +03:00
# include <media/videobuf2-v4l2.h>
2013-06-04 18:22:30 +04:00
# include <media/videobuf2-dma-contig.h>
# include "vsp1.h"
2013-07-11 01:03:46 +04:00
# include "vsp1_bru.h"
2015-11-01 20:18:56 +03:00
# include "vsp1_dl.h"
2013-06-04 18:22:30 +04:00
# include "vsp1_entity.h"
2015-08-02 20:15:23 +03:00
# include "vsp1_pipe.h"
2013-06-04 18:22:30 +04:00
# include "vsp1_rwpf.h"
2014-05-31 04:45:48 +04:00
# include "vsp1_uds.h"
2013-06-04 18:22:30 +04:00
# include "vsp1_video.h"
# define VSP1_VIDEO_DEF_FORMAT V4L2_PIX_FMT_YUYV
# define VSP1_VIDEO_DEF_WIDTH 1024
# define VSP1_VIDEO_DEF_HEIGHT 768
# define VSP1_VIDEO_MIN_WIDTH 2U
# define VSP1_VIDEO_MAX_WIDTH 8190U
# define VSP1_VIDEO_MIN_HEIGHT 2U
# define VSP1_VIDEO_MAX_HEIGHT 8190U
/* -----------------------------------------------------------------------------
* Helper functions
*/
static struct v4l2_subdev *
vsp1_video_remote_subdev ( struct media_pad * local , u32 * pad )
{
struct media_pad * remote ;
remote = media_entity_remote_pad ( local ) ;
2015-05-08 04:12:32 +03:00
if ( ! remote | | ! is_media_entity_v4l2_subdev ( remote - > entity ) )
2013-06-04 18:22:30 +04:00
return NULL ;
if ( pad )
* pad = remote - > index ;
return media_entity_to_v4l2_subdev ( remote - > entity ) ;
}
static int vsp1_video_verify_format ( struct vsp1_video * video )
{
struct v4l2_subdev_format fmt ;
struct v4l2_subdev * subdev ;
int ret ;
subdev = vsp1_video_remote_subdev ( & video - > pad , & fmt . pad ) ;
if ( subdev = = NULL )
return - EINVAL ;
fmt . which = V4L2_SUBDEV_FORMAT_ACTIVE ;
ret = v4l2_subdev_call ( subdev , pad , get_fmt , NULL , & fmt ) ;
if ( ret < 0 )
return ret = = - ENOIOCTLCMD ? - EINVAL : ret ;
2015-07-28 20:00:43 +03:00
if ( video - > rwpf - > fmtinfo - > mbus ! = fmt . format . code | |
video - > rwpf - > format . height ! = fmt . format . height | |
video - > rwpf - > format . width ! = fmt . format . width )
2013-06-04 18:22:30 +04:00
return - EINVAL ;
return 0 ;
}
static int __vsp1_video_try_format ( struct vsp1_video * video ,
struct v4l2_pix_format_mplane * pix ,
const struct vsp1_format_info * * fmtinfo )
{
2014-05-27 02:57:21 +04:00
static const u32 xrgb_formats [ ] [ 2 ] = {
{ V4L2_PIX_FMT_RGB444 , V4L2_PIX_FMT_XRGB444 } ,
{ V4L2_PIX_FMT_RGB555 , V4L2_PIX_FMT_XRGB555 } ,
{ V4L2_PIX_FMT_BGR32 , V4L2_PIX_FMT_XBGR32 } ,
{ V4L2_PIX_FMT_RGB32 , V4L2_PIX_FMT_XRGB32 } ,
} ;
2013-06-04 18:22:30 +04:00
const struct vsp1_format_info * info ;
unsigned int width = pix - > width ;
unsigned int height = pix - > height ;
unsigned int i ;
2014-05-27 02:57:21 +04:00
/* Backward compatibility: replace deprecated RGB formats by their XRGB
* equivalent . This selects the format older userspace applications want
* while still exposing the new format .
*/
for ( i = 0 ; i < ARRAY_SIZE ( xrgb_formats ) ; + + i ) {
if ( xrgb_formats [ i ] [ 0 ] = = pix - > pixelformat ) {
pix - > pixelformat = xrgb_formats [ i ] [ 1 ] ;
break ;
}
}
2013-06-04 18:22:30 +04:00
/* Retrieve format information and select the default format if the
* requested format isn ' t supported .
*/
info = vsp1_get_format_info ( pix - > pixelformat ) ;
if ( info = = NULL )
info = vsp1_get_format_info ( VSP1_VIDEO_DEF_FORMAT ) ;
pix - > pixelformat = info - > fourcc ;
pix - > colorspace = V4L2_COLORSPACE_SRGB ;
pix - > field = V4L2_FIELD_NONE ;
memset ( pix - > reserved , 0 , sizeof ( pix - > reserved ) ) ;
/* Align the width and height for YUV 4:2:2 and 4:2:0 formats. */
width = round_down ( width , info - > hsub ) ;
height = round_down ( height , info - > vsub ) ;
/* Clamp the width and height. */
pix - > width = clamp ( width , VSP1_VIDEO_MIN_WIDTH , VSP1_VIDEO_MAX_WIDTH ) ;
pix - > height = clamp ( height , VSP1_VIDEO_MIN_HEIGHT ,
VSP1_VIDEO_MAX_HEIGHT ) ;
/* Compute and clamp the stride and image size. While not documented in
* the datasheet , strides not aligned to a multiple of 128 bytes result
* in image corruption .
*/
2015-06-19 14:51:22 +03:00
for ( i = 0 ; i < min ( info - > planes , 2U ) ; + + i ) {
2013-06-04 18:22:30 +04:00
unsigned int hsub = i > 0 ? info - > hsub : 1 ;
unsigned int vsub = i > 0 ? info - > vsub : 1 ;
unsigned int align = 128 ;
unsigned int bpl ;
bpl = clamp_t ( unsigned int , pix - > plane_fmt [ i ] . bytesperline ,
pix - > width / hsub * info - > bpp [ i ] / 8 ,
round_down ( 65535U , align ) ) ;
pix - > plane_fmt [ i ] . bytesperline = round_up ( bpl , align ) ;
pix - > plane_fmt [ i ] . sizeimage = pix - > plane_fmt [ i ] . bytesperline
* pix - > height / vsub ;
}
if ( info - > planes = = 3 ) {
/* The second and third planes must have the same stride. */
pix - > plane_fmt [ 2 ] . bytesperline = pix - > plane_fmt [ 1 ] . bytesperline ;
pix - > plane_fmt [ 2 ] . sizeimage = pix - > plane_fmt [ 1 ] . sizeimage ;
}
pix - > num_planes = info - > planes ;
if ( fmtinfo )
* fmtinfo = info ;
return 0 ;
}
/* -----------------------------------------------------------------------------
* Pipeline Management
*/
2016-01-18 00:55:18 +03:00
/*
* vsp1_video_complete_buffer - Complete the current buffer
* @ video : the video node
*
* This function completes the current buffer by filling its sequence number ,
* time stamp and payload size , and hands it back to the videobuf core .
*
* When operating in DU output mode ( deep pipeline to the DU through the LIF ) ,
* the VSP1 needs to constantly supply frames to the display . In that case , if
* no other buffer is queued , reuse the one that has just been processed instead
* of handing it back to the videobuf core .
*
* Return the next queued buffer or NULL if the queue is empty .
*/
static struct vsp1_vb2_buffer *
vsp1_video_complete_buffer ( struct vsp1_video * video )
{
struct vsp1_pipeline * pipe = video - > rwpf - > pipe ;
struct vsp1_vb2_buffer * next = NULL ;
struct vsp1_vb2_buffer * done ;
unsigned long flags ;
unsigned int i ;
spin_lock_irqsave ( & video - > irqlock , flags ) ;
if ( list_empty ( & video - > irqqueue ) ) {
spin_unlock_irqrestore ( & video - > irqlock , flags ) ;
return NULL ;
}
done = list_first_entry ( & video - > irqqueue ,
struct vsp1_vb2_buffer , queue ) ;
/* In DU output mode reuse the buffer if the list is singular. */
if ( pipe - > lif & & list_is_singular ( & video - > irqqueue ) ) {
spin_unlock_irqrestore ( & video - > irqlock , flags ) ;
return done ;
}
list_del ( & done - > queue ) ;
if ( ! list_empty ( & video - > irqqueue ) )
next = list_first_entry ( & video - > irqqueue ,
struct vsp1_vb2_buffer , queue ) ;
spin_unlock_irqrestore ( & video - > irqlock , flags ) ;
done - > buf . sequence = video - > sequence + + ;
done - > buf . vb2_buf . timestamp = ktime_get_ns ( ) ;
for ( i = 0 ; i < done - > buf . vb2_buf . num_planes ; + + i )
vb2_set_plane_payload ( & done - > buf . vb2_buf , i ,
vb2_plane_size ( & done - > buf . vb2_buf , i ) ) ;
vb2_buffer_done ( & done - > buf . vb2_buf , VB2_BUF_STATE_DONE ) ;
return next ;
}
static void vsp1_video_frame_end ( struct vsp1_pipeline * pipe ,
struct vsp1_rwpf * rwpf )
{
struct vsp1_video * video = rwpf - > video ;
struct vsp1_vb2_buffer * buf ;
unsigned long flags ;
buf = vsp1_video_complete_buffer ( video ) ;
if ( buf = = NULL )
return ;
spin_lock_irqsave ( & pipe - > irqlock , flags ) ;
video - > rwpf - > mem = buf - > mem ;
pipe - > buffers_ready | = 1 < < video - > pipe_index ;
spin_unlock_irqrestore ( & pipe - > irqlock , flags ) ;
}
static void vsp1_video_pipeline_run ( struct vsp1_pipeline * pipe )
{
struct vsp1_device * vsp1 = pipe - > output - > entity . vsp1 ;
unsigned int i ;
if ( ! pipe - > dl )
pipe - > dl = vsp1_dl_list_get ( pipe - > output - > dlm ) ;
for ( i = 0 ; i < vsp1 - > info - > rpf_count ; + + i ) {
struct vsp1_rwpf * rwpf = pipe - > inputs [ i ] ;
if ( rwpf )
vsp1_rwpf_set_memory ( rwpf , pipe - > dl ) ;
}
if ( ! pipe - > lif )
vsp1_rwpf_set_memory ( pipe - > output , pipe - > dl ) ;
vsp1_dl_list_commit ( pipe - > dl ) ;
pipe - > dl = NULL ;
vsp1_pipeline_run ( pipe ) ;
}
static void vsp1_video_pipeline_frame_end ( struct vsp1_pipeline * pipe )
{
struct vsp1_device * vsp1 = pipe - > output - > entity . vsp1 ;
enum vsp1_pipeline_state state ;
unsigned long flags ;
unsigned int i ;
/* Complete buffers on all video nodes. */
for ( i = 0 ; i < vsp1 - > info - > rpf_count ; + + i ) {
if ( ! pipe - > inputs [ i ] )
continue ;
vsp1_video_frame_end ( pipe , pipe - > inputs [ i ] ) ;
}
vsp1_video_frame_end ( pipe , pipe - > output ) ;
spin_lock_irqsave ( & pipe - > irqlock , flags ) ;
state = pipe - > state ;
pipe - > state = VSP1_PIPELINE_STOPPED ;
/* If a stop has been requested, mark the pipeline as stopped and
* return . Otherwise restart the pipeline if ready .
*/
if ( state = = VSP1_PIPELINE_STOPPING )
wake_up ( & pipe - > wq ) ;
else if ( vsp1_pipeline_ready ( pipe ) )
vsp1_video_pipeline_run ( pipe ) ;
spin_unlock_irqrestore ( & pipe - > irqlock , flags ) ;
}
2016-01-20 00:42:56 +03:00
static int vsp1_video_pipeline_build_branch ( struct vsp1_pipeline * pipe ,
struct vsp1_rwpf * input ,
struct vsp1_rwpf * output )
2013-06-04 18:22:30 +04:00
{
2015-12-16 16:32:31 +03:00
struct media_entity_enum ent_enum ;
2016-02-29 05:28:38 +03:00
struct vsp1_entity * entity ;
2013-06-04 18:22:30 +04:00
struct media_pad * pad ;
2014-05-31 04:45:48 +04:00
bool bru_found = false ;
2016-02-29 05:28:38 +03:00
int ret ;
2013-06-04 18:22:30 +04:00
2016-02-29 05:28:38 +03:00
ret = media_entity_enum_init ( & ent_enum , & input - > entity . vsp1 - > media_dev ) ;
if ( ret < 0 )
return ret ;
2015-12-16 16:32:31 +03:00
2013-06-04 18:22:30 +04:00
pad = media_entity_remote_pad ( & input - > entity . pads [ RWPF_PAD_SOURCE ] ) ;
while ( 1 ) {
2015-12-16 16:32:31 +03:00
if ( pad = = NULL ) {
2016-02-29 05:28:38 +03:00
ret = - EPIPE ;
2015-12-16 16:32:31 +03:00
goto out ;
}
2013-06-04 18:22:30 +04:00
/* We've reached a video node, that shouldn't have happened. */
2015-12-16 16:32:31 +03:00
if ( ! is_media_entity_v4l2_subdev ( pad - > entity ) ) {
2016-02-29 05:28:38 +03:00
ret = - EPIPE ;
2015-12-16 16:32:31 +03:00
goto out ;
}
2013-06-04 18:22:30 +04:00
2015-12-16 16:32:31 +03:00
entity = to_vsp1_entity (
media_entity_to_v4l2_subdev ( pad - > entity ) ) ;
2013-06-04 18:22:30 +04:00
2015-11-16 00:14:22 +03:00
/* A BRU is present in the pipeline, store the BRU input pad
* number in the input RPF for use when configuring the RPF .
2013-07-11 01:03:46 +04:00
*/
if ( entity - > type = = VSP1_ENTITY_BRU ) {
struct vsp1_bru * bru = to_bru ( & entity - > subdev ) ;
2014-05-28 03:35:36 +04:00
bru - > inputs [ pad - > index ] . rpf = input ;
2015-11-16 00:14:22 +03:00
input - > bru_input = pad - > index ;
2014-05-31 04:45:48 +04:00
bru_found = true ;
2013-07-11 01:03:46 +04:00
}
2013-06-04 18:22:30 +04:00
/* We've reached the WPF, we're done. */
if ( entity - > type = = VSP1_ENTITY_WPF )
break ;
/* Ensure the branch has no loop. */
2015-12-16 16:32:31 +03:00
if ( media_entity_enum_test_and_set ( & ent_enum ,
& entity - > subdev . entity ) ) {
2016-02-29 05:28:38 +03:00
ret = - EPIPE ;
2015-12-16 16:32:31 +03:00
goto out ;
}
2013-06-04 18:22:30 +04:00
/* UDS can't be chained. */
if ( entity - > type = = VSP1_ENTITY_UDS ) {
2015-12-16 16:32:31 +03:00
if ( pipe - > uds ) {
2016-02-29 05:28:38 +03:00
ret = - EPIPE ;
2015-12-16 16:32:31 +03:00
goto out ;
}
2014-05-31 04:45:48 +04:00
pipe - > uds = entity ;
pipe - > uds_input = bru_found ? pipe - > bru
: & input - > entity ;
2013-06-04 18:22:30 +04:00
}
/* Follow the source link. The link setup operations ensure
* that the output fan - out can ' t be more than one , there is thus
* no need to verify here that only a single source link is
* activated .
*/
pad = & entity - > pads [ entity - > source_pad ] ;
pad = media_entity_remote_pad ( pad ) ;
}
/* The last entity must be the output WPF. */
if ( entity ! = & output - > entity )
2016-02-29 05:28:38 +03:00
ret = - EPIPE ;
2013-06-04 18:22:30 +04:00
2015-12-16 16:32:31 +03:00
out :
media_entity_enum_cleanup ( & ent_enum ) ;
2016-02-29 05:28:38 +03:00
return ret ;
2013-06-04 18:22:30 +04:00
}
2016-01-20 00:42:56 +03:00
static int vsp1_video_pipeline_build ( struct vsp1_pipeline * pipe ,
struct vsp1_video * video )
2013-06-04 18:22:30 +04:00
{
struct media_entity_graph graph ;
struct media_entity * entity = & video - > video . entity ;
2015-08-19 18:35:21 +03:00
struct media_device * mdev = entity - > graph_obj . mdev ;
2013-06-04 18:22:30 +04:00
unsigned int i ;
int ret ;
mutex_lock ( & mdev - > graph_mutex ) ;
/* Walk the graph to locate the entities and video nodes. */
2015-12-16 16:32:27 +03:00
ret = media_entity_graph_walk_init ( & graph , mdev ) ;
if ( ret ) {
mutex_unlock ( & mdev - > graph_mutex ) ;
return ret ;
}
2013-06-04 18:22:30 +04:00
media_entity_graph_walk_start ( & graph , entity ) ;
while ( ( entity = media_entity_graph_walk_next ( & graph ) ) ) {
struct v4l2_subdev * subdev ;
struct vsp1_rwpf * rwpf ;
struct vsp1_entity * e ;
2016-03-01 12:27:10 +03:00
if ( ! is_media_entity_v4l2_subdev ( entity ) )
2013-06-04 18:22:30 +04:00
continue ;
subdev = media_entity_to_v4l2_subdev ( entity ) ;
e = to_vsp1_entity ( subdev ) ;
list_add_tail ( & e - > list_pipe , & pipe - > entities ) ;
if ( e - > type = = VSP1_ENTITY_RPF ) {
rwpf = to_rwpf ( subdev ) ;
2015-08-05 22:40:31 +03:00
pipe - > inputs [ rwpf - > entity . index ] = rwpf ;
rwpf - > video - > pipe_index = + + pipe - > num_inputs ;
2016-01-20 00:16:36 +03:00
rwpf - > pipe = pipe ;
2013-06-04 18:22:30 +04:00
} else if ( e - > type = = VSP1_ENTITY_WPF ) {
rwpf = to_rwpf ( subdev ) ;
2015-08-02 23:43:36 +03:00
pipe - > output = rwpf ;
2015-08-02 20:58:43 +03:00
rwpf - > video - > pipe_index = 0 ;
2016-01-20 00:16:36 +03:00
rwpf - > pipe = pipe ;
2013-06-04 18:22:30 +04:00
} else if ( e - > type = = VSP1_ENTITY_LIF ) {
pipe - > lif = e ;
2013-07-11 01:03:46 +04:00
} else if ( e - > type = = VSP1_ENTITY_BRU ) {
pipe - > bru = e ;
2013-06-04 18:22:30 +04:00
}
}
mutex_unlock ( & mdev - > graph_mutex ) ;
2015-12-16 16:32:27 +03:00
media_entity_graph_walk_cleanup ( & graph ) ;
2013-06-04 18:22:30 +04:00
/* We need one output and at least one input. */
if ( pipe - > num_inputs = = 0 | | ! pipe - > output ) {
ret = - EPIPE ;
goto error ;
}
/* Follow links downstream for each input and make sure the graph
* contains no loop and that all branches end at the output WPF .
*/
2015-12-06 01:17:10 +03:00
for ( i = 0 ; i < video - > vsp1 - > info - > rpf_count ; + + i ) {
2015-08-05 22:40:31 +03:00
if ( ! pipe - > inputs [ i ] )
continue ;
2016-01-20 00:42:56 +03:00
ret = vsp1_video_pipeline_build_branch ( pipe , pipe - > inputs [ i ] ,
pipe - > output ) ;
2013-06-04 18:22:30 +04:00
if ( ret < 0 )
goto error ;
}
return 0 ;
error :
2015-08-02 20:15:23 +03:00
vsp1_pipeline_reset ( pipe ) ;
2013-06-04 18:22:30 +04:00
return ret ;
}
2015-08-02 23:29:03 +03:00
static int vsp1_video_pipeline_init ( struct vsp1_pipeline * pipe ,
struct vsp1_video * video )
2013-06-04 18:22:30 +04:00
{
int ret ;
mutex_lock ( & pipe - > lock ) ;
2016-01-20 00:42:56 +03:00
/* If we're the first user build and validate the pipeline. */
2013-06-04 18:22:30 +04:00
if ( pipe - > use_count = = 0 ) {
2016-01-20 00:42:56 +03:00
ret = vsp1_video_pipeline_build ( pipe , video ) ;
2013-06-04 18:22:30 +04:00
if ( ret < 0 )
goto done ;
}
pipe - > use_count + + ;
ret = 0 ;
done :
mutex_unlock ( & pipe - > lock ) ;
return ret ;
}
2015-08-02 23:29:03 +03:00
static void vsp1_video_pipeline_cleanup ( struct vsp1_pipeline * pipe )
2013-06-04 18:22:30 +04:00
{
mutex_lock ( & pipe - > lock ) ;
/* If we're the last user clean up the pipeline. */
2014-05-28 03:35:36 +04:00
if ( - - pipe - > use_count = = 0 )
2015-08-02 20:15:23 +03:00
vsp1_pipeline_reset ( pipe ) ;
2013-06-04 18:22:30 +04:00
mutex_unlock ( & pipe - > lock ) ;
}
/* -----------------------------------------------------------------------------
* videobuf2 Queue Operations
*/
static int
2015-10-28 05:50:37 +03:00
vsp1_video_queue_setup ( struct vb2_queue * vq ,
2013-06-04 18:22:30 +04:00
unsigned int * nbuffers , unsigned int * nplanes ,
unsigned int sizes [ ] , void * alloc_ctxs [ ] )
{
struct vsp1_video * video = vb2_get_drv_priv ( vq ) ;
2015-07-28 20:00:43 +03:00
const struct v4l2_pix_format_mplane * format = & video - > rwpf - > format ;
2013-06-04 18:22:30 +04:00
unsigned int i ;
2015-10-28 05:50:37 +03:00
if ( * nplanes ) {
if ( * nplanes ! = format - > num_planes )
2013-06-04 18:22:30 +04:00
return - EINVAL ;
2015-10-28 05:50:37 +03:00
for ( i = 0 ; i < * nplanes ; i + + ) {
if ( sizes [ i ] < format - > plane_fmt [ i ] . sizeimage )
return - EINVAL ;
alloc_ctxs [ i ] = video - > alloc_ctx ;
}
return 0 ;
2013-06-04 18:22:30 +04:00
}
* nplanes = format - > num_planes ;
for ( i = 0 ; i < format - > num_planes ; + + i ) {
sizes [ i ] = format - > plane_fmt [ i ] . sizeimage ;
alloc_ctxs [ i ] = video - > alloc_ctx ;
}
return 0 ;
}
static int vsp1_video_buffer_prepare ( struct vb2_buffer * vb )
{
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 16:30:30 +03:00
struct vb2_v4l2_buffer * vbuf = to_vb2_v4l2_buffer ( vb ) ;
2013-06-04 18:22:30 +04:00
struct vsp1_video * video = vb2_get_drv_priv ( vb - > vb2_queue ) ;
2015-07-28 20:17:07 +03:00
struct vsp1_vb2_buffer * buf = to_vsp1_vb2_buffer ( vbuf ) ;
2015-07-28 20:00:43 +03:00
const struct v4l2_pix_format_mplane * format = & video - > rwpf - > format ;
2013-06-04 18:22:30 +04:00
unsigned int i ;
if ( vb - > num_planes < format - > num_planes )
return - EINVAL ;
for ( i = 0 ; i < vb - > num_planes ; + + i ) {
2015-07-28 22:04:47 +03:00
buf - > mem . addr [ i ] = vb2_dma_contig_plane_dma_addr ( vb , i ) ;
2013-06-04 18:22:30 +04:00
2015-11-01 20:18:56 +03:00
if ( vb2_plane_size ( vb , i ) < format - > plane_fmt [ i ] . sizeimage )
2013-06-04 18:22:30 +04:00
return - EINVAL ;
}
2015-11-01 20:18:56 +03:00
for ( ; i < 3 ; + + i )
2015-11-01 18:48:11 +03:00
buf - > mem . addr [ i ] = 0 ;
2013-06-04 18:22:30 +04:00
return 0 ;
}
static void vsp1_video_buffer_queue ( struct vb2_buffer * vb )
{
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 16:30:30 +03:00
struct vb2_v4l2_buffer * vbuf = to_vb2_v4l2_buffer ( vb ) ;
2013-06-04 18:22:30 +04:00
struct vsp1_video * video = vb2_get_drv_priv ( vb - > vb2_queue ) ;
2016-01-20 00:16:36 +03:00
struct vsp1_pipeline * pipe = video - > rwpf - > pipe ;
2015-07-28 20:17:07 +03:00
struct vsp1_vb2_buffer * buf = to_vsp1_vb2_buffer ( vbuf ) ;
2013-06-04 18:22:30 +04:00
unsigned long flags ;
bool empty ;
spin_lock_irqsave ( & video - > irqlock , flags ) ;
empty = list_empty ( & video - > irqqueue ) ;
list_add_tail ( & buf - > queue , & video - > irqqueue ) ;
spin_unlock_irqrestore ( & video - > irqlock , flags ) ;
if ( ! empty )
return ;
spin_lock_irqsave ( & pipe - > irqlock , flags ) ;
2015-11-01 20:18:56 +03:00
video - > rwpf - > mem = buf - > mem ;
2013-06-04 18:22:30 +04:00
pipe - > buffers_ready | = 1 < < video - > pipe_index ;
if ( vb2_is_streaming ( & video - > queue ) & &
vsp1_pipeline_ready ( pipe ) )
2015-11-01 20:18:56 +03:00
vsp1_video_pipeline_run ( pipe ) ;
2013-06-04 18:22:30 +04:00
spin_unlock_irqrestore ( & pipe - > irqlock , flags ) ;
}
2015-11-01 20:18:56 +03:00
static int vsp1_video_setup_pipeline ( struct vsp1_pipeline * pipe )
{
struct vsp1_entity * entity ;
/* Prepare the display list. */
pipe - > dl = vsp1_dl_list_get ( pipe - > output - > dlm ) ;
if ( ! pipe - > dl )
return - ENOMEM ;
if ( pipe - > uds ) {
struct vsp1_uds * uds = to_uds ( & pipe - > uds - > subdev ) ;
/* If a BRU is present in the pipeline before the UDS, the alpha
* component doesn ' t need to be scaled as the BRU output alpha
* value is fixed to 255. Otherwise we need to scale the alpha
* component only when available at the input RPF .
*/
if ( pipe - > uds_input - > type = = VSP1_ENTITY_BRU ) {
uds - > scale_alpha = false ;
} else {
struct vsp1_rwpf * rpf =
to_rwpf ( & pipe - > uds_input - > subdev ) ;
uds - > scale_alpha = rpf - > fmtinfo - > alpha ;
}
}
list_for_each_entry ( entity , & pipe - > entities , list_pipe ) {
2015-11-23 01:29:25 +03:00
vsp1_entity_route_setup ( entity , pipe - > dl ) ;
2015-11-01 20:18:56 +03:00
2015-11-17 18:10:26 +03:00
if ( entity - > ops - > configure )
2016-01-14 19:17:32 +03:00
entity - > ops - > configure ( entity , pipe , pipe - > dl ) ;
2015-11-01 20:18:56 +03:00
}
2015-11-17 18:10:26 +03:00
return 0 ;
2015-11-01 20:18:56 +03:00
}
2013-06-04 18:22:30 +04:00
static int vsp1_video_start_streaming ( struct vb2_queue * vq , unsigned int count )
{
struct vsp1_video * video = vb2_get_drv_priv ( vq ) ;
2016-01-20 00:16:36 +03:00
struct vsp1_pipeline * pipe = video - > rwpf - > pipe ;
2013-06-04 18:22:30 +04:00
unsigned long flags ;
int ret ;
mutex_lock ( & pipe - > lock ) ;
2015-08-02 23:17:15 +03:00
if ( pipe - > stream_count = = pipe - > num_inputs ) {
2015-11-01 20:18:56 +03:00
ret = vsp1_video_setup_pipeline ( pipe ) ;
if ( ret < 0 ) {
mutex_unlock ( & pipe - > lock ) ;
return ret ;
2013-06-04 18:22:30 +04:00
}
}
pipe - > stream_count + + ;
mutex_unlock ( & pipe - > lock ) ;
spin_lock_irqsave ( & pipe - > irqlock , flags ) ;
if ( vsp1_pipeline_ready ( pipe ) )
2015-11-01 20:18:56 +03:00
vsp1_video_pipeline_run ( pipe ) ;
2013-06-04 18:22:30 +04:00
spin_unlock_irqrestore ( & pipe - > irqlock , flags ) ;
return 0 ;
}
2014-04-17 09:47:21 +04:00
static void vsp1_video_stop_streaming ( struct vb2_queue * vq )
2013-06-04 18:22:30 +04:00
{
struct vsp1_video * video = vb2_get_drv_priv ( vq ) ;
2016-01-20 00:16:36 +03:00
struct vsp1_pipeline * pipe = video - > rwpf - > pipe ;
2015-07-28 20:17:07 +03:00
struct vsp1_vb2_buffer * buffer ;
2013-06-04 18:22:30 +04:00
unsigned long flags ;
int ret ;
mutex_lock ( & pipe - > lock ) ;
if ( - - pipe - > stream_count = = 0 ) {
/* Stop the pipeline. */
ret = vsp1_pipeline_stop ( pipe ) ;
if ( ret = = - ETIMEDOUT )
dev_err ( video - > vsp1 - > dev , " pipeline stop timeout \n " ) ;
2015-11-01 20:18:56 +03:00
vsp1_dl_list_put ( pipe - > dl ) ;
pipe - > dl = NULL ;
2013-06-04 18:22:30 +04:00
}
mutex_unlock ( & pipe - > lock ) ;
2015-08-02 23:29:03 +03:00
vsp1_video_pipeline_cleanup ( pipe ) ;
2013-06-04 18:22:30 +04:00
media_entity_pipeline_stop ( & video - > video . entity ) ;
/* Remove all buffers from the IRQ queue. */
spin_lock_irqsave ( & video - > irqlock , flags ) ;
2014-06-23 23:57:22 +04:00
list_for_each_entry ( buffer , & video - > irqqueue , queue )
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 16:30:30 +03:00
vb2_buffer_done ( & buffer - > buf . vb2_buf , VB2_BUF_STATE_ERROR ) ;
2013-06-04 18:22:30 +04:00
INIT_LIST_HEAD ( & video - > irqqueue ) ;
spin_unlock_irqrestore ( & video - > irqlock , flags ) ;
}
static struct vb2_ops vsp1_video_queue_qops = {
. queue_setup = vsp1_video_queue_setup ,
. buf_prepare = vsp1_video_buffer_prepare ,
. buf_queue = vsp1_video_buffer_queue ,
. wait_prepare = vb2_ops_wait_prepare ,
. wait_finish = vb2_ops_wait_finish ,
. start_streaming = vsp1_video_start_streaming ,
. stop_streaming = vsp1_video_stop_streaming ,
} ;
/* -----------------------------------------------------------------------------
* V4L2 ioctls
*/
static int
vsp1_video_querycap ( struct file * file , void * fh , struct v4l2_capability * cap )
{
struct v4l2_fh * vfh = file - > private_data ;
struct vsp1_video * video = to_vsp1_video ( vfh - > vdev ) ;
cap - > capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
| V4L2_CAP_VIDEO_CAPTURE_MPLANE
| V4L2_CAP_VIDEO_OUTPUT_MPLANE ;
if ( video - > type = = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE )
cap - > device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE
| V4L2_CAP_STREAMING ;
else
cap - > device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE
| V4L2_CAP_STREAMING ;
strlcpy ( cap - > driver , " vsp1 " , sizeof ( cap - > driver ) ) ;
strlcpy ( cap - > card , video - > video . name , sizeof ( cap - > card ) ) ;
snprintf ( cap - > bus_info , sizeof ( cap - > bus_info ) , " platform:%s " ,
dev_name ( video - > vsp1 - > dev ) ) ;
return 0 ;
}
static int
vsp1_video_get_format ( struct file * file , void * fh , struct v4l2_format * format )
{
struct v4l2_fh * vfh = file - > private_data ;
struct vsp1_video * video = to_vsp1_video ( vfh - > vdev ) ;
if ( format - > type ! = video - > queue . type )
return - EINVAL ;
mutex_lock ( & video - > lock ) ;
2015-07-28 20:00:43 +03:00
format - > fmt . pix_mp = video - > rwpf - > format ;
2013-06-04 18:22:30 +04:00
mutex_unlock ( & video - > lock ) ;
return 0 ;
}
static int
vsp1_video_try_format ( struct file * file , void * fh , struct v4l2_format * format )
{
struct v4l2_fh * vfh = file - > private_data ;
struct vsp1_video * video = to_vsp1_video ( vfh - > vdev ) ;
if ( format - > type ! = video - > queue . type )
return - EINVAL ;
return __vsp1_video_try_format ( video , & format - > fmt . pix_mp , NULL ) ;
}
static int
vsp1_video_set_format ( struct file * file , void * fh , struct v4l2_format * format )
{
struct v4l2_fh * vfh = file - > private_data ;
struct vsp1_video * video = to_vsp1_video ( vfh - > vdev ) ;
const struct vsp1_format_info * info ;
int ret ;
if ( format - > type ! = video - > queue . type )
return - EINVAL ;
ret = __vsp1_video_try_format ( video , & format - > fmt . pix_mp , & info ) ;
if ( ret < 0 )
return ret ;
mutex_lock ( & video - > lock ) ;
if ( vb2_is_busy ( & video - > queue ) ) {
ret = - EBUSY ;
goto done ;
}
2015-07-28 20:00:43 +03:00
video - > rwpf - > format = format - > fmt . pix_mp ;
video - > rwpf - > fmtinfo = info ;
2013-06-04 18:22:30 +04:00
done :
mutex_unlock ( & video - > lock ) ;
return ret ;
}
static int
vsp1_video_streamon ( struct file * file , void * fh , enum v4l2_buf_type type )
{
struct v4l2_fh * vfh = file - > private_data ;
struct vsp1_video * video = to_vsp1_video ( vfh - > vdev ) ;
struct vsp1_pipeline * pipe ;
int ret ;
if ( video - > queue . owner & & video - > queue . owner ! = file - > private_data )
return - EBUSY ;
video - > sequence = 0 ;
/* Start streaming on the pipeline. No link touching an entity in the
* pipeline can be activated or deactivated once streaming is started .
*
* Use the VSP1 pipeline object embedded in the first video object that
* starts streaming .
2015-11-05 15:28:56 +03:00
*
* FIXME : This is racy , the ioctl is only protected by the video node
* lock .
2013-06-04 18:22:30 +04:00
*/
2016-01-20 00:16:36 +03:00
pipe = video - > rwpf - > pipe ? video - > rwpf - > pipe : & video - > pipe ;
2013-06-04 18:22:30 +04:00
ret = media_entity_pipeline_start ( & video - > video . entity , & pipe - > pipe ) ;
if ( ret < 0 )
return ret ;
/* Verify that the configured format matches the output of the connected
* subdev .
*/
ret = vsp1_video_verify_format ( video ) ;
if ( ret < 0 )
goto err_stop ;
2015-08-02 23:29:03 +03:00
ret = vsp1_video_pipeline_init ( pipe , video ) ;
2013-06-04 18:22:30 +04:00
if ( ret < 0 )
goto err_stop ;
/* Start the queue. */
ret = vb2_streamon ( & video - > queue , type ) ;
if ( ret < 0 )
goto err_cleanup ;
return 0 ;
err_cleanup :
2015-08-02 23:29:03 +03:00
vsp1_video_pipeline_cleanup ( pipe ) ;
2013-06-04 18:22:30 +04:00
err_stop :
media_entity_pipeline_stop ( & video - > video . entity ) ;
return ret ;
}
static const struct v4l2_ioctl_ops vsp1_video_ioctl_ops = {
. vidioc_querycap = vsp1_video_querycap ,
. vidioc_g_fmt_vid_cap_mplane = vsp1_video_get_format ,
. vidioc_s_fmt_vid_cap_mplane = vsp1_video_set_format ,
. vidioc_try_fmt_vid_cap_mplane = vsp1_video_try_format ,
. vidioc_g_fmt_vid_out_mplane = vsp1_video_get_format ,
. vidioc_s_fmt_vid_out_mplane = vsp1_video_set_format ,
. vidioc_try_fmt_vid_out_mplane = vsp1_video_try_format ,
. vidioc_reqbufs = vb2_ioctl_reqbufs ,
. vidioc_querybuf = vb2_ioctl_querybuf ,
. vidioc_qbuf = vb2_ioctl_qbuf ,
. vidioc_dqbuf = vb2_ioctl_dqbuf ,
. vidioc_create_bufs = vb2_ioctl_create_bufs ,
. vidioc_prepare_buf = vb2_ioctl_prepare_buf ,
. vidioc_streamon = vsp1_video_streamon ,
. vidioc_streamoff = vb2_ioctl_streamoff ,
} ;
/* -----------------------------------------------------------------------------
* V4L2 File Operations
*/
static int vsp1_video_open ( struct file * file )
{
struct vsp1_video * video = video_drvdata ( file ) ;
struct v4l2_fh * vfh ;
int ret = 0 ;
vfh = kzalloc ( sizeof ( * vfh ) , GFP_KERNEL ) ;
if ( vfh = = NULL )
return - ENOMEM ;
v4l2_fh_init ( vfh , & video - > video ) ;
v4l2_fh_add ( vfh ) ;
file - > private_data = vfh ;
2014-05-31 15:50:32 +04:00
ret = vsp1_device_get ( video - > vsp1 ) ;
if ( ret < 0 ) {
2013-06-04 18:22:30 +04:00
v4l2_fh_del ( vfh ) ;
kfree ( vfh ) ;
}
return ret ;
}
static int vsp1_video_release ( struct file * file )
{
struct vsp1_video * video = video_drvdata ( file ) ;
struct v4l2_fh * vfh = file - > private_data ;
mutex_lock ( & video - > lock ) ;
if ( video - > queue . owner = = vfh ) {
vb2_queue_release ( & video - > queue ) ;
video - > queue . owner = NULL ;
}
mutex_unlock ( & video - > lock ) ;
vsp1_device_put ( video - > vsp1 ) ;
v4l2_fh_release ( file ) ;
file - > private_data = NULL ;
return 0 ;
}
static struct v4l2_file_operations vsp1_video_fops = {
. owner = THIS_MODULE ,
. unlocked_ioctl = video_ioctl2 ,
. open = vsp1_video_open ,
. release = vsp1_video_release ,
. poll = vb2_fop_poll ,
. mmap = vb2_fop_mmap ,
} ;
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
2015-07-28 21:46:00 +03:00
struct vsp1_video * vsp1_video_create ( struct vsp1_device * vsp1 ,
struct vsp1_rwpf * rwpf )
2013-06-04 18:22:30 +04:00
{
2015-07-28 21:46:00 +03:00
struct vsp1_video * video ;
2013-06-04 18:22:30 +04:00
const char * direction ;
int ret ;
2015-07-28 21:46:00 +03:00
video = devm_kzalloc ( vsp1 - > dev , sizeof ( * video ) , GFP_KERNEL ) ;
if ( ! video )
return ERR_PTR ( - ENOMEM ) ;
2013-06-04 18:22:30 +04:00
2015-08-02 20:58:43 +03:00
rwpf - > video = video ;
2015-07-28 21:46:00 +03:00
video - > vsp1 = vsp1 ;
video - > rwpf = rwpf ;
if ( rwpf - > entity . type = = VSP1_ENTITY_RPF ) {
2013-06-04 18:22:30 +04:00
direction = " input " ;
2015-07-28 21:46:00 +03:00
video - > type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ;
2013-06-04 18:22:30 +04:00
video - > pad . flags = MEDIA_PAD_FL_SOURCE ;
video - > video . vfl_dir = VFL_DIR_TX ;
2015-07-28 21:46:00 +03:00
} else {
direction = " output " ;
video - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ;
video - > pad . flags = MEDIA_PAD_FL_SINK ;
video - > video . vfl_dir = VFL_DIR_RX ;
2013-06-04 18:22:30 +04:00
}
mutex_init ( & video - > lock ) ;
spin_lock_init ( & video - > irqlock ) ;
INIT_LIST_HEAD ( & video - > irqqueue ) ;
2015-08-02 23:32:13 +03:00
vsp1_pipeline_init ( & video - > pipe ) ;
2015-08-02 22:37:11 +03:00
video - > pipe . frame_end = vsp1_video_pipeline_frame_end ;
2013-06-04 18:22:30 +04:00
/* Initialize the media entity... */
2015-12-11 12:44:40 +03:00
ret = media_entity_pads_init ( & video - > video . entity , 1 , & video - > pad ) ;
2013-06-04 18:22:30 +04:00
if ( ret < 0 )
2015-07-28 21:46:00 +03:00
return ERR_PTR ( ret ) ;
2013-06-04 18:22:30 +04:00
/* ... and the format ... */
2015-11-22 19:08:18 +03:00
rwpf - > format . pixelformat = VSP1_VIDEO_DEF_FORMAT ;
2015-07-28 20:00:43 +03:00
rwpf - > format . width = VSP1_VIDEO_DEF_WIDTH ;
rwpf - > format . height = VSP1_VIDEO_DEF_HEIGHT ;
2015-11-22 19:08:18 +03:00
__vsp1_video_try_format ( video , & rwpf - > format , & rwpf - > fmtinfo ) ;
2013-06-04 18:22:30 +04:00
/* ... and the video node... */
video - > video . v4l2_dev = & video - > vsp1 - > v4l2_dev ;
video - > video . fops = & vsp1_video_fops ;
snprintf ( video - > video . name , sizeof ( video - > video . name ) , " %s %s " ,
2015-07-28 19:54:03 +03:00
rwpf - > entity . subdev . name , direction ) ;
2013-06-04 18:22:30 +04:00
video - > video . vfl_type = VFL_TYPE_GRABBER ;
video - > video . release = video_device_release_empty ;
video - > video . ioctl_ops = & vsp1_video_ioctl_ops ;
video_set_drvdata ( & video - > video , video ) ;
/* ... and the buffers queue... */
video - > alloc_ctx = vb2_dma_contig_init_ctx ( video - > vsp1 - > dev ) ;
2013-09-11 18:10:24 +04:00
if ( IS_ERR ( video - > alloc_ctx ) ) {
ret = PTR_ERR ( video - > alloc_ctx ) ;
2013-06-04 18:22:30 +04:00
goto error ;
2013-09-11 18:10:24 +04:00
}
2013-06-04 18:22:30 +04:00
video - > queue . type = video - > type ;
video - > queue . io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF ;
video - > queue . lock = & video - > lock ;
video - > queue . drv_priv = video ;
2015-07-28 20:17:07 +03:00
video - > queue . buf_struct_size = sizeof ( struct vsp1_vb2_buffer ) ;
2013-06-04 18:22:30 +04:00
video - > queue . ops = & vsp1_video_queue_qops ;
video - > queue . mem_ops = & vb2_dma_contig_memops ;
2014-02-26 02:12:19 +04:00
video - > queue . timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY ;
2013-06-04 18:22:30 +04:00
ret = vb2_queue_init ( & video - > queue ) ;
if ( ret < 0 ) {
dev_err ( video - > vsp1 - > dev , " failed to initialize vb2 queue \n " ) ;
goto error ;
}
/* ... and register the video device. */
video - > video . queue = & video - > queue ;
ret = video_register_device ( & video - > video , VFL_TYPE_GRABBER , - 1 ) ;
if ( ret < 0 ) {
dev_err ( video - > vsp1 - > dev , " failed to register video device \n " ) ;
goto error ;
}
2015-07-28 21:46:00 +03:00
return video ;
2013-06-04 18:22:30 +04:00
error :
vb2_dma_contig_cleanup_ctx ( video - > alloc_ctx ) ;
vsp1_video_cleanup ( video ) ;
2015-07-28 21:46:00 +03:00
return ERR_PTR ( ret ) ;
2013-06-04 18:22:30 +04:00
}
void vsp1_video_cleanup ( struct vsp1_video * video )
{
if ( video_is_registered ( & video - > video ) )
video_unregister_device ( & video - > video ) ;
vb2_dma_contig_cleanup_ctx ( video - > alloc_ctx ) ;
media_entity_cleanup ( & video - > video . entity ) ;
}