2018-07-25 22:36:55 -04:00
// SPDX-License-Identifier: GPL-2.0
2010-03-17 11:21:13 -03:00
/*
* SuperH Video Output Unit ( VOU ) driver
*
* Copyright ( C ) 2010 , Guennadi Liakhovetski < g . liakhovetski @ gmx . de >
*/
# include <linux/dma-mapping.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/i2c.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
2010-07-08 05:12:11 -03:00
# include <linux/slab.h>
2010-03-17 11:21:13 -03:00
# include <linux/videodev2.h>
2011-07-03 14:03:12 -04:00
# include <linux/module.h>
2010-03-17 11:21:13 -03:00
2015-11-13 19:40:07 -02:00
# include <media/drv-intf/sh_vou.h>
2010-03-17 11:21:13 -03:00
# include <media/v4l2-common.h>
# include <media/v4l2-device.h>
# include <media/v4l2-ioctl.h>
# include <media/v4l2-mediabus.h>
[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 10:30:30 -03:00
# include <media/videobuf2-v4l2.h>
2015-06-07 05:58:05 -03:00
# include <media/videobuf2-dma-contig.h>
2010-03-17 11:21:13 -03:00
/* Mirror addresses are not available for all registers */
# define VOUER 0
# define VOUCR 4
# define VOUSTR 8
# define VOUVCR 0xc
# define VOUISR 0x10
# define VOUBCR 0x14
# define VOUDPR 0x18
# define VOUDSR 0x1c
# define VOUVPR 0x20
# define VOUIR 0x24
# define VOUSRR 0x28
# define VOUMSR 0x2c
# define VOUHIR 0x30
# define VOUDFR 0x34
# define VOUAD1R 0x38
# define VOUAD2R 0x3c
# define VOUAIR 0x40
# define VOUSWR 0x44
# define VOURCR 0x48
# define VOURPR 0x50
enum sh_vou_status {
SH_VOU_IDLE ,
SH_VOU_INITIALISING ,
SH_VOU_RUNNING ,
} ;
2015-06-07 05:58:05 -03:00
# define VOU_MIN_IMAGE_WIDTH 16
2010-03-17 11:21:13 -03:00
# define VOU_MAX_IMAGE_WIDTH 720
2015-06-07 05:58:05 -03:00
# define VOU_MIN_IMAGE_HEIGHT 16
struct sh_vou_buffer {
[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 10:30:30 -03:00
struct vb2_v4l2_buffer vb ;
2015-06-07 05:58:05 -03:00
struct list_head list ;
} ;
[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 10:30:30 -03:00
static inline struct
sh_vou_buffer * to_sh_vou_buffer ( struct vb2_v4l2_buffer * vb2 )
2015-06-07 05:58:05 -03:00
{
return container_of ( vb2 , struct sh_vou_buffer , vb ) ;
}
2010-03-17 11:21:13 -03:00
struct sh_vou_device {
struct v4l2_device v4l2_dev ;
2015-03-09 19:10:51 -03:00
struct video_device vdev ;
2010-03-17 11:21:13 -03:00
struct sh_vou_pdata * pdata ;
spinlock_t lock ;
void __iomem * base ;
/* State information */
struct v4l2_pix_format pix ;
struct v4l2_rect rect ;
2015-06-07 05:58:05 -03:00
struct list_head buf_list ;
2010-03-17 11:21:13 -03:00
v4l2_std_id std ;
int pix_idx ;
2015-06-07 05:58:05 -03:00
struct vb2_queue queue ;
struct sh_vou_buffer * active ;
2010-03-17 11:21:13 -03:00
enum sh_vou_status status ;
2015-06-07 05:58:05 -03:00
unsigned sequence ;
2010-11-16 18:12:16 -03:00
struct mutex fop_lock ;
2010-03-17 11:21:13 -03:00
} ;
/* Register access routines for sides A, B and mirror addresses */
static void sh_vou_reg_a_write ( struct sh_vou_device * vou_dev , unsigned int reg ,
u32 value )
{
__raw_writel ( value , vou_dev - > base + reg ) ;
}
static void sh_vou_reg_ab_write ( struct sh_vou_device * vou_dev , unsigned int reg ,
u32 value )
{
__raw_writel ( value , vou_dev - > base + reg ) ;
__raw_writel ( value , vou_dev - > base + reg + 0x1000 ) ;
}
static void sh_vou_reg_m_write ( struct sh_vou_device * vou_dev , unsigned int reg ,
u32 value )
{
__raw_writel ( value , vou_dev - > base + reg + 0x2000 ) ;
}
static u32 sh_vou_reg_a_read ( struct sh_vou_device * vou_dev , unsigned int reg )
{
return __raw_readl ( vou_dev - > base + reg ) ;
}
static void sh_vou_reg_a_set ( struct sh_vou_device * vou_dev , unsigned int reg ,
u32 value , u32 mask )
{
u32 old = __raw_readl ( vou_dev - > base + reg ) ;
value = ( value & mask ) | ( old & ~ mask ) ;
__raw_writel ( value , vou_dev - > base + reg ) ;
}
static void sh_vou_reg_b_set ( struct sh_vou_device * vou_dev , unsigned int reg ,
u32 value , u32 mask )
{
sh_vou_reg_a_set ( vou_dev , reg + 0x1000 , value , mask ) ;
}
static void sh_vou_reg_ab_set ( struct sh_vou_device * vou_dev , unsigned int reg ,
u32 value , u32 mask )
{
sh_vou_reg_a_set ( vou_dev , reg , value , mask ) ;
sh_vou_reg_b_set ( vou_dev , reg , value , mask ) ;
}
struct sh_vou_fmt {
u32 pfmt ;
unsigned char bpp ;
2015-06-07 05:58:04 -03:00
unsigned char bpl ;
2010-03-17 11:21:13 -03:00
unsigned char rgb ;
unsigned char yf ;
unsigned char pkf ;
} ;
/* Further pixel formats can be added */
static struct sh_vou_fmt vou_fmt [ ] = {
{
. pfmt = V4L2_PIX_FMT_NV12 ,
. bpp = 12 ,
2015-06-07 05:58:04 -03:00
. bpl = 1 ,
2010-03-17 11:21:13 -03:00
. yf = 0 ,
. rgb = 0 ,
} ,
{
. pfmt = V4L2_PIX_FMT_NV16 ,
. bpp = 16 ,
2015-06-07 05:58:04 -03:00
. bpl = 1 ,
2010-03-17 11:21:13 -03:00
. yf = 1 ,
. rgb = 0 ,
} ,
{
. pfmt = V4L2_PIX_FMT_RGB24 ,
. bpp = 24 ,
2015-06-07 05:58:04 -03:00
. bpl = 3 ,
2010-03-17 11:21:13 -03:00
. pkf = 2 ,
. rgb = 1 ,
} ,
{
. pfmt = V4L2_PIX_FMT_RGB565 ,
. bpp = 16 ,
2015-06-07 05:58:04 -03:00
. bpl = 2 ,
2010-03-17 11:21:13 -03:00
. pkf = 3 ,
. rgb = 1 ,
} ,
{
. pfmt = V4L2_PIX_FMT_RGB565X ,
. bpp = 16 ,
2015-06-07 05:58:04 -03:00
. bpl = 2 ,
2010-03-17 11:21:13 -03:00
. pkf = 3 ,
. rgb = 1 ,
} ,
} ;
static void sh_vou_schedule_next ( struct sh_vou_device * vou_dev ,
[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 10:30:30 -03:00
struct vb2_v4l2_buffer * vbuf )
2010-03-17 11:21:13 -03:00
{
dma_addr_t addr1 , addr2 ;
[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 10:30:30 -03:00
addr1 = vb2_dma_contig_plane_dma_addr ( & vbuf - > vb2_buf , 0 ) ;
2010-03-17 11:21:13 -03:00
switch ( vou_dev - > pix . pixelformat ) {
case V4L2_PIX_FMT_NV12 :
case V4L2_PIX_FMT_NV16 :
addr2 = addr1 + vou_dev - > pix . width * vou_dev - > pix . height ;
break ;
default :
addr2 = 0 ;
}
sh_vou_reg_m_write ( vou_dev , VOUAD1R , addr1 ) ;
sh_vou_reg_m_write ( vou_dev , VOUAD2R , addr2 ) ;
}
2015-06-07 05:58:05 -03:00
static void sh_vou_stream_config ( struct sh_vou_device * vou_dev )
2010-03-17 11:21:13 -03:00
{
unsigned int row_coeff ;
# ifdef __LITTLE_ENDIAN
u32 dataswap = 7 ;
# else
u32 dataswap = 0 ;
# endif
switch ( vou_dev - > pix . pixelformat ) {
2012-11-26 14:32:48 -03:00
default :
2010-03-17 11:21:13 -03:00
case V4L2_PIX_FMT_NV12 :
case V4L2_PIX_FMT_NV16 :
row_coeff = 1 ;
break ;
case V4L2_PIX_FMT_RGB565 :
dataswap ^ = 1 ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2010-03-17 11:21:13 -03:00
case V4L2_PIX_FMT_RGB565X :
row_coeff = 2 ;
break ;
case V4L2_PIX_FMT_RGB24 :
row_coeff = 3 ;
break ;
}
sh_vou_reg_a_write ( vou_dev , VOUSWR , dataswap ) ;
sh_vou_reg_ab_write ( vou_dev , VOUAIR , vou_dev - > pix . width * row_coeff ) ;
}
2010-11-16 18:12:16 -03:00
/* Locking: caller holds fop_lock mutex */
2015-10-28 00:50:37 -02:00
static int sh_vou_queue_setup ( struct vb2_queue * vq ,
2015-06-07 05:58:05 -03:00
unsigned int * nbuffers , unsigned int * nplanes ,
2016-04-15 09:15:05 -03:00
unsigned int sizes [ ] , struct device * alloc_devs [ ] )
2010-03-17 11:21:13 -03:00
{
2015-06-07 05:58:05 -03:00
struct sh_vou_device * vou_dev = vb2_get_drv_priv ( vq ) ;
struct v4l2_pix_format * pix = & vou_dev - > pix ;
int bytes_per_line = vou_fmt [ vou_dev - > pix_idx ] . bpp * pix - > width / 8 ;
2010-03-17 11:21:13 -03:00
2015-06-07 05:58:05 -03:00
dev_dbg ( vou_dev - > v4l2_dev . dev , " %s() \n " , __func__ ) ;
2010-03-17 11:21:13 -03:00
2015-10-28 00:50:37 -02:00
if ( * nplanes )
return sizes [ 0 ] < pix - > height * bytes_per_line ? - EINVAL : 0 ;
* nplanes = 1 ;
sizes [ 0 ] = pix - > height * bytes_per_line ;
2010-03-17 11:21:13 -03:00
return 0 ;
}
2015-06-07 05:58:05 -03:00
static int sh_vou_buf_prepare ( struct vb2_buffer * vb )
2010-03-17 11:21:13 -03:00
{
2015-06-07 05:58:05 -03:00
struct sh_vou_device * vou_dev = vb2_get_drv_priv ( vb - > vb2_queue ) ;
2010-03-17 11:21:13 -03:00
struct v4l2_pix_format * pix = & vou_dev - > pix ;
2015-06-07 05:58:05 -03:00
unsigned bytes_per_line = vou_fmt [ vou_dev - > pix_idx ] . bpp * pix - > width / 8 ;
unsigned size = pix - > height * bytes_per_line ;
2010-03-17 11:21:13 -03:00
2013-01-03 15:35:57 -03:00
dev_dbg ( vou_dev - > v4l2_dev . dev , " %s() \n " , __func__ ) ;
2010-03-17 11:21:13 -03:00
2015-06-07 05:58:05 -03:00
if ( vb2_plane_size ( vb , 0 ) < size ) {
2010-03-17 11:21:13 -03:00
/* User buffer too small */
2015-06-07 05:58:05 -03:00
dev_warn ( vou_dev - > v4l2_dev . dev , " buffer too small (%lu < %u) \n " ,
vb2_plane_size ( vb , 0 ) , size ) ;
2010-03-17 11:21:13 -03:00
return - EINVAL ;
}
2015-06-07 05:58:05 -03:00
vb2_set_plane_payload ( vb , 0 , size ) ;
2010-03-17 11:21:13 -03:00
return 0 ;
}
2010-11-16 18:12:16 -03:00
/* Locking: caller holds fop_lock mutex and vq->irqlock spinlock */
2015-06-07 05:58:05 -03:00
static void sh_vou_buf_queue ( struct vb2_buffer * vb )
2010-03-17 11:21:13 -03:00
{
[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 10:30:30 -03:00
struct vb2_v4l2_buffer * vbuf = to_vb2_v4l2_buffer ( vb ) ;
2015-06-07 05:58:05 -03:00
struct sh_vou_device * vou_dev = vb2_get_drv_priv ( vb - > vb2_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 10:30:30 -03:00
struct sh_vou_buffer * shbuf = to_sh_vou_buffer ( vbuf ) ;
2015-06-07 05:58:05 -03:00
unsigned long flags ;
2010-03-17 11:21:13 -03:00
2015-06-07 05:58:05 -03:00
spin_lock_irqsave ( & vou_dev - > lock , flags ) ;
list_add_tail ( & shbuf - > list , & vou_dev - > buf_list ) ;
spin_unlock_irqrestore ( & vou_dev - > lock , flags ) ;
}
2010-03-17 11:21:13 -03:00
2015-06-07 05:58:05 -03:00
static int sh_vou_start_streaming ( struct vb2_queue * vq , unsigned int count )
{
struct sh_vou_device * vou_dev = vb2_get_drv_priv ( vq ) ;
struct sh_vou_buffer * buf , * node ;
int ret ;
vou_dev - > sequence = 0 ;
ret = v4l2_device_call_until_err ( & vou_dev - > v4l2_dev , 0 ,
video , s_stream , 1 ) ;
if ( ret < 0 & & ret ! = - ENOIOCTLCMD ) {
list_for_each_entry_safe ( buf , node , & vou_dev - > buf_list , list ) {
[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 10:30:30 -03:00
vb2_buffer_done ( & buf - > vb . vb2_buf ,
VB2_BUF_STATE_QUEUED ) ;
2015-06-07 05:58:05 -03:00
list_del ( & buf - > list ) ;
}
vou_dev - > active = NULL ;
return ret ;
2010-03-17 11:21:13 -03:00
}
2015-06-07 05:58:05 -03:00
buf = list_entry ( vou_dev - > buf_list . next , struct sh_vou_buffer , list ) ;
vou_dev - > active = buf ;
/* Start from side A: we use mirror addresses, so, set B */
sh_vou_reg_a_write ( vou_dev , VOURPR , 1 ) ;
dev_dbg ( vou_dev - > v4l2_dev . dev , " %s: first buffer status 0x%x \n " ,
__func__ , sh_vou_reg_a_read ( vou_dev , VOUSTR ) ) ;
sh_vou_schedule_next ( vou_dev , & buf - > vb ) ;
buf = list_entry ( buf - > list . next , struct sh_vou_buffer , list ) ;
/* Second buffer - initialise register side B */
sh_vou_reg_a_write ( vou_dev , VOURPR , 0 ) ;
sh_vou_schedule_next ( vou_dev , & buf - > vb ) ;
/* Register side switching with frame VSYNC */
sh_vou_reg_a_write ( vou_dev , VOURCR , 5 ) ;
sh_vou_stream_config ( vou_dev ) ;
/* Enable End-of-Frame (VSYNC) interrupts */
sh_vou_reg_a_write ( vou_dev , VOUIR , 0x10004 ) ;
/* Two buffers on the queue - activate the hardware */
vou_dev - > status = SH_VOU_RUNNING ;
sh_vou_reg_a_write ( vou_dev , VOUER , 0x107 ) ;
return 0 ;
2010-03-17 11:21:13 -03:00
}
2015-06-07 05:58:05 -03:00
static void sh_vou_stop_streaming ( struct vb2_queue * vq )
2010-03-17 11:21:13 -03:00
{
2015-06-07 05:58:05 -03:00
struct sh_vou_device * vou_dev = vb2_get_drv_priv ( vq ) ;
struct sh_vou_buffer * buf , * node ;
2010-03-17 11:21:13 -03:00
unsigned long flags ;
2015-06-07 05:58:05 -03:00
v4l2_device_call_until_err ( & vou_dev - > v4l2_dev , 0 ,
video , s_stream , 0 ) ;
/* disable output */
sh_vou_reg_a_set ( vou_dev , VOUER , 0 , 1 ) ;
/* ...but the current frame will complete */
sh_vou_reg_a_set ( vou_dev , VOUIR , 0 , 0x30000 ) ;
msleep ( 50 ) ;
2010-03-17 11:21:13 -03:00
spin_lock_irqsave ( & vou_dev - > lock , flags ) ;
2015-06-07 05:58:05 -03:00
list_for_each_entry_safe ( buf , node , & vou_dev - > buf_list , list ) {
[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 10:30:30 -03:00
vb2_buffer_done ( & buf - > vb . vb2_buf , VB2_BUF_STATE_ERROR ) ;
2015-06-07 05:58:05 -03:00
list_del ( & buf - > list ) ;
2010-03-17 11:21:13 -03:00
}
2015-06-07 05:58:05 -03:00
vou_dev - > active = NULL ;
2010-03-17 11:21:13 -03:00
spin_unlock_irqrestore ( & vou_dev - > lock , flags ) ;
}
2016-09-08 20:59:10 -03:00
static const struct vb2_ops sh_vou_qops = {
2015-06-07 05:58:05 -03:00
. queue_setup = sh_vou_queue_setup ,
. buf_prepare = sh_vou_buf_prepare ,
. buf_queue = sh_vou_buf_queue ,
. start_streaming = sh_vou_start_streaming ,
. stop_streaming = sh_vou_stop_streaming ,
. wait_prepare = vb2_ops_wait_prepare ,
. wait_finish = vb2_ops_wait_finish ,
2010-03-17 11:21:13 -03:00
} ;
/* Video IOCTLs */
static int sh_vou_querycap ( struct file * file , void * priv ,
struct v4l2_capability * cap )
{
2013-01-03 15:35:57 -03:00
struct sh_vou_device * vou_dev = video_drvdata ( file ) ;
2010-03-17 11:21:13 -03:00
2013-01-03 15:35:57 -03:00
dev_dbg ( vou_dev - > v4l2_dev . dev , " %s() \n " , __func__ ) ;
2010-03-17 11:21:13 -03:00
2018-09-10 08:19:14 -04:00
strscpy ( cap - > card , " SuperH VOU " , sizeof ( cap - > card ) ) ;
strscpy ( cap - > driver , " sh-vou " , sizeof ( cap - > driver ) ) ;
strscpy ( cap - > bus_info , " platform:sh-vou " , sizeof ( cap - > bus_info ) ) ;
2010-03-17 11:21:13 -03:00
return 0 ;
}
/* Enumerate formats, that the device can accept from the user */
static int sh_vou_enum_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_fmtdesc * fmt )
{
2013-01-03 15:35:57 -03:00
struct sh_vou_device * vou_dev = video_drvdata ( file ) ;
2010-03-17 11:21:13 -03:00
if ( fmt - > index > = ARRAY_SIZE ( vou_fmt ) )
return - EINVAL ;
2013-01-03 15:35:57 -03:00
dev_dbg ( vou_dev - > v4l2_dev . dev , " %s() \n " , __func__ ) ;
2010-03-17 11:21:13 -03:00
fmt - > pixelformat = vou_fmt [ fmt - > index ] . pfmt ;
return 0 ;
}
static int sh_vou_g_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_format * fmt )
{
2013-01-03 15:35:56 -03:00
struct sh_vou_device * vou_dev = video_drvdata ( file ) ;
2010-03-17 11:21:13 -03:00
dev_dbg ( vou_dev - > v4l2_dev . dev , " %s() \n " , __func__ ) ;
fmt - > type = V4L2_BUF_TYPE_VIDEO_OUTPUT ;
fmt - > fmt . pix = vou_dev - > pix ;
return 0 ;
}
static const unsigned char vou_scale_h_num [ ] = { 1 , 9 , 2 , 9 , 4 } ;
static const unsigned char vou_scale_h_den [ ] = { 1 , 8 , 1 , 4 , 1 } ;
static const unsigned char vou_scale_h_fld [ ] = { 0 , 2 , 1 , 3 } ;
static const unsigned char vou_scale_v_num [ ] = { 1 , 2 , 4 } ;
static const unsigned char vou_scale_v_den [ ] = { 1 , 1 , 1 } ;
static const unsigned char vou_scale_v_fld [ ] = { 0 , 1 } ;
static void sh_vou_configure_geometry ( struct sh_vou_device * vou_dev ,
int pix_idx , int w_idx , int h_idx )
{
struct sh_vou_fmt * fmt = vou_fmt + pix_idx ;
2014-01-13 11:30:26 -02:00
unsigned int black_left , black_top , width_max ,
2010-03-17 11:21:13 -03:00
frame_in_height , frame_out_height , frame_out_top ;
struct v4l2_rect * rect = & vou_dev - > rect ;
struct v4l2_pix_format * pix = & vou_dev - > pix ;
u32 vouvcr = 0 , dsr_h , dsr_v ;
if ( vou_dev - > std & V4L2_STD_525_60 ) {
width_max = 858 ;
2014-01-13 11:30:26 -02:00
/* height_max = 262; */
2010-03-17 11:21:13 -03:00
} else {
width_max = 864 ;
2014-01-13 11:30:26 -02:00
/* height_max = 312; */
2010-03-17 11:21:13 -03:00
}
frame_in_height = pix - > height / 2 ;
frame_out_height = rect - > height / 2 ;
frame_out_top = rect - > top / 2 ;
/*
* Cropping scheme : max useful image is 720 x480 , and the total video
* area is 858 x525 ( NTSC ) or 864 x625 ( PAL ) . AK8813 / 8814 starts
* sampling data beginning with fixed 276 th ( NTSC ) / 288 th ( PAL ) clock ,
* of which the first 33 / 25 clocks HSYNC must be held active . This
* has to be configured in CR [ HW ] . 1 pixel equals 2 clock periods .
* This gives CR [ HW ] = 16 / 12 , VPR [ HVP ] = 138 / 144 , which gives
* exactly 858 - 138 = 864 - 144 = 720 ! We call the out - of - display area ,
* beyond DSR , specified on the left and top by the VPR register " black
* pixels " and out-of-image area (DPR) " background pixels . " We fix VPR
* at 138 / 144 : 20 , because that ' s the HSYNC timing , that our first
* client requires , and that ' s exactly what leaves us 720 pixels for the
* image ; we leave VPR [ VVP ] at default 20 for now , because the client
* doesn ' t seem to have any special requirements for it . Otherwise we
* could also set it to max - 240 = 22 / 72. Thus VPR depends only on
* the selected standard , and DPR and DSR are selected according to
* cropping . Q : how does the client detect the first valid line ? Does
* HSYNC stay inactive during invalid ( black ) lines ?
*/
black_left = width_max - VOU_MAX_IMAGE_WIDTH ;
black_top = 20 ;
dsr_h = rect - > width + rect - > left ;
dsr_v = frame_out_height + frame_out_top ;
dev_dbg ( vou_dev - > v4l2_dev . dev ,
" image %ux%u, black %u:%u, offset %u:%u, display %ux%u \n " ,
pix - > width , frame_in_height , black_left , black_top ,
rect - > left , frame_out_top , dsr_h , dsr_v ) ;
/* VOUISR height - half of a frame height in frame mode */
sh_vou_reg_ab_write ( vou_dev , VOUISR , ( pix - > width < < 16 ) | frame_in_height ) ;
sh_vou_reg_ab_write ( vou_dev , VOUVPR , ( black_left < < 16 ) | black_top ) ;
sh_vou_reg_ab_write ( vou_dev , VOUDPR , ( rect - > left < < 16 ) | frame_out_top ) ;
sh_vou_reg_ab_write ( vou_dev , VOUDSR , ( dsr_h < < 16 ) | dsr_v ) ;
/*
* if necessary , we could set VOUHIR to
* max ( black_left + dsr_h , width_max ) here
*/
if ( w_idx )
vouvcr | = ( 1 < < 15 ) | ( vou_scale_h_fld [ w_idx - 1 ] < < 4 ) ;
if ( h_idx )
vouvcr | = ( 1 < < 14 ) | vou_scale_v_fld [ h_idx - 1 ] ;
2019-06-11 10:25:15 -04:00
dev_dbg ( vou_dev - > v4l2_dev . dev , " 0x%08x: scaling 0x%x \n " ,
fmt - > pfmt , vouvcr ) ;
2010-03-17 11:21:13 -03:00
/* To produce a colour bar for testing set bit 23 of VOUVCR */
sh_vou_reg_ab_write ( vou_dev , VOUVCR , vouvcr ) ;
sh_vou_reg_ab_write ( vou_dev , VOUDFR ,
fmt - > pkf | ( fmt - > yf < < 8 ) | ( fmt - > rgb < < 16 ) ) ;
}
struct sh_vou_geometry {
struct v4l2_rect output ;
unsigned int in_width ;
unsigned int in_height ;
int scale_idx_h ;
int scale_idx_v ;
} ;
/*
* Find input geometry , that we can use to produce output , closest to the
* requested rectangle , using VOU scaling
*/
static void vou_adjust_input ( struct sh_vou_geometry * geo , v4l2_std_id std )
{
/* The compiler cannot know, that best and idx will indeed be set */
2010-07-29 04:11:42 -03:00
unsigned int best_err = UINT_MAX , best = 0 , img_height_max ;
2010-03-17 11:21:13 -03:00
int i , idx = 0 ;
2010-07-29 04:11:42 -03:00
if ( std & V4L2_STD_525_60 )
img_height_max = 480 ;
else
img_height_max = 576 ;
2010-03-17 11:21:13 -03:00
/* Image width must be a multiple of 4 */
2015-06-07 05:58:05 -03:00
v4l_bound_align_image ( & geo - > in_width ,
VOU_MIN_IMAGE_WIDTH , VOU_MAX_IMAGE_WIDTH , 2 ,
& geo - > in_height ,
VOU_MIN_IMAGE_HEIGHT , img_height_max , 1 , 0 ) ;
2010-03-17 11:21:13 -03:00
/* Select scales to come as close as possible to the output image */
for ( i = ARRAY_SIZE ( vou_scale_h_num ) - 1 ; i > = 0 ; i - - ) {
unsigned int err ;
unsigned int found = geo - > output . width * vou_scale_h_den [ i ] /
vou_scale_h_num [ i ] ;
if ( found > VOU_MAX_IMAGE_WIDTH )
/* scales increase */
break ;
err = abs ( found - geo - > in_width ) ;
if ( err < best_err ) {
best_err = err ;
idx = i ;
best = found ;
}
if ( ! err )
break ;
}
geo - > in_width = best ;
geo - > scale_idx_h = idx ;
best_err = UINT_MAX ;
/* This loop can be replaced with one division */
for ( i = ARRAY_SIZE ( vou_scale_v_num ) - 1 ; i > = 0 ; i - - ) {
unsigned int err ;
unsigned int found = geo - > output . height * vou_scale_v_den [ i ] /
vou_scale_v_num [ i ] ;
2010-07-29 04:11:42 -03:00
if ( found > img_height_max )
2010-03-17 11:21:13 -03:00
/* scales increase */
break ;
err = abs ( found - geo - > in_height ) ;
if ( err < best_err ) {
best_err = err ;
idx = i ;
best = found ;
}
if ( ! err )
break ;
}
geo - > in_height = best ;
geo - > scale_idx_v = idx ;
}
/*
* Find output geometry , that we can produce , using VOU scaling , closest to
* the requested rectangle
*/
static void vou_adjust_output ( struct sh_vou_geometry * geo , v4l2_std_id std )
{
2012-11-26 14:32:48 -03:00
unsigned int best_err = UINT_MAX , best = geo - > in_width ,
width_max , height_max , img_height_max ;
2015-06-05 08:07:19 -03:00
int i , idx_h = 0 , idx_v = 0 ;
2010-03-17 11:21:13 -03:00
if ( std & V4L2_STD_525_60 ) {
width_max = 858 ;
height_max = 262 * 2 ;
2010-07-29 04:11:42 -03:00
img_height_max = 480 ;
2010-03-17 11:21:13 -03:00
} else {
width_max = 864 ;
height_max = 312 * 2 ;
2010-07-29 04:11:42 -03:00
img_height_max = 576 ;
2010-03-17 11:21:13 -03:00
}
/* Select scales to come as close as possible to the output image */
for ( i = 0 ; i < ARRAY_SIZE ( vou_scale_h_num ) ; i + + ) {
unsigned int err ;
unsigned int found = geo - > in_width * vou_scale_h_num [ i ] /
vou_scale_h_den [ i ] ;
if ( found > VOU_MAX_IMAGE_WIDTH )
/* scales increase */
break ;
err = abs ( found - geo - > output . width ) ;
if ( err < best_err ) {
best_err = err ;
2015-06-05 08:07:19 -03:00
idx_h = i ;
2010-03-17 11:21:13 -03:00
best = found ;
}
if ( ! err )
break ;
}
geo - > output . width = best ;
2015-06-05 08:07:19 -03:00
geo - > scale_idx_h = idx_h ;
2010-03-17 11:21:13 -03:00
if ( geo - > output . left + best > width_max )
geo - > output . left = width_max - best ;
pr_debug ( " %s(): W %u * %u/%u = %u \n " , __func__ , geo - > in_width ,
2015-06-05 08:07:19 -03:00
vou_scale_h_num [ idx_h ] , vou_scale_h_den [ idx_h ] , best ) ;
2010-03-17 11:21:13 -03:00
best_err = UINT_MAX ;
/* This loop can be replaced with one division */
for ( i = 0 ; i < ARRAY_SIZE ( vou_scale_v_num ) ; i + + ) {
unsigned int err ;
unsigned int found = geo - > in_height * vou_scale_v_num [ i ] /
vou_scale_v_den [ i ] ;
2010-07-29 04:11:42 -03:00
if ( found > img_height_max )
2010-03-17 11:21:13 -03:00
/* scales increase */
break ;
err = abs ( found - geo - > output . height ) ;
if ( err < best_err ) {
best_err = err ;
2015-06-05 08:07:19 -03:00
idx_v = i ;
2010-03-17 11:21:13 -03:00
best = found ;
}
if ( ! err )
break ;
}
geo - > output . height = best ;
2015-06-05 08:07:19 -03:00
geo - > scale_idx_v = idx_v ;
2010-03-17 11:21:13 -03:00
if ( geo - > output . top + best > height_max )
geo - > output . top = height_max - best ;
pr_debug ( " %s(): H %u * %u/%u = %u \n " , __func__ , geo - > in_height ,
2015-06-05 08:07:19 -03:00
vou_scale_v_num [ idx_v ] , vou_scale_v_den [ idx_v ] , best ) ;
2010-03-17 11:21:13 -03:00
}
2015-06-07 05:58:03 -03:00
static int sh_vou_try_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_format * fmt )
2010-03-17 11:21:13 -03:00
{
2013-01-03 15:35:56 -03:00
struct sh_vou_device * vou_dev = video_drvdata ( file ) ;
2010-03-17 11:21:13 -03:00
struct v4l2_pix_format * pix = & fmt - > fmt . pix ;
2010-07-29 04:11:42 -03:00
unsigned int img_height_max ;
2010-03-17 11:21:13 -03:00
int pix_idx ;
2015-06-07 05:58:03 -03:00
dev_dbg ( vou_dev - > v4l2_dev . dev , " %s() \n " , __func__ ) ;
2010-03-17 11:21:13 -03:00
2015-06-07 05:58:03 -03:00
pix - > field = V4L2_FIELD_INTERLACED ;
pix - > colorspace = V4L2_COLORSPACE_SMPTE170M ;
pix - > ycbcr_enc = pix - > quantization = 0 ;
2010-03-17 11:21:13 -03:00
for ( pix_idx = 0 ; pix_idx < ARRAY_SIZE ( vou_fmt ) ; pix_idx + + )
if ( vou_fmt [ pix_idx ] . pfmt = = pix - > pixelformat )
break ;
if ( pix_idx = = ARRAY_SIZE ( vou_fmt ) )
return - EINVAL ;
2010-07-29 04:11:42 -03:00
if ( vou_dev - > std & V4L2_STD_525_60 )
img_height_max = 480 ;
else
img_height_max = 576 ;
2015-06-07 05:58:05 -03:00
v4l_bound_align_image ( & pix - > width ,
VOU_MIN_IMAGE_WIDTH , VOU_MAX_IMAGE_WIDTH , 2 ,
& pix - > height ,
VOU_MIN_IMAGE_HEIGHT , img_height_max , 1 , 0 ) ;
2015-06-07 05:58:04 -03:00
pix - > bytesperline = pix - > width * vou_fmt [ pix_idx ] . bpl ;
pix - > sizeimage = pix - > height * ( ( pix - > width * vou_fmt [ pix_idx ] . bpp ) > > 3 ) ;
2015-06-07 05:58:03 -03:00
return 0 ;
}
2015-06-07 05:58:05 -03:00
static int sh_vou_set_fmt_vid_out ( struct sh_vou_device * vou_dev ,
struct v4l2_pix_format * pix )
2015-06-07 05:58:03 -03:00
{
unsigned int img_height_max ;
struct sh_vou_geometry geo ;
struct v4l2_subdev_format format = {
. which = V4L2_SUBDEV_FORMAT_ACTIVE ,
/* Revisit: is this the correct code? */
. format . code = MEDIA_BUS_FMT_YUYV8_2X8 ,
. format . field = V4L2_FIELD_INTERLACED ,
. format . colorspace = V4L2_COLORSPACE_SMPTE170M ,
} ;
struct v4l2_mbus_framefmt * mbfmt = & format . format ;
int pix_idx ;
2015-06-07 05:58:05 -03:00
int ret ;
2015-06-07 05:58:03 -03:00
2015-06-07 05:58:05 -03:00
if ( vb2_is_busy ( & vou_dev - > queue ) )
return - EBUSY ;
2015-06-07 05:58:03 -03:00
for ( pix_idx = 0 ; pix_idx < ARRAY_SIZE ( vou_fmt ) ; pix_idx + + )
if ( vou_fmt [ pix_idx ] . pfmt = = pix - > pixelformat )
break ;
2010-03-17 11:21:13 -03:00
geo . in_width = pix - > width ;
geo . in_height = pix - > height ;
geo . output = vou_dev - > rect ;
vou_adjust_output ( & geo , vou_dev - > std ) ;
2015-04-09 04:05:59 -03:00
mbfmt - > width = geo . output . width ;
mbfmt - > height = geo . output . height ;
ret = v4l2_device_call_until_err ( & vou_dev - > v4l2_dev , 0 , pad ,
set_fmt , NULL , & format ) ;
2010-03-17 11:21:13 -03:00
/* Must be implemented, so, don't check for -ENOIOCTLCMD */
if ( ret < 0 )
return ret ;
dev_dbg ( vou_dev - > v4l2_dev . dev , " %s(): %ux%u -> %ux%u \n " , __func__ ,
2015-04-09 04:05:59 -03:00
geo . output . width , geo . output . height , mbfmt - > width , mbfmt - > height ) ;
2010-03-17 11:21:13 -03:00
2015-06-07 05:58:03 -03:00
if ( vou_dev - > std & V4L2_STD_525_60 )
img_height_max = 480 ;
else
img_height_max = 576 ;
2010-03-17 11:21:13 -03:00
/* Sanity checks */
2015-04-09 04:05:59 -03:00
if ( ( unsigned ) mbfmt - > width > VOU_MAX_IMAGE_WIDTH | |
( unsigned ) mbfmt - > height > img_height_max | |
mbfmt - > code ! = MEDIA_BUS_FMT_YUYV8_2X8 )
2010-03-17 11:21:13 -03:00
return - EIO ;
2015-04-09 04:05:59 -03:00
if ( mbfmt - > width ! = geo . output . width | |
mbfmt - > height ! = geo . output . height ) {
geo . output . width = mbfmt - > width ;
geo . output . height = mbfmt - > height ;
2010-03-17 11:21:13 -03:00
vou_adjust_input ( & geo , vou_dev - > std ) ;
}
/* We tried to preserve output rectangle, but it could have changed */
vou_dev - > rect = geo . output ;
pix - > width = geo . in_width ;
pix - > height = geo . in_height ;
dev_dbg ( vou_dev - > v4l2_dev . dev , " %s(): %ux%u \n " , __func__ ,
pix - > width , pix - > height ) ;
vou_dev - > pix_idx = pix_idx ;
vou_dev - > pix = * pix ;
sh_vou_configure_geometry ( vou_dev , pix_idx ,
geo . scale_idx_h , geo . scale_idx_v ) ;
return 0 ;
}
2015-06-07 05:58:05 -03:00
static int sh_vou_s_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_format * fmt )
2010-03-17 11:21:13 -03:00
{
2013-01-03 15:35:56 -03:00
struct sh_vou_device * vou_dev = video_drvdata ( file ) ;
2015-06-07 05:58:05 -03:00
int ret = sh_vou_try_fmt_vid_out ( file , priv , fmt ) ;
2010-03-17 11:21:13 -03:00
2015-06-07 05:58:05 -03:00
if ( ret )
2010-03-17 11:21:13 -03:00
return ret ;
2015-06-07 05:58:05 -03:00
return sh_vou_set_fmt_vid_out ( vou_dev , & fmt - > fmt . pix ) ;
2010-03-17 11:21:13 -03:00
}
2015-06-07 05:57:59 -03:00
static int sh_vou_enum_output ( struct file * file , void * fh ,
struct v4l2_output * a )
{
struct sh_vou_device * vou_dev = video_drvdata ( file ) ;
if ( a - > index )
return - EINVAL ;
2018-09-10 08:19:14 -04:00
strscpy ( a - > name , " Video Out " , sizeof ( a - > name ) ) ;
2015-06-07 05:57:59 -03:00
a - > type = V4L2_OUTPUT_TYPE_ANALOG ;
a - > std = vou_dev - > vdev . tvnorms ;
return 0 ;
}
2015-07-03 09:12:54 -03:00
static int sh_vou_g_output ( struct file * file , void * fh , unsigned int * i )
2015-06-07 05:57:59 -03:00
{
* i = 0 ;
return 0 ;
}
2015-07-03 09:12:54 -03:00
static int sh_vou_s_output ( struct file * file , void * fh , unsigned int i )
2015-06-07 05:57:59 -03:00
{
return i ? - EINVAL : 0 ;
}
2010-03-17 11:21:13 -03:00
static u32 sh_vou_ntsc_mode ( enum sh_vou_bus_fmt bus_fmt )
{
switch ( bus_fmt ) {
default :
2017-02-17 05:11:35 -02:00
pr_warn ( " %s(): Invalid bus-format code %d, using default 8-bit \n " ,
__func__ , bus_fmt ) ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2010-03-17 11:21:13 -03:00
case SH_VOU_BUS_8BIT :
return 1 ;
case SH_VOU_BUS_16BIT :
return 0 ;
case SH_VOU_BUS_BT656 :
return 3 ;
}
}
2013-03-15 06:10:40 -03:00
static int sh_vou_s_std ( struct file * file , void * priv , v4l2_std_id std_id )
2010-03-17 11:21:13 -03:00
{
2013-01-03 15:35:56 -03:00
struct sh_vou_device * vou_dev = video_drvdata ( file ) ;
2010-03-17 11:21:13 -03:00
int ret ;
2013-03-15 06:10:40 -03:00
dev_dbg ( vou_dev - > v4l2_dev . dev , " %s(): 0x%llx \n " , __func__ , std_id ) ;
2010-03-17 11:21:13 -03:00
2015-06-07 05:58:05 -03:00
if ( std_id = = vou_dev - > std )
return 0 ;
if ( vb2_is_busy ( & vou_dev - > queue ) )
return - EBUSY ;
2010-03-17 11:21:13 -03:00
ret = v4l2_device_call_until_err ( & vou_dev - > v4l2_dev , 0 , video ,
2013-03-15 06:10:40 -03:00
s_std_output , std_id ) ;
2010-03-17 11:21:13 -03:00
/* Shall we continue, if the subdev doesn't support .s_std_output()? */
if ( ret < 0 & & ret ! = - ENOIOCTLCMD )
return ret ;
2015-06-07 05:58:05 -03:00
vou_dev - > rect . top = vou_dev - > rect . left = 0 ;
vou_dev - > rect . width = VOU_MAX_IMAGE_WIDTH ;
if ( std_id & V4L2_STD_525_60 ) {
2010-03-17 11:21:13 -03:00
sh_vou_reg_ab_set ( vou_dev , VOUCR ,
sh_vou_ntsc_mode ( vou_dev - > pdata - > bus_fmt ) < < 29 , 7 < < 29 ) ;
2015-06-07 05:58:05 -03:00
vou_dev - > rect . height = 480 ;
} else {
2010-03-17 11:21:13 -03:00
sh_vou_reg_ab_set ( vou_dev , VOUCR , 5 < < 29 , 7 < < 29 ) ;
2015-06-07 05:58:05 -03:00
vou_dev - > rect . height = 576 ;
}
2010-03-17 11:21:13 -03:00
2015-06-07 05:58:05 -03:00
vou_dev - > pix . width = vou_dev - > rect . width ;
vou_dev - > pix . height = vou_dev - > rect . height ;
vou_dev - > pix . bytesperline =
vou_dev - > pix . width * vou_fmt [ vou_dev - > pix_idx ] . bpl ;
vou_dev - > pix . sizeimage = vou_dev - > pix . height *
( ( vou_dev - > pix . width * vou_fmt [ vou_dev - > pix_idx ] . bpp ) > > 3 ) ;
2013-03-15 06:10:40 -03:00
vou_dev - > std = std_id ;
2015-06-07 05:58:05 -03:00
sh_vou_set_fmt_vid_out ( vou_dev , & vou_dev - > pix ) ;
2010-03-17 11:21:13 -03:00
return 0 ;
}
static int sh_vou_g_std ( struct file * file , void * priv , v4l2_std_id * std )
{
2013-01-03 15:35:56 -03:00
struct sh_vou_device * vou_dev = video_drvdata ( file ) ;
2010-03-17 11:21:13 -03:00
dev_dbg ( vou_dev - > v4l2_dev . dev , " %s() \n " , __func__ ) ;
* std = vou_dev - > std ;
return 0 ;
}
2015-06-08 03:20:15 -03:00
static int sh_vou_log_status ( struct file * file , void * priv )
{
struct sh_vou_device * vou_dev = video_drvdata ( file ) ;
pr_info ( " VOUER: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUER ) ) ;
pr_info ( " VOUCR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUCR ) ) ;
pr_info ( " VOUSTR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUSTR ) ) ;
pr_info ( " VOUVCR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUVCR ) ) ;
pr_info ( " VOUISR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUISR ) ) ;
pr_info ( " VOUBCR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUBCR ) ) ;
pr_info ( " VOUDPR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUDPR ) ) ;
pr_info ( " VOUDSR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUDSR ) ) ;
pr_info ( " VOUVPR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUVPR ) ) ;
pr_info ( " VOUIR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUIR ) ) ;
pr_info ( " VOUSRR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUSRR ) ) ;
pr_info ( " VOUMSR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUMSR ) ) ;
pr_info ( " VOUHIR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUHIR ) ) ;
pr_info ( " VOUDFR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUDFR ) ) ;
pr_info ( " VOUAD1R: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUAD1R ) ) ;
pr_info ( " VOUAD2R: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUAD2R ) ) ;
pr_info ( " VOUAIR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUAIR ) ) ;
pr_info ( " VOUSWR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOUSWR ) ) ;
pr_info ( " VOURCR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOURCR ) ) ;
pr_info ( " VOURPR: 0x%08x \n " , sh_vou_reg_a_read ( vou_dev , VOURPR ) ) ;
return 0 ;
}
2015-06-07 05:58:01 -03:00
static int sh_vou_g_selection ( struct file * file , void * fh ,
struct v4l2_selection * sel )
2010-03-17 11:21:13 -03:00
{
2013-01-03 15:35:56 -03:00
struct sh_vou_device * vou_dev = video_drvdata ( file ) ;
2010-03-17 11:21:13 -03:00
2015-06-07 05:58:01 -03:00
if ( sel - > type ! = V4L2_BUF_TYPE_VIDEO_OUTPUT )
return - EINVAL ;
switch ( sel - > target ) {
case V4L2_SEL_TGT_COMPOSE :
sel - > r = vou_dev - > rect ;
break ;
case V4L2_SEL_TGT_COMPOSE_DEFAULT :
case V4L2_SEL_TGT_COMPOSE_BOUNDS :
sel - > r . left = 0 ;
sel - > r . top = 0 ;
sel - > r . width = VOU_MAX_IMAGE_WIDTH ;
2015-06-07 05:58:05 -03:00
if ( vou_dev - > std & V4L2_STD_525_60 )
sel - > r . height = 480 ;
else
sel - > r . height = 576 ;
2015-06-07 05:58:01 -03:00
break ;
default :
return - EINVAL ;
}
2010-03-17 11:21:13 -03:00
return 0 ;
}
/* Assume a dull encoder, do all the work ourselves. */
2015-06-07 05:58:01 -03:00
static int sh_vou_s_selection ( struct file * file , void * fh ,
struct v4l2_selection * sel )
2010-03-17 11:21:13 -03:00
{
2015-06-07 05:58:01 -03:00
struct v4l2_rect * rect = & sel - > r ;
2013-01-03 15:35:56 -03:00
struct sh_vou_device * vou_dev = video_drvdata ( file ) ;
2015-12-14 08:25:32 -02:00
struct v4l2_subdev_selection sd_sel = {
. which = V4L2_SUBDEV_FORMAT_ACTIVE ,
. target = V4L2_SEL_TGT_COMPOSE ,
} ;
2010-03-17 11:21:13 -03:00
struct v4l2_pix_format * pix = & vou_dev - > pix ;
struct sh_vou_geometry geo ;
2015-04-09 04:05:59 -03:00
struct v4l2_subdev_format format = {
. which = V4L2_SUBDEV_FORMAT_ACTIVE ,
2010-03-17 11:21:13 -03:00
/* Revisit: is this the correct code? */
2015-04-09 04:05:59 -03:00
. format . code = MEDIA_BUS_FMT_YUYV8_2X8 ,
. format . field = V4L2_FIELD_INTERLACED ,
. format . colorspace = V4L2_COLORSPACE_SMPTE170M ,
2010-03-17 11:21:13 -03:00
} ;
2010-07-29 04:11:42 -03:00
unsigned int img_height_max ;
2010-03-17 11:21:13 -03:00
int ret ;
2015-06-07 05:58:01 -03:00
if ( sel - > type ! = V4L2_BUF_TYPE_VIDEO_OUTPUT | |
sel - > target ! = V4L2_SEL_TGT_COMPOSE )
2010-03-17 11:21:13 -03:00
return - EINVAL ;
2015-06-07 05:58:05 -03:00
if ( vb2_is_busy ( & vou_dev - > queue ) )
return - EBUSY ;
2010-07-29 04:11:42 -03:00
if ( vou_dev - > std & V4L2_STD_525_60 )
img_height_max = 480 ;
else
img_height_max = 576 ;
2015-06-07 05:58:05 -03:00
v4l_bound_align_image ( & rect - > width ,
VOU_MIN_IMAGE_WIDTH , VOU_MAX_IMAGE_WIDTH , 1 ,
& rect - > height ,
VOU_MIN_IMAGE_HEIGHT , img_height_max , 1 , 0 ) ;
2010-03-17 11:21:13 -03:00
if ( rect - > width + rect - > left > VOU_MAX_IMAGE_WIDTH )
rect - > left = VOU_MAX_IMAGE_WIDTH - rect - > width ;
2010-07-29 04:11:42 -03:00
if ( rect - > height + rect - > top > img_height_max )
rect - > top = img_height_max - rect - > height ;
2010-03-17 11:21:13 -03:00
geo . output = * rect ;
geo . in_width = pix - > width ;
geo . in_height = pix - > height ;
/* Configure the encoder one-to-one, position at 0, ignore errors */
2015-12-14 08:25:32 -02:00
sd_sel . r . width = geo . output . width ;
sd_sel . r . height = geo . output . height ;
2010-03-17 11:21:13 -03:00
/*
2015-12-14 08:25:32 -02:00
* We first issue a S_SELECTION , so that the subsequent S_FMT delivers the
2010-03-17 11:21:13 -03:00
* final encoder configuration .
*/
2015-12-14 08:25:32 -02:00
v4l2_device_call_until_err ( & vou_dev - > v4l2_dev , 0 , pad ,
set_selection , NULL , & sd_sel ) ;
2015-04-09 04:05:59 -03:00
format . format . width = geo . output . width ;
format . format . height = geo . output . height ;
ret = v4l2_device_call_until_err ( & vou_dev - > v4l2_dev , 0 , pad ,
set_fmt , NULL , & format ) ;
2010-03-17 11:21:13 -03:00
/* Must be implemented, so, don't check for -ENOIOCTLCMD */
if ( ret < 0 )
return ret ;
/* Sanity checks */
2015-04-09 04:05:59 -03:00
if ( ( unsigned ) format . format . width > VOU_MAX_IMAGE_WIDTH | |
( unsigned ) format . format . height > img_height_max | |
format . format . code ! = MEDIA_BUS_FMT_YUYV8_2X8 )
2010-03-17 11:21:13 -03:00
return - EIO ;
2015-04-09 04:05:59 -03:00
geo . output . width = format . format . width ;
geo . output . height = format . format . height ;
2010-03-17 11:21:13 -03:00
/*
* No down - scaling . According to the API , current call has precedence :
2018-11-09 10:25:41 -05:00
* https : //linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/crop.html#cropping-structures
2010-03-17 11:21:13 -03:00
*/
vou_adjust_input ( & geo , vou_dev - > std ) ;
/* We tried to preserve output rectangle, but it could have changed */
vou_dev - > rect = geo . output ;
pix - > width = geo . in_width ;
pix - > height = geo . in_height ;
sh_vou_configure_geometry ( vou_dev , vou_dev - > pix_idx ,
geo . scale_idx_h , geo . scale_idx_v ) ;
return 0 ;
}
static irqreturn_t sh_vou_isr ( int irq , void * dev_id )
{
struct sh_vou_device * vou_dev = dev_id ;
static unsigned long j ;
2015-06-07 05:58:05 -03:00
struct sh_vou_buffer * vb ;
2010-03-17 11:21:13 -03:00
static int cnt ;
u32 irq_status = sh_vou_reg_a_read ( vou_dev , VOUIR ) , masked ;
u32 vou_status = sh_vou_reg_a_read ( vou_dev , VOUSTR ) ;
if ( ! ( irq_status & 0x300 ) ) {
if ( printk_timed_ratelimit ( & j , 500 ) )
dev_warn ( vou_dev - > v4l2_dev . dev , " IRQ status 0x%x! \n " ,
irq_status ) ;
return IRQ_NONE ;
}
spin_lock ( & vou_dev - > lock ) ;
2015-06-07 05:58:05 -03:00
if ( ! vou_dev - > active | | list_empty ( & vou_dev - > buf_list ) ) {
2010-03-17 11:21:13 -03:00
if ( printk_timed_ratelimit ( & j , 500 ) )
dev_warn ( vou_dev - > v4l2_dev . dev ,
" IRQ without active buffer: %x! \n " , irq_status ) ;
/* Just ack: buf_release will disable further interrupts */
sh_vou_reg_a_set ( vou_dev , VOUIR , 0 , 0x300 ) ;
spin_unlock ( & vou_dev - > lock ) ;
return IRQ_HANDLED ;
}
masked = ~ ( 0x300 & irq_status ) & irq_status & 0x30304 ;
dev_dbg ( vou_dev - > v4l2_dev . dev ,
" IRQ status 0x%x -> 0x%x, VOU status 0x%x, cnt %d \n " ,
irq_status , masked , vou_status , cnt ) ;
cnt + + ;
2014-01-13 11:30:26 -02:00
/* side = vou_status & 0x10000; */
2010-03-17 11:21:13 -03:00
/* Clear only set interrupts */
sh_vou_reg_a_write ( vou_dev , VOUIR , masked ) ;
vb = vou_dev - > active ;
2015-06-07 05:58:05 -03:00
if ( list_is_singular ( & vb - > list ) ) {
/* Keep cycling while no next buffer is available */
sh_vou_schedule_next ( vou_dev , & vb - > vb ) ;
2010-03-17 11:21:13 -03:00
spin_unlock ( & vou_dev - > lock ) ;
return IRQ_HANDLED ;
}
2015-06-07 05:58:05 -03:00
list_del ( & vb - > list ) ;
2015-11-03 08:16:37 -02:00
vb - > vb . vb2_buf . timestamp = ktime_get_ns ( ) ;
[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 10:30:30 -03:00
vb - > vb . sequence = vou_dev - > sequence + + ;
vb - > vb . field = V4L2_FIELD_INTERLACED ;
vb2_buffer_done ( & vb - > vb . vb2_buf , VB2_BUF_STATE_DONE ) ;
2015-06-07 05:58:05 -03:00
vou_dev - > active = list_entry ( vou_dev - > buf_list . next ,
struct sh_vou_buffer , list ) ;
2010-03-17 11:21:13 -03:00
2015-06-07 05:58:05 -03:00
if ( list_is_singular ( & vou_dev - > buf_list ) ) {
/* Keep cycling while no next buffer is available */
sh_vou_schedule_next ( vou_dev , & vou_dev - > active - > vb ) ;
} else {
struct sh_vou_buffer * new = list_entry ( vou_dev - > active - > list . next ,
struct sh_vou_buffer , list ) ;
sh_vou_schedule_next ( vou_dev , & new - > vb ) ;
2010-03-17 11:21:13 -03:00
}
spin_unlock ( & vou_dev - > lock ) ;
return IRQ_HANDLED ;
}
static int sh_vou_hw_init ( struct sh_vou_device * vou_dev )
{
struct sh_vou_pdata * pdata = vou_dev - > pdata ;
u32 voucr = sh_vou_ntsc_mode ( pdata - > bus_fmt ) < < 29 ;
int i = 100 ;
/* Disable all IRQs */
sh_vou_reg_a_write ( vou_dev , VOUIR , 0 ) ;
/* Reset VOU interfaces - registers unaffected */
sh_vou_reg_a_write ( vou_dev , VOUSRR , 0x101 ) ;
while ( - - i & & ( sh_vou_reg_a_read ( vou_dev , VOUSRR ) & 0x101 ) )
udelay ( 1 ) ;
if ( ! i )
return - ETIMEDOUT ;
dev_dbg ( vou_dev - > v4l2_dev . dev , " Reset took %dus \n " , 100 - i ) ;
if ( pdata - > flags & SH_VOU_PCLK_FALLING )
voucr | = 1 < < 28 ;
if ( pdata - > flags & SH_VOU_HSYNC_LOW )
voucr | = 1 < < 27 ;
if ( pdata - > flags & SH_VOU_VSYNC_LOW )
voucr | = 1 < < 26 ;
sh_vou_reg_ab_set ( vou_dev , VOUCR , voucr , 0xfc000000 ) ;
/* Manual register side switching at first */
sh_vou_reg_a_write ( vou_dev , VOURCR , 4 ) ;
/* Default - fixed HSYNC length, can be made configurable is required */
sh_vou_reg_ab_write ( vou_dev , VOUMSR , 0x800000 ) ;
2015-06-07 05:58:05 -03:00
sh_vou_set_fmt_vid_out ( vou_dev , & vou_dev - > pix ) ;
2010-03-17 11:21:13 -03:00
return 0 ;
}
/* File operations */
static int sh_vou_open ( struct file * file )
{
2013-01-03 15:35:56 -03:00
struct sh_vou_device * vou_dev = video_drvdata ( file ) ;
2015-06-07 05:58:05 -03:00
int err ;
2010-03-17 11:21:13 -03:00
2015-06-07 05:58:05 -03:00
if ( mutex_lock_interruptible ( & vou_dev - > fop_lock ) )
2012-06-24 06:33:26 -03:00
return - ERESTARTSYS ;
2015-06-07 05:57:58 -03:00
2015-06-07 05:58:05 -03:00
err = v4l2_fh_open ( file ) ;
if ( err )
goto done_open ;
if ( v4l2_fh_is_singular_file ( file ) & &
vou_dev - > status = = SH_VOU_INITIALISING ) {
2010-03-17 11:21:13 -03:00
/* First open */
2021-04-23 17:07:41 +02:00
err = pm_runtime_resume_and_get ( vou_dev - > v4l2_dev . dev ) ;
if ( err < 0 ) {
v4l2_fh_release ( file ) ;
goto done_open ;
}
2015-06-07 05:58:05 -03:00
err = sh_vou_hw_init ( vou_dev ) ;
if ( err < 0 ) {
2013-01-03 15:35:56 -03:00
pm_runtime_put ( vou_dev - > v4l2_dev . dev ) ;
2015-06-07 05:58:05 -03:00
v4l2_fh_release ( file ) ;
} else {
2010-03-17 11:21:13 -03:00
vou_dev - > status = SH_VOU_IDLE ;
}
}
2015-06-07 05:58:05 -03:00
done_open :
2012-06-24 06:33:26 -03:00
mutex_unlock ( & vou_dev - > fop_lock ) ;
2015-06-07 05:58:05 -03:00
return err ;
2010-03-17 11:21:13 -03:00
}
static int sh_vou_release ( struct file * file )
{
2013-01-03 15:35:56 -03:00
struct sh_vou_device * vou_dev = video_drvdata ( file ) ;
2015-06-07 05:58:05 -03:00
bool is_last ;
2010-03-17 11:21:13 -03:00
2015-06-07 05:57:58 -03:00
mutex_lock ( & vou_dev - > fop_lock ) ;
2015-06-07 05:58:05 -03:00
is_last = v4l2_fh_is_singular_file ( file ) ;
_vb2_fop_release ( file , NULL ) ;
if ( is_last ) {
2010-03-17 11:21:13 -03:00
/* Last close */
2015-06-07 05:58:05 -03:00
vou_dev - > status = SH_VOU_INITIALISING ;
2010-03-17 11:21:13 -03:00
sh_vou_reg_a_set ( vou_dev , VOUER , 0 , 0x101 ) ;
2013-01-03 15:35:56 -03:00
pm_runtime_put ( vou_dev - > v4l2_dev . dev ) ;
2010-03-17 11:21:13 -03:00
}
2015-06-07 05:57:58 -03:00
mutex_unlock ( & vou_dev - > fop_lock ) ;
2010-03-17 11:21:13 -03:00
return 0 ;
}
/* sh_vou display ioctl operations */
static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = {
2018-01-04 13:08:56 -05:00
. vidioc_querycap = sh_vou_querycap ,
2010-03-17 11:21:13 -03:00
. vidioc_enum_fmt_vid_out = sh_vou_enum_fmt_vid_out ,
. vidioc_g_fmt_vid_out = sh_vou_g_fmt_vid_out ,
. vidioc_s_fmt_vid_out = sh_vou_s_fmt_vid_out ,
. vidioc_try_fmt_vid_out = sh_vou_try_fmt_vid_out ,
2015-06-07 05:58:05 -03:00
. vidioc_reqbufs = vb2_ioctl_reqbufs ,
. vidioc_create_bufs = vb2_ioctl_create_bufs ,
. vidioc_querybuf = vb2_ioctl_querybuf ,
. vidioc_qbuf = vb2_ioctl_qbuf ,
. vidioc_dqbuf = vb2_ioctl_dqbuf ,
. vidioc_prepare_buf = vb2_ioctl_prepare_buf ,
. vidioc_streamon = vb2_ioctl_streamon ,
. vidioc_streamoff = vb2_ioctl_streamoff ,
. vidioc_expbuf = vb2_ioctl_expbuf ,
2015-06-07 05:57:59 -03:00
. vidioc_g_output = sh_vou_g_output ,
. vidioc_s_output = sh_vou_s_output ,
. vidioc_enum_output = sh_vou_enum_output ,
2010-03-17 11:21:13 -03:00
. vidioc_s_std = sh_vou_s_std ,
. vidioc_g_std = sh_vou_g_std ,
2015-06-07 05:58:01 -03:00
. vidioc_g_selection = sh_vou_g_selection ,
. vidioc_s_selection = sh_vou_s_selection ,
2015-06-08 03:20:15 -03:00
. vidioc_log_status = sh_vou_log_status ,
2010-03-17 11:21:13 -03:00
} ;
static const struct v4l2_file_operations sh_vou_fops = {
. owner = THIS_MODULE ,
. open = sh_vou_open ,
. release = sh_vou_release ,
2010-11-16 18:12:16 -03:00
. unlocked_ioctl = video_ioctl2 ,
2015-06-07 05:58:05 -03:00
. mmap = vb2_fop_mmap ,
. poll = vb2_fop_poll ,
. write = vb2_fop_write ,
2010-03-17 11:21:13 -03:00
} ;
static const struct video_device sh_vou_video_template = {
. name = " sh_vou " ,
. fops = & sh_vou_fops ,
. ioctl_ops = & sh_vou_ioctl_ops ,
. tvnorms = V4L2_STD_525_60 , /* PAL only supported in 8-bit non-bt656 mode */
2012-09-05 06:05:50 -03:00
. vfl_dir = VFL_DIR_TX ,
2019-06-26 03:16:07 -04:00
. device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING ,
2010-03-17 11:21:13 -03:00
} ;
2012-12-21 13:17:53 -08:00
static int sh_vou_probe ( struct platform_device * pdev )
2010-03-17 11:21:13 -03:00
{
struct sh_vou_pdata * vou_pdata = pdev - > dev . platform_data ;
struct v4l2_rect * rect ;
struct v4l2_pix_format * pix ;
struct i2c_adapter * i2c_adap ;
struct video_device * vdev ;
struct sh_vou_device * vou_dev ;
2015-06-07 05:57:56 -03:00
struct resource * reg_res ;
2010-03-17 11:21:13 -03:00
struct v4l2_subdev * subdev ;
2015-06-07 05:58:05 -03:00
struct vb2_queue * q ;
2010-03-17 11:21:13 -03:00
int irq , ret ;
reg_res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( ! vou_pdata | | ! reg_res | | irq < = 0 ) {
dev_err ( & pdev - > dev , " Insufficient VOU platform information. \n " ) ;
return - ENODEV ;
}
2015-06-07 05:57:56 -03:00
vou_dev = devm_kzalloc ( & pdev - > dev , sizeof ( * vou_dev ) , GFP_KERNEL ) ;
2010-03-17 11:21:13 -03:00
if ( ! vou_dev )
return - ENOMEM ;
2015-06-07 05:58:05 -03:00
INIT_LIST_HEAD ( & vou_dev - > buf_list ) ;
2010-03-17 11:21:13 -03:00
spin_lock_init ( & vou_dev - > lock ) ;
2010-11-16 18:12:16 -03:00
mutex_init ( & vou_dev - > fop_lock ) ;
2010-03-17 11:21:13 -03:00
vou_dev - > pdata = vou_pdata ;
2015-06-07 05:58:05 -03:00
vou_dev - > status = SH_VOU_INITIALISING ;
vou_dev - > pix_idx = 1 ;
2010-03-17 11:21:13 -03:00
rect = & vou_dev - > rect ;
pix = & vou_dev - > pix ;
/* Fill in defaults */
2013-06-03 05:36:40 -03:00
vou_dev - > std = V4L2_STD_NTSC_M ;
2010-03-17 11:21:13 -03:00
rect - > left = 0 ;
rect - > top = 0 ;
rect - > width = VOU_MAX_IMAGE_WIDTH ;
2010-07-29 04:11:42 -03:00
rect - > height = 480 ;
2010-03-17 11:21:13 -03:00
pix - > width = VOU_MAX_IMAGE_WIDTH ;
2010-07-29 04:11:42 -03:00
pix - > height = 480 ;
2015-06-07 05:58:00 -03:00
pix - > pixelformat = V4L2_PIX_FMT_NV16 ;
2015-06-07 05:58:05 -03:00
pix - > field = V4L2_FIELD_INTERLACED ;
2015-06-07 05:58:04 -03:00
pix - > bytesperline = VOU_MAX_IMAGE_WIDTH ;
2010-07-29 04:11:42 -03:00
pix - > sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480 ;
2010-03-17 11:21:13 -03:00
pix - > colorspace = V4L2_COLORSPACE_SMPTE170M ;
2015-06-07 05:57:56 -03:00
vou_dev - > base = devm_ioremap_resource ( & pdev - > dev , reg_res ) ;
if ( IS_ERR ( vou_dev - > base ) )
return PTR_ERR ( vou_dev - > base ) ;
2010-03-17 11:21:13 -03:00
2015-06-07 05:57:56 -03:00
ret = devm_request_irq ( & pdev - > dev , irq , sh_vou_isr , 0 , " vou " , vou_dev ) ;
2010-03-17 11:21:13 -03:00
if ( ret < 0 )
2015-06-07 05:57:56 -03:00
return ret ;
2010-03-17 11:21:13 -03:00
ret = v4l2_device_register ( & pdev - > dev , & vou_dev - > v4l2_dev ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Error registering v4l2 device \n " ) ;
2015-06-07 05:57:56 -03:00
return ret ;
2010-03-17 11:21:13 -03:00
}
2015-03-09 19:10:51 -03:00
vdev = & vou_dev - > vdev ;
2010-03-17 11:21:13 -03:00
* vdev = sh_vou_video_template ;
if ( vou_pdata - > bus_fmt = = SH_VOU_BUS_8BIT )
vdev - > tvnorms | = V4L2_STD_PAL ;
vdev - > v4l2_dev = & vou_dev - > v4l2_dev ;
2015-03-09 19:10:51 -03:00
vdev - > release = video_device_release_empty ;
2010-11-16 18:12:16 -03:00
vdev - > lock = & vou_dev - > fop_lock ;
2010-03-17 11:21:13 -03:00
video_set_drvdata ( vdev , vou_dev ) ;
2015-06-07 05:58:05 -03:00
/* Initialize the vb2 queue */
q = & vou_dev - > queue ;
q - > type = V4L2_BUF_TYPE_VIDEO_OUTPUT ;
q - > io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE ;
q - > drv_priv = vou_dev ;
q - > buf_struct_size = sizeof ( struct sh_vou_buffer ) ;
q - > ops = & sh_vou_qops ;
q - > mem_ops = & vb2_dma_contig_memops ;
q - > timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC ;
q - > min_buffers_needed = 2 ;
q - > lock = & vou_dev - > fop_lock ;
2016-02-15 13:41:51 -02:00
q - > dev = & pdev - > dev ;
2015-06-07 05:58:05 -03:00
ret = vb2_queue_init ( q ) ;
if ( ret )
2016-02-15 13:41:51 -02:00
goto ei2cgadap ;
2015-06-07 05:58:05 -03:00
vdev - > queue = q ;
INIT_LIST_HEAD ( & vou_dev - > buf_list ) ;
2010-03-17 11:21:13 -03:00
pm_runtime_enable ( & pdev - > dev ) ;
pm_runtime_resume ( & pdev - > dev ) ;
i2c_adap = i2c_get_adapter ( vou_pdata - > i2c_adap ) ;
if ( ! i2c_adap ) {
ret = - ENODEV ;
goto ei2cgadap ;
}
ret = sh_vou_hw_init ( vou_dev ) ;
if ( ret < 0 )
goto ereset ;
subdev = v4l2_i2c_new_subdev_board ( & vou_dev - > v4l2_dev , i2c_adap ,
2010-09-24 10:16:44 -03:00
vou_pdata - > board_info , NULL ) ;
2010-03-17 11:21:13 -03:00
if ( ! subdev ) {
ret = - ENOMEM ;
goto ei2cnd ;
}
2020-02-03 12:41:18 +01:00
ret = video_register_device ( vdev , VFL_TYPE_VIDEO , - 1 ) ;
2010-03-17 11:21:13 -03:00
if ( ret < 0 )
goto evregdev ;
return 0 ;
evregdev :
ei2cnd :
ereset :
i2c_put_adapter ( i2c_adap ) ;
ei2cgadap :
pm_runtime_disable ( & pdev - > dev ) ;
v4l2_device_unregister ( & vou_dev - > v4l2_dev ) ;
return ret ;
}
2012-12-21 13:17:53 -08:00
static int sh_vou_remove ( struct platform_device * pdev )
2010-03-17 11:21:13 -03:00
{
struct v4l2_device * v4l2_dev = platform_get_drvdata ( pdev ) ;
struct sh_vou_device * vou_dev = container_of ( v4l2_dev ,
struct sh_vou_device , v4l2_dev ) ;
struct v4l2_subdev * sd = list_entry ( v4l2_dev - > subdevs . next ,
struct v4l2_subdev , list ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
pm_runtime_disable ( & pdev - > dev ) ;
2015-03-09 19:10:51 -03:00
video_unregister_device ( & vou_dev - > vdev ) ;
2010-03-17 11:21:13 -03:00
i2c_put_adapter ( client - > adapter ) ;
v4l2_device_unregister ( & vou_dev - > v4l2_dev ) ;
return 0 ;
}
2020-12-11 14:38:06 +01:00
static struct platform_driver sh_vou = {
2012-12-21 13:17:53 -08:00
. remove = sh_vou_remove ,
2010-03-17 11:21:13 -03:00
. driver = {
. name = " sh-vou " ,
} ,
} ;
2013-03-05 01:53:37 -03:00
module_platform_driver_probe ( sh_vou , sh_vou_probe ) ;
2010-03-17 11:21:13 -03:00
MODULE_DESCRIPTION ( " SuperH VOU driver " ) ;
MODULE_AUTHOR ( " Guennadi Liakhovetski <g.liakhovetski@gmx.de> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2011-06-25 11:28:37 -03:00
MODULE_VERSION ( " 0.1.0 " ) ;
2010-03-17 11:21:13 -03:00
MODULE_ALIAS ( " platform:sh-vou " ) ;