2018-07-20 03:52:50 -04:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* A virtual codec example device .
*
* Copyright 2018 Cisco Systems , Inc . and / or its affiliates . All rights reserved .
*
* This is a virtual codec device driver for testing the codec framework .
* It simulates a device that uses memory buffers for both source and
* destination and encodes or decodes the data .
*/
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/fs.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/platform_device.h>
# include <media/v4l2-mem2mem.h>
# include <media/v4l2-device.h>
# include <media/v4l2-ioctl.h>
# include <media/v4l2-ctrls.h>
# include <media/v4l2-event.h>
# include <media/videobuf2-vmalloc.h>
2018-08-22 03:50:56 -04:00
# include "codec-v4l2-fwht.h"
2018-07-20 03:52:50 -04:00
MODULE_DESCRIPTION ( " Virtual codec device " ) ;
MODULE_AUTHOR ( " Hans Verkuil <hans.verkuil@cisco.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
static bool multiplanar ;
module_param ( multiplanar , bool , 0444 ) ;
MODULE_PARM_DESC ( multiplanar ,
" use multi-planar API instead of single-planar API " ) ;
static unsigned int debug ;
module_param ( debug , uint , 0644 ) ;
MODULE_PARM_DESC ( debug , " activates debug info " ) ;
# define VICODEC_NAME "vicodec"
# define MAX_WIDTH 4096U
# define MIN_WIDTH 640U
# define MAX_HEIGHT 2160U
2018-10-10 03:03:43 -04:00
# define MIN_HEIGHT 360U
2018-07-20 03:52:50 -04:00
# define dprintk(dev, fmt, arg...) \
v4l2_dbg ( 1 , debug , & dev - > v4l2_dev , " %s: " fmt , __func__ , # # arg )
2018-08-19 10:58:55 -04:00
struct pixfmt_info {
u32 id ;
unsigned int bytesperline_mult ;
unsigned int sizeimage_mult ;
unsigned int sizeimage_div ;
unsigned int luma_step ;
unsigned int chroma_step ;
/* Chroma plane subsampling */
unsigned int width_div ;
unsigned int height_div ;
} ;
2018-08-22 03:50:56 -04:00
static const struct v4l2_fwht_pixfmt_info pixfmt_fwht = {
2019-01-20 11:29:02 -02:00
V4L2_PIX_FMT_FWHT , 0 , 3 , 1 , 1 , 1 , 1 , 1 , 0 , 1
2018-08-19 10:58:55 -04:00
} ;
2019-03-06 16:13:40 -05:00
static const struct v4l2_fwht_pixfmt_info pixfmt_stateless_fwht = {
V4L2_PIX_FMT_FWHT_STATELESS , 0 , 3 , 1 , 1 , 1 , 1 , 1 , 0 , 1
} ;
2018-07-20 03:52:50 -04:00
static void vicodec_dev_release ( struct device * dev )
{
}
static struct platform_device vicodec_pdev = {
. name = VICODEC_NAME ,
. dev . release = vicodec_dev_release ,
} ;
/* Per-queue, driver-specific private data */
struct vicodec_q_data {
2019-01-21 09:46:14 -02:00
unsigned int coded_width ;
unsigned int coded_height ;
unsigned int visible_width ;
unsigned int visible_height ;
2018-07-20 03:52:50 -04:00
unsigned int sizeimage ;
2019-05-07 05:30:24 -04:00
unsigned int vb2_sizeimage ;
2018-07-20 03:52:50 -04:00
unsigned int sequence ;
2018-08-22 03:50:56 -04:00
const struct v4l2_fwht_pixfmt_info * info ;
2018-07-20 03:52:50 -04:00
} ;
enum {
V4L2_M2M_SRC = 0 ,
V4L2_M2M_DST = 1 ,
} ;
2019-03-06 16:13:36 -05:00
struct vicodec_dev_instance {
struct video_device vfd ;
struct mutex mutex ;
spinlock_t lock ;
struct v4l2_m2m_dev * m2m_dev ;
} ;
2018-07-20 03:52:50 -04:00
struct vicodec_dev {
struct v4l2_device v4l2_dev ;
2019-03-06 16:13:36 -05:00
struct vicodec_dev_instance stateful_enc ;
struct vicodec_dev_instance stateful_dec ;
2019-03-06 16:13:41 -05:00
struct vicodec_dev_instance stateless_dec ;
2018-07-20 03:52:50 -04:00
# ifdef CONFIG_MEDIA_CONTROLLER
struct media_device mdev ;
# endif
} ;
struct vicodec_ctx {
struct v4l2_fh fh ;
struct vicodec_dev * dev ;
bool is_enc ;
2019-03-06 16:13:41 -05:00
bool is_stateless ;
2019-03-25 07:27:45 -04:00
bool is_draining ;
bool next_is_last ;
bool has_stopped ;
2018-07-20 03:52:50 -04:00
spinlock_t * lock ;
struct v4l2_ctrl_handler hdl ;
struct vb2_v4l2_buffer * last_src_buf ;
/* Source and destination queue data */
struct vicodec_q_data q_data [ 2 ] ;
2018-08-22 03:50:56 -04:00
struct v4l2_fwht_state state ;
2018-07-20 03:52:50 -04:00
u32 cur_buf_offset ;
u32 comp_max_size ;
u32 comp_size ;
2019-01-21 09:46:17 -02:00
u32 header_size ;
2018-07-20 03:52:50 -04:00
u32 comp_magic_cnt ;
bool comp_has_frame ;
bool comp_has_next_frame ;
2019-01-21 09:46:18 -02:00
bool first_source_change_sent ;
bool source_changed ;
2018-07-20 03:52:50 -04:00
} ;
2019-03-25 07:27:45 -04:00
static const struct v4l2_event vicodec_eos_event = {
. type = V4L2_EVENT_EOS
} ;
2018-07-20 03:52:50 -04:00
static inline struct vicodec_ctx * file2ctx ( struct file * file )
{
return container_of ( file - > private_data , struct vicodec_ctx , fh ) ;
}
static struct vicodec_q_data * get_q_data ( struct vicodec_ctx * ctx ,
enum v4l2_buf_type type )
{
switch ( type ) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT :
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
return & ctx - > q_data [ V4L2_M2M_SRC ] ;
case V4L2_BUF_TYPE_VIDEO_CAPTURE :
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
return & ctx - > q_data [ V4L2_M2M_DST ] ;
default :
break ;
}
return NULL ;
}
2019-03-06 16:13:32 -05:00
static void copy_cap_to_ref ( const u8 * cap , const struct v4l2_fwht_pixfmt_info * info ,
struct v4l2_fwht_state * state )
{
int plane_idx ;
u8 * p_ref = state - > ref_frame . buf ;
unsigned int cap_stride = state - > stride ;
unsigned int ref_stride = state - > ref_stride ;
for ( plane_idx = 0 ; plane_idx < info - > planes_num ; plane_idx + + ) {
int i ;
unsigned int h_div = ( plane_idx = = 1 | | plane_idx = = 2 ) ?
info - > height_div : 1 ;
const u8 * row_cap = cap ;
u8 * row_ref = p_ref ;
if ( info - > planes_num = = 3 & & plane_idx = = 1 ) {
cap_stride / = 2 ;
ref_stride / = 2 ;
}
if ( plane_idx = = 1 & &
( info - > id = = V4L2_PIX_FMT_NV24 | |
info - > id = = V4L2_PIX_FMT_NV42 ) ) {
cap_stride * = 2 ;
ref_stride * = 2 ;
}
for ( i = 0 ; i < state - > visible_height / h_div ; i + + ) {
memcpy ( row_ref , row_cap , ref_stride ) ;
row_ref + = ref_stride ;
row_cap + = cap_stride ;
}
cap + = cap_stride * ( state - > coded_height / h_div ) ;
p_ref + = ref_stride * ( state - > coded_height / h_div ) ;
}
}
2019-03-06 16:13:33 -05:00
static bool validate_by_version ( unsigned int flags , unsigned int version )
{
if ( ! version | | version > FWHT_VERSION )
return false ;
if ( version > = 2 ) {
unsigned int components_num = 1 +
( ( flags & FWHT_FL_COMPONENTS_NUM_MSK ) > >
FWHT_FL_COMPONENTS_NUM_OFFSET ) ;
unsigned int pixenc = flags & FWHT_FL_PIXENC_MSK ;
if ( components_num = = 0 | | components_num > 4 | | ! pixenc )
return false ;
}
return true ;
}
2019-03-06 16:13:42 -05:00
static bool validate_stateless_params_flags ( const struct v4l2_ctrl_fwht_params * params ,
const struct v4l2_fwht_pixfmt_info * cur_info )
{
unsigned int width_div =
( params - > flags & FWHT_FL_CHROMA_FULL_WIDTH ) ? 1 : 2 ;
unsigned int height_div =
( params - > flags & FWHT_FL_CHROMA_FULL_HEIGHT ) ? 1 : 2 ;
unsigned int components_num = 3 ;
unsigned int pixenc = 0 ;
if ( params - > version < 3 )
return false ;
components_num = 1 + ( ( params - > flags & FWHT_FL_COMPONENTS_NUM_MSK ) > >
FWHT_FL_COMPONENTS_NUM_OFFSET ) ;
pixenc = ( params - > flags & FWHT_FL_PIXENC_MSK ) ;
if ( v4l2_fwht_validate_fmt ( cur_info , width_div , height_div ,
components_num , pixenc ) )
return true ;
return false ;
}
static void update_state_from_header ( struct vicodec_ctx * ctx )
{
const struct fwht_cframe_hdr * p_hdr = & ctx - > state . header ;
ctx - > state . visible_width = ntohl ( p_hdr - > width ) ;
ctx - > state . visible_height = ntohl ( p_hdr - > height ) ;
ctx - > state . colorspace = ntohl ( p_hdr - > colorspace ) ;
ctx - > state . xfer_func = ntohl ( p_hdr - > xfer_func ) ;
ctx - > state . ycbcr_enc = ntohl ( p_hdr - > ycbcr_enc ) ;
ctx - > state . quantization = ntohl ( p_hdr - > quantization ) ;
}
2018-07-20 03:52:50 -04:00
static int device_process ( struct vicodec_ctx * ctx ,
2018-12-04 15:54:37 -05:00
struct vb2_v4l2_buffer * src_vb ,
struct vb2_v4l2_buffer * dst_vb )
2018-07-20 03:52:50 -04:00
{
struct vicodec_dev * dev = ctx - > dev ;
2018-08-22 03:50:56 -04:00
struct v4l2_fwht_state * state = & ctx - > state ;
2018-12-04 15:54:37 -05:00
u8 * p_src , * p_dst ;
2019-03-06 16:13:42 -05:00
int ret = 0 ;
2018-07-20 03:52:50 -04:00
2019-03-06 16:13:42 -05:00
if ( ctx - > is_enc | | ctx - > is_stateless )
2018-12-04 15:54:37 -05:00
p_src = vb2_plane_vaddr ( & src_vb - > vb2_buf , 0 ) ;
2018-07-20 03:52:50 -04:00
else
2018-12-04 15:54:37 -05:00
p_src = state - > compressed_frame ;
2019-03-06 16:13:42 -05:00
if ( ctx - > is_stateless ) {
struct media_request * src_req = src_vb - > vb2_buf . req_obj . req ;
ret = v4l2_ctrl_request_setup ( src_req , & ctx - > hdl ) ;
if ( ret )
return ret ;
update_state_from_header ( ctx ) ;
ctx - > state . header . size =
htonl ( vb2_get_plane_payload ( & src_vb - > vb2_buf , 0 ) ) ;
/*
* set the reference buffer from the reference timestamp
* only if this is a P - frame
*/
if ( ! ( ntohl ( ctx - > state . header . flags ) & FWHT_FL_I_FRAME ) ) {
struct vb2_buffer * ref_vb2_buf ;
int ref_buf_idx ;
struct vb2_queue * vq_cap =
v4l2_m2m_get_vq ( ctx - > fh . m2m_ctx ,
V4L2_BUF_TYPE_VIDEO_CAPTURE ) ;
ref_buf_idx = vb2_find_timestamp ( vq_cap ,
ctx - > state . ref_frame_ts , 0 ) ;
if ( ref_buf_idx < 0 )
return - EINVAL ;
ref_vb2_buf = vq_cap - > bufs [ ref_buf_idx ] ;
if ( ref_vb2_buf - > state = = VB2_BUF_STATE_ERROR )
ret = - EINVAL ;
ctx - > state . ref_frame . buf =
vb2_plane_vaddr ( ref_vb2_buf , 0 ) ;
} else {
ctx - > state . ref_frame . buf = NULL ;
}
}
2018-12-04 15:54:37 -05:00
p_dst = vb2_plane_vaddr ( & dst_vb - > vb2_buf , 0 ) ;
if ( ! p_src | | ! p_dst ) {
2018-07-20 03:52:50 -04:00
v4l2_err ( & dev - > v4l2_dev ,
" Acquiring kernel pointers to buffers failed \n " ) ;
return - EFAULT ;
}
if ( ctx - > is_enc ) {
2018-12-04 15:54:37 -05:00
struct vicodec_q_data * q_src ;
2019-03-06 16:13:27 -05:00
int comp_sz_or_errcode ;
2018-07-20 03:52:50 -04:00
2018-12-04 15:54:37 -05:00
q_src = get_q_data ( ctx , V4L2_BUF_TYPE_VIDEO_OUTPUT ) ;
state - > info = q_src - > info ;
2019-03-06 16:13:27 -05:00
comp_sz_or_errcode = v4l2_fwht_encode ( state , p_src , p_dst ) ;
if ( comp_sz_or_errcode < 0 )
return comp_sz_or_errcode ;
vb2_set_plane_payload ( & dst_vb - > vb2_buf , 0 , comp_sz_or_errcode ) ;
2018-07-20 03:52:50 -04:00
} else {
2019-03-06 16:13:28 -05:00
struct vicodec_q_data * q_dst ;
2019-01-24 07:51:08 -02:00
unsigned int comp_frame_size = ntohl ( ctx - > state . header . size ) ;
2019-03-06 16:13:28 -05:00
q_dst = get_q_data ( ctx , V4L2_BUF_TYPE_VIDEO_CAPTURE ) ;
2019-01-24 07:51:08 -02:00
if ( comp_frame_size > ctx - > comp_max_size )
return - EINVAL ;
2018-12-04 15:54:37 -05:00
state - > info = q_dst - > info ;
ret = v4l2_fwht_decode ( state , p_src , p_dst ) ;
2018-09-10 11:00:40 -04:00
if ( ret < 0 )
2018-07-20 03:52:50 -04:00
return ret ;
2019-03-06 16:13:42 -05:00
if ( ! ctx - > is_stateless )
copy_cap_to_ref ( p_dst , ctx - > state . info , & ctx - > state ) ;
2019-03-06 16:13:32 -05:00
2018-12-04 15:54:37 -05:00
vb2_set_plane_payload ( & dst_vb - > vb2_buf , 0 , q_dst - > sizeimage ) ;
2019-03-30 10:11:38 -04:00
if ( ntohl ( ctx - > state . header . flags ) & FWHT_FL_I_FRAME )
dst_vb - > flags | = V4L2_BUF_FLAG_KEYFRAME ;
else
dst_vb - > flags | = V4L2_BUF_FLAG_PFRAME ;
2018-07-20 03:52:50 -04:00
}
2019-03-06 16:13:42 -05:00
return ret ;
2018-07-20 03:52:50 -04:00
}
/*
* mem2mem callbacks
*/
2019-01-26 09:12:04 -02:00
static enum vb2_buffer_state get_next_header ( struct vicodec_ctx * ctx ,
u8 * * pp , u32 sz )
2019-01-21 09:46:17 -02:00
{
static const u8 magic [ ] = {
0x4f , 0x4f , 0x4f , 0x4f , 0xff , 0xff , 0xff , 0xff
} ;
u8 * p = * pp ;
u32 state ;
u8 * header = ( u8 * ) & ctx - > state . header ;
state = VB2_BUF_STATE_DONE ;
if ( ! ctx - > header_size ) {
state = VB2_BUF_STATE_ERROR ;
for ( ; p < * pp + sz ; p + + ) {
u32 copy ;
p = memchr ( p , magic [ ctx - > comp_magic_cnt ] ,
* pp + sz - p ) ;
if ( ! p ) {
ctx - > comp_magic_cnt = 0 ;
p = * pp + sz ;
break ;
}
copy = sizeof ( magic ) - ctx - > comp_magic_cnt ;
if ( * pp + sz - p < copy )
copy = * pp + sz - p ;
memcpy ( header + ctx - > comp_magic_cnt , p , copy ) ;
ctx - > comp_magic_cnt + = copy ;
if ( ! memcmp ( header , magic , ctx - > comp_magic_cnt ) ) {
p + = copy ;
state = VB2_BUF_STATE_DONE ;
break ;
}
ctx - > comp_magic_cnt = 0 ;
}
if ( ctx - > comp_magic_cnt < sizeof ( magic ) ) {
* pp = p ;
return state ;
}
ctx - > header_size = sizeof ( magic ) ;
}
if ( ctx - > header_size < sizeof ( struct fwht_cframe_hdr ) ) {
u32 copy = sizeof ( struct fwht_cframe_hdr ) - ctx - > header_size ;
if ( * pp + sz - p < copy )
copy = * pp + sz - p ;
memcpy ( header + ctx - > header_size , p , copy ) ;
p + = copy ;
ctx - > header_size + = copy ;
}
* pp = p ;
return state ;
}
2018-07-20 03:52:50 -04:00
/* device_run() - prepares and starts the device */
static void device_run ( void * priv )
{
struct vicodec_ctx * ctx = priv ;
struct vicodec_dev * dev = ctx - > dev ;
struct vb2_v4l2_buffer * src_buf , * dst_buf ;
2019-03-06 16:13:28 -05:00
struct vicodec_q_data * q_src , * q_dst ;
2018-07-20 03:52:50 -04:00
u32 state ;
2019-03-06 16:13:42 -05:00
struct media_request * src_req ;
2018-07-20 03:52:50 -04:00
src_buf = v4l2_m2m_next_src_buf ( ctx - > fh . m2m_ctx ) ;
dst_buf = v4l2_m2m_dst_buf_remove ( ctx - > fh . m2m_ctx ) ;
2019-03-06 16:13:42 -05:00
src_req = src_buf - > vb2_buf . req_obj . req ;
2018-12-04 15:54:37 -05:00
q_src = get_q_data ( ctx , V4L2_BUF_TYPE_VIDEO_OUTPUT ) ;
2019-03-06 16:13:28 -05:00
q_dst = get_q_data ( ctx , V4L2_BUF_TYPE_VIDEO_CAPTURE ) ;
2018-07-20 03:52:50 -04:00
state = VB2_BUF_STATE_DONE ;
if ( device_process ( ctx , src_buf , dst_buf ) )
state = VB2_BUF_STATE_ERROR ;
2019-03-06 16:13:28 -05:00
else
dst_buf - > sequence = q_dst - > sequence + + ;
dst_buf - > flags & = ~ V4L2_BUF_FLAG_LAST ;
2019-03-30 10:11:38 -04:00
v4l2_m2m_buf_copy_metadata ( src_buf , dst_buf , false ) ;
2019-03-06 16:13:28 -05:00
2018-07-20 03:52:50 -04:00
spin_lock ( ctx - > lock ) ;
if ( ! ctx - > comp_has_next_frame & & src_buf = = ctx - > last_src_buf ) {
dst_buf - > flags | = V4L2_BUF_FLAG_LAST ;
2019-03-25 07:27:45 -04:00
v4l2_event_queue_fh ( & ctx - > fh , & vicodec_eos_event ) ;
ctx - > is_draining = false ;
ctx - > has_stopped = true ;
2018-07-20 03:52:50 -04:00
}
2019-03-06 16:13:42 -05:00
if ( ctx - > is_enc | | ctx - > is_stateless ) {
2018-12-04 15:54:37 -05:00
src_buf - > sequence = q_src - > sequence + + ;
2018-07-20 03:52:50 -04:00
src_buf = v4l2_m2m_src_buf_remove ( ctx - > fh . m2m_ctx ) ;
v4l2_m2m_buf_done ( src_buf , state ) ;
} else if ( vb2_get_plane_payload ( & src_buf - > vb2_buf , 0 ) = = ctx - > cur_buf_offset ) {
2018-12-04 15:54:37 -05:00
src_buf - > sequence = q_src - > sequence + + ;
2018-07-20 03:52:50 -04:00
src_buf = v4l2_m2m_src_buf_remove ( ctx - > fh . m2m_ctx ) ;
v4l2_m2m_buf_done ( src_buf , state ) ;
ctx - > cur_buf_offset = 0 ;
ctx - > comp_has_next_frame = false ;
}
v4l2_m2m_buf_done ( dst_buf , state ) ;
2019-03-06 16:13:42 -05:00
2018-07-20 03:52:50 -04:00
ctx - > comp_size = 0 ;
2019-01-21 09:46:17 -02:00
ctx - > header_size = 0 ;
2018-07-20 03:52:50 -04:00
ctx - > comp_magic_cnt = 0 ;
ctx - > comp_has_frame = false ;
spin_unlock ( ctx - > lock ) ;
2019-03-30 10:27:36 -04:00
if ( ctx - > is_stateless & & src_req )
v4l2_ctrl_request_complete ( src_req , & ctx - > hdl ) ;
2018-07-20 03:52:50 -04:00
if ( ctx - > is_enc )
2019-03-06 16:13:36 -05:00
v4l2_m2m_job_finish ( dev - > stateful_enc . m2m_dev , ctx - > fh . m2m_ctx ) ;
2019-03-06 16:13:41 -05:00
else if ( ctx - > is_stateless )
v4l2_m2m_job_finish ( dev - > stateless_dec . m2m_dev ,
ctx - > fh . m2m_ctx ) ;
2018-07-20 03:52:50 -04:00
else
2019-03-06 16:13:36 -05:00
v4l2_m2m_job_finish ( dev - > stateful_dec . m2m_dev , ctx - > fh . m2m_ctx ) ;
2018-07-20 03:52:50 -04:00
}
2018-12-04 15:54:37 -05:00
static void job_remove_src_buf ( struct vicodec_ctx * ctx , u32 state )
2018-07-20 03:52:50 -04:00
{
struct vb2_v4l2_buffer * src_buf ;
2018-12-04 15:54:37 -05:00
struct vicodec_q_data * q_src ;
2018-07-20 03:52:50 -04:00
2018-12-04 15:54:37 -05:00
q_src = get_q_data ( ctx , V4L2_BUF_TYPE_VIDEO_OUTPUT ) ;
2018-07-20 03:52:50 -04:00
spin_lock ( ctx - > lock ) ;
src_buf = v4l2_m2m_src_buf_remove ( ctx - > fh . m2m_ctx ) ;
2018-12-04 15:54:37 -05:00
src_buf - > sequence = q_src - > sequence + + ;
2018-07-20 03:52:50 -04:00
v4l2_m2m_buf_done ( src_buf , state ) ;
ctx - > cur_buf_offset = 0 ;
spin_unlock ( ctx - > lock ) ;
}
2019-01-21 09:46:18 -02:00
static const struct v4l2_fwht_pixfmt_info *
info_from_header ( const struct fwht_cframe_hdr * p_hdr )
{
unsigned int flags = ntohl ( p_hdr - > flags ) ;
unsigned int width_div = ( flags & FWHT_FL_CHROMA_FULL_WIDTH ) ? 1 : 2 ;
unsigned int height_div = ( flags & FWHT_FL_CHROMA_FULL_HEIGHT ) ? 1 : 2 ;
unsigned int components_num = 3 ;
unsigned int pixenc = 0 ;
unsigned int version = ntohl ( p_hdr - > version ) ;
2019-02-15 15:58:53 -05:00
if ( version > = 2 ) {
2019-01-21 09:46:18 -02:00
components_num = 1 + ( ( flags & FWHT_FL_COMPONENTS_NUM_MSK ) > >
FWHT_FL_COMPONENTS_NUM_OFFSET ) ;
pixenc = ( flags & FWHT_FL_PIXENC_MSK ) ;
}
2019-03-06 16:13:34 -05:00
return v4l2_fwht_find_nth_fmt ( width_div , height_div ,
2019-01-21 09:46:18 -02:00
components_num , pixenc , 0 ) ;
}
static bool is_header_valid ( const struct fwht_cframe_hdr * p_hdr )
{
const struct v4l2_fwht_pixfmt_info * info ;
unsigned int w = ntohl ( p_hdr - > width ) ;
unsigned int h = ntohl ( p_hdr - > height ) ;
unsigned int version = ntohl ( p_hdr - > version ) ;
unsigned int flags = ntohl ( p_hdr - > flags ) ;
if ( w < MIN_WIDTH | | w > MAX_WIDTH | | h < MIN_HEIGHT | | h > MAX_HEIGHT )
return false ;
2019-03-06 16:13:33 -05:00
if ( ! validate_by_version ( flags , version ) )
return false ;
2019-01-21 09:46:18 -02:00
info = info_from_header ( p_hdr ) ;
if ( ! info )
return false ;
return true ;
}
static void update_capture_data_from_header ( struct vicodec_ctx * ctx )
{
struct vicodec_q_data * q_dst = get_q_data ( ctx ,
V4L2_BUF_TYPE_VIDEO_CAPTURE ) ;
const struct fwht_cframe_hdr * p_hdr = & ctx - > state . header ;
const struct v4l2_fwht_pixfmt_info * info = info_from_header ( p_hdr ) ;
unsigned int flags = ntohl ( p_hdr - > flags ) ;
unsigned int hdr_width_div = ( flags & FWHT_FL_CHROMA_FULL_WIDTH ) ? 1 : 2 ;
unsigned int hdr_height_div = ( flags & FWHT_FL_CHROMA_FULL_HEIGHT ) ? 1 : 2 ;
2019-03-06 16:13:42 -05:00
/*
* This function should not be used by a stateless codec since
* it changes values in q_data that are not request specific
*/
WARN_ON ( ctx - > is_stateless ) ;
2019-01-21 09:46:18 -02:00
q_dst - > info = info ;
q_dst - > visible_width = ntohl ( p_hdr - > width ) ;
q_dst - > visible_height = ntohl ( p_hdr - > height ) ;
q_dst - > coded_width = vic_round_dim ( q_dst - > visible_width , hdr_width_div ) ;
q_dst - > coded_height = vic_round_dim ( q_dst - > visible_height ,
hdr_height_div ) ;
q_dst - > sizeimage = q_dst - > coded_width * q_dst - > coded_height *
q_dst - > info - > sizeimage_mult / q_dst - > info - > sizeimage_div ;
ctx - > state . colorspace = ntohl ( p_hdr - > colorspace ) ;
ctx - > state . xfer_func = ntohl ( p_hdr - > xfer_func ) ;
ctx - > state . ycbcr_enc = ntohl ( p_hdr - > ycbcr_enc ) ;
ctx - > state . quantization = ntohl ( p_hdr - > quantization ) ;
}
static void set_last_buffer ( struct vb2_v4l2_buffer * dst_buf ,
const struct vb2_v4l2_buffer * src_buf ,
struct vicodec_ctx * ctx )
{
struct vicodec_q_data * q_dst = get_q_data ( ctx ,
V4L2_BUF_TYPE_VIDEO_CAPTURE ) ;
vb2_set_plane_payload ( & dst_buf - > vb2_buf , 0 , 0 ) ;
dst_buf - > sequence = q_dst - > sequence + + ;
2019-02-05 16:20:33 -05:00
v4l2_m2m_buf_copy_metadata ( src_buf , dst_buf , ! ctx - > is_enc ) ;
2019-01-21 09:46:18 -02:00
dst_buf - > flags | = V4L2_BUF_FLAG_LAST ;
v4l2_m2m_buf_done ( dst_buf , VB2_BUF_STATE_DONE ) ;
}
2018-07-20 03:52:50 -04:00
static int job_ready ( void * priv )
{
static const u8 magic [ ] = {
0x4f , 0x4f , 0x4f , 0x4f , 0xff , 0xff , 0xff , 0xff
} ;
struct vicodec_ctx * ctx = priv ;
struct vb2_v4l2_buffer * src_buf ;
2018-12-04 15:54:37 -05:00
u8 * p_src ;
2018-07-20 03:52:50 -04:00
u8 * p ;
u32 sz ;
u32 state ;
2019-01-21 09:46:18 -02:00
struct vicodec_q_data * q_dst = get_q_data ( ctx ,
V4L2_BUF_TYPE_VIDEO_CAPTURE ) ;
unsigned int flags ;
unsigned int hdr_width_div ;
unsigned int hdr_height_div ;
unsigned int max_to_copy ;
unsigned int comp_frame_size ;
2019-03-25 07:27:45 -04:00
if ( ctx - > has_stopped )
return 0 ;
2019-01-21 09:46:18 -02:00
if ( ctx - > source_changed )
return 0 ;
2019-03-06 16:13:42 -05:00
if ( ctx - > is_stateless | | ctx - > is_enc | | ctx - > comp_has_frame )
2018-07-20 03:52:50 -04:00
return 1 ;
restart :
ctx - > comp_has_next_frame = false ;
src_buf = v4l2_m2m_next_src_buf ( ctx - > fh . m2m_ctx ) ;
if ( ! src_buf )
return 0 ;
2018-12-04 15:54:37 -05:00
p_src = vb2_plane_vaddr ( & src_buf - > vb2_buf , 0 ) ;
2018-07-20 03:52:50 -04:00
sz = vb2_get_plane_payload ( & src_buf - > vb2_buf , 0 ) ;
2018-12-04 15:54:37 -05:00
p = p_src + ctx - > cur_buf_offset ;
2018-07-20 03:52:50 -04:00
state = VB2_BUF_STATE_DONE ;
2019-01-21 09:46:17 -02:00
if ( ctx - > header_size < sizeof ( struct fwht_cframe_hdr ) ) {
state = get_next_header ( ctx , & p , p_src + sz - p ) ;
if ( ctx - > header_size < sizeof ( struct fwht_cframe_hdr ) ) {
2019-03-25 07:27:45 -04:00
if ( ctx - > is_draining & & src_buf = = ctx - > last_src_buf )
return 1 ;
2018-12-04 15:54:37 -05:00
job_remove_src_buf ( ctx , state ) ;
2018-07-20 03:52:50 -04:00
goto restart ;
}
2019-01-21 09:46:18 -02:00
}
2018-07-20 03:52:50 -04:00
2019-01-21 09:46:18 -02:00
comp_frame_size = ntohl ( ctx - > state . header . size ) ;
2019-01-21 09:46:17 -02:00
2019-01-21 09:46:18 -02:00
/*
* The current scanned frame might be the first frame of a new
* resolution so its size might be larger than ctx - > comp_max_size .
* In that case it is copied up to the current buffer capacity and
* the copy will continue after allocating new large enough buffer
* when restreaming
*/
max_to_copy = min ( comp_frame_size , ctx - > comp_max_size ) ;
if ( ctx - > comp_size < max_to_copy ) {
u32 copy = max_to_copy - ctx - > comp_size ;
2018-07-20 03:52:50 -04:00
2018-12-04 15:54:37 -05:00
if ( copy > p_src + sz - p )
copy = p_src + sz - p ;
2018-08-22 03:50:56 -04:00
memcpy ( ctx - > state . compressed_frame + ctx - > comp_size ,
2018-07-20 03:52:50 -04:00
p , copy ) ;
p + = copy ;
ctx - > comp_size + = copy ;
2019-01-21 09:46:18 -02:00
if ( ctx - > comp_size < max_to_copy ) {
2019-03-25 07:27:45 -04:00
if ( ctx - > is_draining & & src_buf = = ctx - > last_src_buf )
return 1 ;
2018-12-04 15:54:37 -05:00
job_remove_src_buf ( ctx , state ) ;
2018-07-20 03:52:50 -04:00
goto restart ;
}
}
2018-12-04 15:54:37 -05:00
ctx - > cur_buf_offset = p - p_src ;
2019-01-21 09:46:18 -02:00
if ( ctx - > comp_size = = comp_frame_size )
ctx - > comp_has_frame = true ;
2018-07-20 03:52:50 -04:00
ctx - > comp_has_next_frame = false ;
2019-01-21 09:46:18 -02:00
if ( ctx - > comp_has_frame & & sz - ctx - > cur_buf_offset > =
sizeof ( struct fwht_cframe_hdr ) ) {
2018-08-21 02:53:34 -04:00
struct fwht_cframe_hdr * p_hdr = ( struct fwht_cframe_hdr * ) p ;
2018-07-20 03:52:50 -04:00
u32 frame_size = ntohl ( p_hdr - > size ) ;
u32 remaining = sz - ctx - > cur_buf_offset - sizeof ( * p_hdr ) ;
if ( ! memcmp ( p , magic , sizeof ( magic ) ) )
ctx - > comp_has_next_frame = remaining > = frame_size ;
}
2019-01-21 09:46:18 -02:00
/*
* if the header is invalid the device_run will just drop the frame
* with an error
*/
if ( ! is_header_valid ( & ctx - > state . header ) & & ctx - > comp_has_frame )
return 1 ;
flags = ntohl ( ctx - > state . header . flags ) ;
hdr_width_div = ( flags & FWHT_FL_CHROMA_FULL_WIDTH ) ? 1 : 2 ;
hdr_height_div = ( flags & FWHT_FL_CHROMA_FULL_HEIGHT ) ? 1 : 2 ;
if ( ntohl ( ctx - > state . header . width ) ! = q_dst - > visible_width | |
ntohl ( ctx - > state . header . height ) ! = q_dst - > visible_height | |
! q_dst - > info | |
hdr_width_div ! = q_dst - > info - > width_div | |
hdr_height_div ! = q_dst - > info - > height_div ) {
static const struct v4l2_event rs_event = {
. type = V4L2_EVENT_SOURCE_CHANGE ,
. u . src_change . changes = V4L2_EVENT_SRC_CH_RESOLUTION ,
} ;
struct vb2_v4l2_buffer * dst_buf =
v4l2_m2m_dst_buf_remove ( ctx - > fh . m2m_ctx ) ;
update_capture_data_from_header ( ctx ) ;
v4l2_event_queue_fh ( & ctx - > fh , & rs_event ) ;
set_last_buffer ( dst_buf , src_buf , ctx ) ;
ctx - > source_changed = true ;
return 0 ;
}
2018-07-20 03:52:50 -04:00
return 1 ;
}
/*
* video ioctls
*/
2018-08-22 03:50:56 -04:00
static const struct v4l2_fwht_pixfmt_info * find_fmt ( u32 fmt )
2018-07-20 03:52:50 -04:00
{
2018-08-22 03:50:56 -04:00
const struct v4l2_fwht_pixfmt_info * info =
v4l2_fwht_find_pixfmt ( fmt ) ;
2018-07-20 03:52:50 -04:00
2018-08-22 03:50:56 -04:00
if ( ! info )
info = v4l2_fwht_get_pixfmt ( 0 ) ;
return info ;
2018-07-20 03:52:50 -04:00
}
static int vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * cap )
{
2018-09-10 08:19:16 -04:00
strscpy ( cap - > driver , VICODEC_NAME , sizeof ( cap - > driver ) ) ;
strscpy ( cap - > card , VICODEC_NAME , sizeof ( cap - > card ) ) ;
2018-07-20 03:52:50 -04:00
snprintf ( cap - > bus_info , sizeof ( cap - > bus_info ) ,
" platform:%s " , VICODEC_NAME ) ;
return 0 ;
}
2019-01-21 09:46:16 -02:00
static int enum_fmt ( struct v4l2_fmtdesc * f , struct vicodec_ctx * ctx ,
bool is_out )
2018-07-20 03:52:50 -04:00
{
2019-01-21 09:46:16 -02:00
bool is_uncomp = ( ctx - > is_enc & & is_out ) | | ( ! ctx - > is_enc & & ! is_out ) ;
2018-07-20 03:52:50 -04:00
if ( V4L2_TYPE_IS_MULTIPLANAR ( f - > type ) & & ! multiplanar )
return - EINVAL ;
if ( ! V4L2_TYPE_IS_MULTIPLANAR ( f - > type ) & & multiplanar )
return - EINVAL ;
2018-08-22 03:50:56 -04:00
if ( is_uncomp ) {
const struct v4l2_fwht_pixfmt_info * info =
2019-01-21 09:46:16 -02:00
get_q_data ( ctx , f - > type ) - > info ;
2019-03-25 07:27:45 -04:00
if ( ctx - > is_enc | |
! vb2_is_streaming ( & ctx - > fh . m2m_ctx - > cap_q_ctx . q ) )
2019-01-21 09:46:16 -02:00
info = v4l2_fwht_get_pixfmt ( f - > index ) ;
else
2019-03-06 16:13:34 -05:00
info = v4l2_fwht_find_nth_fmt ( info - > width_div ,
2019-01-21 09:46:16 -02:00
info - > height_div ,
info - > components_num ,
info - > pixenc ,
f - > index ) ;
2018-08-22 03:50:56 -04:00
if ( ! info )
return - EINVAL ;
f - > pixelformat = info - > id ;
} else {
if ( f - > index )
return - EINVAL ;
2019-03-06 16:13:43 -05:00
f - > pixelformat = ctx - > is_stateless ?
V4L2_PIX_FMT_FWHT_STATELESS : V4L2_PIX_FMT_FWHT ;
2018-08-22 03:50:56 -04:00
}
2018-07-20 03:52:50 -04:00
return 0 ;
}
static int vidioc_enum_fmt_vid_cap ( struct file * file , void * priv ,
struct v4l2_fmtdesc * f )
{
struct vicodec_ctx * ctx = file2ctx ( file ) ;
2019-01-21 09:46:16 -02:00
return enum_fmt ( f , ctx , false ) ;
2018-07-20 03:52:50 -04:00
}
static int vidioc_enum_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_fmtdesc * f )
{
struct vicodec_ctx * ctx = file2ctx ( file ) ;
2019-01-21 09:46:16 -02:00
return enum_fmt ( f , ctx , true ) ;
2018-07-20 03:52:50 -04:00
}
static int vidioc_g_fmt ( struct vicodec_ctx * ctx , struct v4l2_format * f )
{
struct vb2_queue * vq ;
struct vicodec_q_data * q_data ;
struct v4l2_pix_format_mplane * pix_mp ;
struct v4l2_pix_format * pix ;
2018-08-22 03:50:56 -04:00
const struct v4l2_fwht_pixfmt_info * info ;
2018-07-20 03:52:50 -04:00
vq = v4l2_m2m_get_vq ( ctx - > fh . m2m_ctx , f - > type ) ;
if ( ! vq )
return - EINVAL ;
q_data = get_q_data ( ctx , f - > type ) ;
2018-08-19 10:58:55 -04:00
info = q_data - > info ;
2018-07-20 03:52:50 -04:00
switch ( f - > type ) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE :
case V4L2_BUF_TYPE_VIDEO_OUTPUT :
if ( multiplanar )
return - EINVAL ;
pix = & f - > fmt . pix ;
2019-01-21 09:46:14 -02:00
pix - > width = q_data - > coded_width ;
pix - > height = q_data - > coded_height ;
2018-07-20 03:52:50 -04:00
pix - > field = V4L2_FIELD_NONE ;
2018-08-19 10:58:55 -04:00
pix - > pixelformat = info - > id ;
2019-01-21 09:46:14 -02:00
pix - > bytesperline = q_data - > coded_width *
info - > bytesperline_mult ;
2018-07-20 03:52:50 -04:00
pix - > sizeimage = q_data - > sizeimage ;
2018-08-22 03:50:56 -04:00
pix - > colorspace = ctx - > state . colorspace ;
pix - > xfer_func = ctx - > state . xfer_func ;
pix - > ycbcr_enc = ctx - > state . ycbcr_enc ;
pix - > quantization = ctx - > state . quantization ;
2018-07-20 03:52:50 -04:00
break ;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
if ( ! multiplanar )
return - EINVAL ;
pix_mp = & f - > fmt . pix_mp ;
2019-01-21 09:46:14 -02:00
pix_mp - > width = q_data - > coded_width ;
pix_mp - > height = q_data - > coded_height ;
2018-07-20 03:52:50 -04:00
pix_mp - > field = V4L2_FIELD_NONE ;
2018-08-19 10:58:55 -04:00
pix_mp - > pixelformat = info - > id ;
2018-07-20 03:52:50 -04:00
pix_mp - > num_planes = 1 ;
2018-08-19 10:58:55 -04:00
pix_mp - > plane_fmt [ 0 ] . bytesperline =
2019-01-21 09:46:14 -02:00
q_data - > coded_width * info - > bytesperline_mult ;
2018-07-20 03:52:50 -04:00
pix_mp - > plane_fmt [ 0 ] . sizeimage = q_data - > sizeimage ;
2018-08-22 03:50:56 -04:00
pix_mp - > colorspace = ctx - > state . colorspace ;
pix_mp - > xfer_func = ctx - > state . xfer_func ;
pix_mp - > ycbcr_enc = ctx - > state . ycbcr_enc ;
pix_mp - > quantization = ctx - > state . quantization ;
2018-07-20 03:52:50 -04:00
memset ( pix_mp - > reserved , 0 , sizeof ( pix_mp - > reserved ) ) ;
memset ( pix_mp - > plane_fmt [ 0 ] . reserved , 0 ,
sizeof ( pix_mp - > plane_fmt [ 0 ] . reserved ) ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int vidioc_g_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_format * f )
{
return vidioc_g_fmt ( file2ctx ( file ) , f ) ;
}
static int vidioc_g_fmt_vid_cap ( struct file * file , void * priv ,
struct v4l2_format * f )
{
return vidioc_g_fmt ( file2ctx ( file ) , f ) ;
}
static int vidioc_try_fmt ( struct vicodec_ctx * ctx , struct v4l2_format * f )
{
struct v4l2_pix_format_mplane * pix_mp ;
struct v4l2_pix_format * pix ;
2018-08-19 10:58:55 -04:00
struct v4l2_plane_pix_format * plane ;
2019-03-06 16:13:43 -05:00
const struct v4l2_fwht_pixfmt_info * info = ctx - > is_stateless ?
& pixfmt_stateless_fwht : & pixfmt_fwht ;
2018-07-20 03:52:50 -04:00
switch ( f - > type ) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE :
case V4L2_BUF_TYPE_VIDEO_OUTPUT :
pix = & f - > fmt . pix ;
2019-03-06 16:13:43 -05:00
if ( pix - > pixelformat ! = V4L2_PIX_FMT_FWHT & &
pix - > pixelformat ! = V4L2_PIX_FMT_FWHT_STATELESS )
2018-08-19 10:58:55 -04:00
info = find_fmt ( pix - > pixelformat ) ;
2019-01-21 09:46:14 -02:00
pix - > width = clamp ( pix - > width , MIN_WIDTH , MAX_WIDTH ) ;
pix - > width = vic_round_dim ( pix - > width , info - > width_div ) ;
pix - > height = clamp ( pix - > height , MIN_HEIGHT , MAX_HEIGHT ) ;
pix - > height = vic_round_dim ( pix - > height , info - > height_div ) ;
2018-07-20 03:52:50 -04:00
pix - > field = V4L2_FIELD_NONE ;
2018-08-19 10:58:55 -04:00
pix - > bytesperline =
pix - > width * info - > bytesperline_mult ;
pix - > sizeimage = pix - > width * pix - > height *
info - > sizeimage_mult / info - > sizeimage_div ;
if ( pix - > pixelformat = = V4L2_PIX_FMT_FWHT )
2018-08-21 02:53:34 -04:00
pix - > sizeimage + = sizeof ( struct fwht_cframe_hdr ) ;
2018-07-20 03:52:50 -04:00
break ;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
pix_mp = & f - > fmt . pix_mp ;
2018-08-19 10:58:55 -04:00
plane = pix_mp - > plane_fmt ;
2019-03-06 16:13:43 -05:00
if ( pix_mp - > pixelformat ! = V4L2_PIX_FMT_FWHT & &
pix_mp - > pixelformat ! = V4L2_PIX_FMT_FWHT_STATELESS )
2018-08-19 10:58:55 -04:00
info = find_fmt ( pix_mp - > pixelformat ) ;
pix_mp - > num_planes = 1 ;
2019-01-21 09:46:14 -02:00
pix_mp - > width = clamp ( pix_mp - > width , MIN_WIDTH , MAX_WIDTH ) ;
pix_mp - > width = vic_round_dim ( pix_mp - > width , info - > width_div ) ;
pix_mp - > height = clamp ( pix_mp - > height , MIN_HEIGHT , MAX_HEIGHT ) ;
pix_mp - > height = vic_round_dim ( pix_mp - > height ,
info - > height_div ) ;
2018-07-20 03:52:50 -04:00
pix_mp - > field = V4L2_FIELD_NONE ;
2018-08-19 10:58:55 -04:00
plane - > bytesperline =
pix_mp - > width * info - > bytesperline_mult ;
plane - > sizeimage = pix_mp - > width * pix_mp - > height *
info - > sizeimage_mult / info - > sizeimage_div ;
if ( pix_mp - > pixelformat = = V4L2_PIX_FMT_FWHT )
2018-08-21 02:53:34 -04:00
plane - > sizeimage + = sizeof ( struct fwht_cframe_hdr ) ;
2018-07-20 03:52:50 -04:00
memset ( pix_mp - > reserved , 0 , sizeof ( pix_mp - > reserved ) ) ;
2018-08-19 10:58:55 -04:00
memset ( plane - > reserved , 0 , sizeof ( plane - > reserved ) ) ;
2018-07-20 03:52:50 -04:00
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int vidioc_try_fmt_vid_cap ( struct file * file , void * priv ,
struct v4l2_format * f )
{
struct vicodec_ctx * ctx = file2ctx ( file ) ;
struct v4l2_pix_format_mplane * pix_mp ;
struct v4l2_pix_format * pix ;
switch ( f - > type ) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE :
if ( multiplanar )
return - EINVAL ;
pix = & f - > fmt . pix ;
pix - > pixelformat = ctx - > is_enc ? V4L2_PIX_FMT_FWHT :
2018-08-19 10:58:55 -04:00
find_fmt ( f - > fmt . pix . pixelformat ) - > id ;
2018-08-22 03:50:56 -04:00
pix - > colorspace = ctx - > state . colorspace ;
pix - > xfer_func = ctx - > state . xfer_func ;
pix - > ycbcr_enc = ctx - > state . ycbcr_enc ;
pix - > quantization = ctx - > state . quantization ;
2018-07-20 03:52:50 -04:00
break ;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
if ( ! multiplanar )
return - EINVAL ;
pix_mp = & f - > fmt . pix_mp ;
pix_mp - > pixelformat = ctx - > is_enc ? V4L2_PIX_FMT_FWHT :
2018-08-19 10:58:55 -04:00
find_fmt ( pix_mp - > pixelformat ) - > id ;
2018-08-22 03:50:56 -04:00
pix_mp - > colorspace = ctx - > state . colorspace ;
pix_mp - > xfer_func = ctx - > state . xfer_func ;
pix_mp - > ycbcr_enc = ctx - > state . ycbcr_enc ;
pix_mp - > quantization = ctx - > state . quantization ;
2018-07-20 03:52:50 -04:00
break ;
default :
return - EINVAL ;
}
return vidioc_try_fmt ( ctx , f ) ;
}
static int vidioc_try_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_format * f )
{
struct vicodec_ctx * ctx = file2ctx ( file ) ;
struct v4l2_pix_format_mplane * pix_mp ;
struct v4l2_pix_format * pix ;
switch ( f - > type ) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT :
if ( multiplanar )
return - EINVAL ;
pix = & f - > fmt . pix ;
2019-03-06 16:13:43 -05:00
if ( ctx - > is_enc )
pix - > pixelformat = find_fmt ( pix - > pixelformat ) - > id ;
else if ( ctx - > is_stateless )
pix - > pixelformat = V4L2_PIX_FMT_FWHT_STATELESS ;
else
pix - > pixelformat = V4L2_PIX_FMT_FWHT ;
2018-07-20 03:52:50 -04:00
if ( ! pix - > colorspace )
pix - > colorspace = V4L2_COLORSPACE_REC709 ;
break ;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
if ( ! multiplanar )
return - EINVAL ;
pix_mp = & f - > fmt . pix_mp ;
2019-03-06 16:13:43 -05:00
if ( ctx - > is_enc )
pix_mp - > pixelformat = find_fmt ( pix_mp - > pixelformat ) - > id ;
else if ( ctx - > is_stateless )
pix_mp - > pixelformat = V4L2_PIX_FMT_FWHT_STATELESS ;
else
pix_mp - > pixelformat = V4L2_PIX_FMT_FWHT ;
2018-07-20 03:52:50 -04:00
if ( ! pix_mp - > colorspace )
pix_mp - > colorspace = V4L2_COLORSPACE_REC709 ;
break ;
default :
return - EINVAL ;
}
return vidioc_try_fmt ( ctx , f ) ;
}
static int vidioc_s_fmt ( struct vicodec_ctx * ctx , struct v4l2_format * f )
{
struct vicodec_q_data * q_data ;
struct vb2_queue * vq ;
bool fmt_changed = true ;
struct v4l2_pix_format_mplane * pix_mp ;
struct v4l2_pix_format * pix ;
vq = v4l2_m2m_get_vq ( ctx - > fh . m2m_ctx , f - > type ) ;
if ( ! vq )
return - EINVAL ;
q_data = get_q_data ( ctx , f - > type ) ;
if ( ! q_data )
return - EINVAL ;
switch ( f - > type ) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE :
case V4L2_BUF_TYPE_VIDEO_OUTPUT :
pix = & f - > fmt . pix ;
if ( ctx - > is_enc & & V4L2_TYPE_IS_OUTPUT ( f - > type ) )
fmt_changed =
2019-01-21 09:46:18 -02:00
! q_data - > info | |
2018-08-19 10:58:55 -04:00
q_data - > info - > id ! = pix - > pixelformat | |
2019-01-21 09:46:14 -02:00
q_data - > coded_width ! = pix - > width | |
q_data - > coded_height ! = pix - > height ;
2018-07-20 03:52:50 -04:00
if ( vb2_is_busy ( vq ) & & fmt_changed )
return - EBUSY ;
2018-08-19 10:58:55 -04:00
if ( pix - > pixelformat = = V4L2_PIX_FMT_FWHT )
q_data - > info = & pixfmt_fwht ;
2019-03-06 16:13:43 -05:00
else if ( pix - > pixelformat = = V4L2_PIX_FMT_FWHT_STATELESS )
q_data - > info = & pixfmt_stateless_fwht ;
2018-08-19 10:58:55 -04:00
else
q_data - > info = find_fmt ( pix - > pixelformat ) ;
2019-01-21 09:46:14 -02:00
q_data - > coded_width = pix - > width ;
q_data - > coded_height = pix - > height ;
2018-07-20 03:52:50 -04:00
q_data - > sizeimage = pix - > sizeimage ;
break ;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
pix_mp = & f - > fmt . pix_mp ;
if ( ctx - > is_enc & & V4L2_TYPE_IS_OUTPUT ( f - > type ) )
fmt_changed =
2019-01-21 09:46:18 -02:00
! q_data - > info | |
2018-08-19 10:58:55 -04:00
q_data - > info - > id ! = pix_mp - > pixelformat | |
2019-01-21 09:46:14 -02:00
q_data - > coded_width ! = pix_mp - > width | |
q_data - > coded_height ! = pix_mp - > height ;
2018-07-20 03:52:50 -04:00
if ( vb2_is_busy ( vq ) & & fmt_changed )
return - EBUSY ;
2018-08-19 10:58:55 -04:00
if ( pix_mp - > pixelformat = = V4L2_PIX_FMT_FWHT )
q_data - > info = & pixfmt_fwht ;
2019-03-06 16:13:43 -05:00
else if ( pix_mp - > pixelformat = = V4L2_PIX_FMT_FWHT_STATELESS )
q_data - > info = & pixfmt_stateless_fwht ;
2018-08-19 10:58:55 -04:00
else
q_data - > info = find_fmt ( pix_mp - > pixelformat ) ;
2019-01-21 09:46:14 -02:00
q_data - > coded_width = pix_mp - > width ;
q_data - > coded_height = pix_mp - > height ;
2018-07-20 03:52:50 -04:00
q_data - > sizeimage = pix_mp - > plane_fmt [ 0 ] . sizeimage ;
break ;
default :
return - EINVAL ;
}
dprintk ( ctx - > dev ,
2019-03-15 11:04:24 -04:00
" Setting format for type %d, coded wxh: %dx%d, fourcc: 0x%08x \n " ,
2019-01-21 09:46:14 -02:00
f - > type , q_data - > coded_width , q_data - > coded_height ,
q_data - > info - > id ) ;
2018-07-20 03:52:50 -04:00
return 0 ;
}
static int vidioc_s_fmt_vid_cap ( struct file * file , void * priv ,
struct v4l2_format * f )
{
int ret ;
ret = vidioc_try_fmt_vid_cap ( file , priv , f ) ;
if ( ret )
return ret ;
return vidioc_s_fmt ( file2ctx ( file ) , f ) ;
}
static int vidioc_s_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_format * f )
{
struct vicodec_ctx * ctx = file2ctx ( file ) ;
2019-03-15 11:04:24 -04:00
struct vicodec_q_data * q_data ;
struct vicodec_q_data * q_data_cap ;
2018-07-20 03:52:50 -04:00
struct v4l2_pix_format * pix ;
2019-03-15 11:04:24 -04:00
struct v4l2_pix_format_mplane * pix_mp ;
u32 coded_w = 0 , coded_h = 0 ;
unsigned int size = 0 ;
2018-07-20 03:52:50 -04:00
int ret ;
2019-03-15 11:04:24 -04:00
q_data = get_q_data ( ctx , f - > type ) ;
q_data_cap = get_q_data ( ctx , V4L2_BUF_TYPE_VIDEO_CAPTURE ) ;
2018-07-20 03:52:50 -04:00
ret = vidioc_try_fmt_vid_out ( file , priv , f ) ;
if ( ret )
return ret ;
2019-03-15 11:04:24 -04:00
if ( ctx - > is_enc ) {
struct vb2_queue * vq = v4l2_m2m_get_vq ( ctx - > fh . m2m_ctx , f - > type ) ;
struct vb2_queue * vq_cap = v4l2_m2m_get_vq ( ctx - > fh . m2m_ctx ,
V4L2_BUF_TYPE_VIDEO_CAPTURE ) ;
const struct v4l2_fwht_pixfmt_info * info = ctx - > is_stateless ?
& pixfmt_stateless_fwht : & pixfmt_fwht ;
if ( f - > type = = V4L2_BUF_TYPE_VIDEO_OUTPUT ) {
coded_w = f - > fmt . pix . width ;
coded_h = f - > fmt . pix . height ;
} else {
coded_w = f - > fmt . pix_mp . width ;
coded_h = f - > fmt . pix_mp . height ;
}
if ( vb2_is_busy ( vq ) & & ( coded_w ! = q_data - > coded_width | |
coded_h ! = q_data - > coded_height ) )
return - EBUSY ;
size = coded_w * coded_h *
info - > sizeimage_mult / info - > sizeimage_div ;
if ( ! ctx - > is_stateless )
size + = sizeof ( struct fwht_cframe_hdr ) ;
if ( vb2_is_busy ( vq_cap ) & & size > q_data_cap - > sizeimage )
return - EBUSY ;
}
2018-07-20 03:52:50 -04:00
ret = vidioc_s_fmt ( file2ctx ( file ) , f ) ;
if ( ! ret ) {
2019-03-15 11:04:24 -04:00
if ( ctx - > is_enc ) {
q_data - > visible_width = coded_w ;
q_data - > visible_height = coded_h ;
q_data_cap - > coded_width = coded_w ;
q_data_cap - > coded_height = coded_h ;
q_data_cap - > sizeimage = size ;
}
2018-07-20 03:52:50 -04:00
switch ( f - > type ) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT :
pix = & f - > fmt . pix ;
2018-08-22 03:50:56 -04:00
ctx - > state . colorspace = pix - > colorspace ;
ctx - > state . xfer_func = pix - > xfer_func ;
ctx - > state . ycbcr_enc = pix - > ycbcr_enc ;
ctx - > state . quantization = pix - > quantization ;
2018-07-20 03:52:50 -04:00
break ;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
pix_mp = & f - > fmt . pix_mp ;
2018-08-22 03:50:56 -04:00
ctx - > state . colorspace = pix_mp - > colorspace ;
ctx - > state . xfer_func = pix_mp - > xfer_func ;
ctx - > state . ycbcr_enc = pix_mp - > ycbcr_enc ;
ctx - > state . quantization = pix_mp - > quantization ;
2018-07-20 03:52:50 -04:00
break ;
default :
break ;
}
}
return ret ;
}
2019-01-21 09:46:14 -02:00
static int vidioc_g_selection ( struct file * file , void * priv ,
struct v4l2_selection * s )
{
struct vicodec_ctx * ctx = file2ctx ( file ) ;
struct vicodec_q_data * q_data ;
2019-01-30 02:33:41 -05:00
2019-01-21 09:46:14 -02:00
q_data = get_q_data ( ctx , s - > type ) ;
if ( ! q_data )
return - EINVAL ;
/*
* encoder supports only cropping on the OUTPUT buffer
* decoder supports only composing on the CAPTURE buffer
*/
2019-03-12 07:53:55 -04:00
if ( ctx - > is_enc & & s - > type = = V4L2_BUF_TYPE_VIDEO_OUTPUT ) {
2019-01-21 09:46:14 -02:00
switch ( s - > target ) {
case V4L2_SEL_TGT_CROP :
s - > r . left = 0 ;
s - > r . top = 0 ;
s - > r . width = q_data - > visible_width ;
s - > r . height = q_data - > visible_height ;
return 0 ;
case V4L2_SEL_TGT_CROP_DEFAULT :
case V4L2_SEL_TGT_CROP_BOUNDS :
s - > r . left = 0 ;
2019-03-12 07:53:55 -04:00
s - > r . top = 0 ;
s - > r . width = q_data - > coded_width ;
s - > r . height = q_data - > coded_height ;
return 0 ;
}
} else if ( ! ctx - > is_enc & & s - > type = = V4L2_BUF_TYPE_VIDEO_CAPTURE ) {
switch ( s - > target ) {
case V4L2_SEL_TGT_COMPOSE :
s - > r . left = 0 ;
s - > r . top = 0 ;
s - > r . width = q_data - > visible_width ;
s - > r . height = q_data - > visible_height ;
return 0 ;
case V4L2_SEL_TGT_COMPOSE_DEFAULT :
case V4L2_SEL_TGT_COMPOSE_BOUNDS :
s - > r . left = 0 ;
2019-01-21 09:46:14 -02:00
s - > r . top = 0 ;
s - > r . width = q_data - > coded_width ;
s - > r . height = q_data - > coded_height ;
return 0 ;
}
}
return - EINVAL ;
}
static int vidioc_s_selection ( struct file * file , void * priv ,
struct v4l2_selection * s )
{
struct vicodec_ctx * ctx = file2ctx ( file ) ;
struct vicodec_q_data * q_data ;
2019-03-06 16:13:24 -05:00
if ( s - > type ! = V4L2_BUF_TYPE_VIDEO_OUTPUT )
2019-01-30 02:33:41 -05:00
return - EINVAL ;
2019-01-21 09:46:14 -02:00
q_data = get_q_data ( ctx , s - > type ) ;
if ( ! q_data )
return - EINVAL ;
2019-01-30 02:33:41 -05:00
if ( ! ctx - > is_enc | | s - > target ! = V4L2_SEL_TGT_CROP )
2019-01-21 09:46:14 -02:00
return - EINVAL ;
s - > r . left = 0 ;
s - > r . top = 0 ;
q_data - > visible_width = clamp ( s - > r . width , MIN_WIDTH ,
q_data - > coded_width ) ;
s - > r . width = q_data - > visible_width ;
q_data - > visible_height = clamp ( s - > r . height , MIN_HEIGHT ,
q_data - > coded_height ) ;
s - > r . height = q_data - > visible_height ;
return 0 ;
}
2019-03-25 07:27:45 -04:00
static int vicodec_mark_last_buf ( struct vicodec_ctx * ctx )
2018-07-20 03:52:50 -04:00
{
2019-03-25 07:27:45 -04:00
struct vb2_v4l2_buffer * next_dst_buf ;
int ret = 0 ;
2018-07-20 03:52:50 -04:00
spin_lock ( ctx - > lock ) ;
2019-03-25 07:27:45 -04:00
if ( ctx - > is_draining ) {
ret = - EBUSY ;
goto unlock ;
}
if ( ctx - > has_stopped )
goto unlock ;
2018-07-20 03:52:50 -04:00
ctx - > last_src_buf = v4l2_m2m_last_src_buf ( ctx - > fh . m2m_ctx ) ;
2019-03-25 07:27:45 -04:00
ctx - > is_draining = true ;
if ( ctx - > last_src_buf )
goto unlock ;
next_dst_buf = v4l2_m2m_dst_buf_remove ( ctx - > fh . m2m_ctx ) ;
if ( ! next_dst_buf ) {
ctx - > next_is_last = true ;
goto unlock ;
2018-07-20 03:52:50 -04:00
}
2019-03-25 07:27:45 -04:00
next_dst_buf - > flags | = V4L2_BUF_FLAG_LAST ;
vb2_buffer_done ( & next_dst_buf - > vb2_buf , VB2_BUF_STATE_DONE ) ;
ctx - > is_draining = false ;
ctx - > has_stopped = true ;
v4l2_event_queue_fh ( & ctx - > fh , & vicodec_eos_event ) ;
unlock :
2018-07-20 03:52:50 -04:00
spin_unlock ( ctx - > lock ) ;
2019-03-25 07:27:45 -04:00
return ret ;
2018-07-20 03:52:50 -04:00
}
static int vicodec_encoder_cmd ( struct file * file , void * fh ,
struct v4l2_encoder_cmd * ec )
{
struct vicodec_ctx * ctx = file2ctx ( file ) ;
int ret ;
2019-05-28 04:34:37 -04:00
ret = v4l2_m2m_ioctl_try_encoder_cmd ( file , fh , ec ) ;
2018-07-20 03:52:50 -04:00
if ( ret < 0 )
return ret ;
2019-03-25 07:27:45 -04:00
if ( ! vb2_is_streaming ( & ctx - > fh . m2m_ctx - > cap_q_ctx . q ) | |
! vb2_is_streaming ( & ctx - > fh . m2m_ctx - > out_q_ctx . q ) )
return 0 ;
if ( ec - > cmd = = V4L2_ENC_CMD_STOP )
return vicodec_mark_last_buf ( ctx ) ;
ret = 0 ;
spin_lock ( ctx - > lock ) ;
if ( ctx - > is_draining ) {
ret = - EBUSY ;
} else if ( ctx - > has_stopped ) {
ctx - > has_stopped = false ;
vb2_clear_last_buffer_dequeued ( & ctx - > fh . m2m_ctx - > cap_q_ctx . q ) ;
}
spin_unlock ( ctx - > lock ) ;
return ret ;
2018-07-20 03:52:50 -04:00
}
static int vicodec_decoder_cmd ( struct file * file , void * fh ,
struct v4l2_decoder_cmd * dc )
{
struct vicodec_ctx * ctx = file2ctx ( file ) ;
int ret ;
2019-05-28 04:34:37 -04:00
ret = v4l2_m2m_ioctl_try_decoder_cmd ( file , fh , dc ) ;
2018-07-20 03:52:50 -04:00
if ( ret < 0 )
return ret ;
2019-03-25 07:27:45 -04:00
if ( ! vb2_is_streaming ( & ctx - > fh . m2m_ctx - > cap_q_ctx . q ) | |
! vb2_is_streaming ( & ctx - > fh . m2m_ctx - > out_q_ctx . q ) )
return 0 ;
if ( dc - > cmd = = V4L2_DEC_CMD_STOP )
return vicodec_mark_last_buf ( ctx ) ;
ret = 0 ;
spin_lock ( ctx - > lock ) ;
if ( ctx - > is_draining ) {
ret = - EBUSY ;
} else if ( ctx - > has_stopped ) {
ctx - > has_stopped = false ;
vb2_clear_last_buffer_dequeued ( & ctx - > fh . m2m_ctx - > cap_q_ctx . q ) ;
}
spin_unlock ( ctx - > lock ) ;
return ret ;
2018-07-20 03:52:50 -04:00
}
static int vicodec_enum_framesizes ( struct file * file , void * fh ,
struct v4l2_frmsizeenum * fsize )
{
switch ( fsize - > pixel_format ) {
2019-03-06 16:13:43 -05:00
case V4L2_PIX_FMT_FWHT_STATELESS :
break ;
2018-07-20 03:52:50 -04:00
case V4L2_PIX_FMT_FWHT :
break ;
default :
2018-08-19 10:58:55 -04:00
if ( find_fmt ( fsize - > pixel_format ) - > id = = fsize - > pixel_format )
2018-07-20 03:52:50 -04:00
break ;
return - EINVAL ;
}
if ( fsize - > index )
return - EINVAL ;
fsize - > type = V4L2_FRMSIZE_TYPE_STEPWISE ;
fsize - > stepwise . min_width = MIN_WIDTH ;
fsize - > stepwise . max_width = MAX_WIDTH ;
fsize - > stepwise . step_width = 8 ;
fsize - > stepwise . min_height = MIN_HEIGHT ;
fsize - > stepwise . max_height = MAX_HEIGHT ;
fsize - > stepwise . step_height = 8 ;
return 0 ;
}
static int vicodec_subscribe_event ( struct v4l2_fh * fh ,
const struct v4l2_event_subscription * sub )
{
2019-01-31 08:21:44 -05:00
struct vicodec_ctx * ctx = container_of ( fh , struct vicodec_ctx , fh ) ;
2018-07-20 03:52:50 -04:00
switch ( sub - > type ) {
2019-01-21 09:46:18 -02:00
case V4L2_EVENT_SOURCE_CHANGE :
2019-01-31 08:21:44 -05:00
if ( ctx - > is_enc )
return - EINVAL ;
/* fall through */
case V4L2_EVENT_EOS :
2019-06-06 06:00:30 -04:00
if ( ctx - > is_stateless )
return - EINVAL ;
2018-07-20 03:52:50 -04:00
return v4l2_event_subscribe ( fh , sub , 0 , NULL ) ;
default :
return v4l2_ctrl_subscribe_event ( fh , sub ) ;
}
}
static const struct v4l2_ioctl_ops vicodec_ioctl_ops = {
. vidioc_querycap = vidioc_querycap ,
. vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap ,
. vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap ,
. vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap ,
. vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap ,
. vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap ,
. vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap ,
. vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap ,
. vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out ,
. vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out ,
. vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out ,
. vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out ,
. vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out ,
. vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out ,
. vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out ,
. vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs ,
. vidioc_querybuf = v4l2_m2m_ioctl_querybuf ,
. vidioc_qbuf = v4l2_m2m_ioctl_qbuf ,
. vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf ,
. vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf ,
. vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs ,
. vidioc_expbuf = v4l2_m2m_ioctl_expbuf ,
. vidioc_streamon = v4l2_m2m_ioctl_streamon ,
. vidioc_streamoff = v4l2_m2m_ioctl_streamoff ,
2019-01-21 09:46:14 -02:00
. vidioc_g_selection = vidioc_g_selection ,
. vidioc_s_selection = vidioc_s_selection ,
2019-05-28 04:34:37 -04:00
. vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd ,
2018-07-20 03:52:50 -04:00
. vidioc_encoder_cmd = vicodec_encoder_cmd ,
2019-05-28 04:34:37 -04:00
. vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd ,
2018-07-20 03:52:50 -04:00
. vidioc_decoder_cmd = vicodec_decoder_cmd ,
. vidioc_enum_framesizes = vicodec_enum_framesizes ,
. vidioc_subscribe_event = vicodec_subscribe_event ,
. vidioc_unsubscribe_event = v4l2_event_unsubscribe ,
} ;
/*
* Queue operations
*/
static int vicodec_queue_setup ( struct vb2_queue * vq , unsigned int * nbuffers ,
unsigned int * nplanes , unsigned int sizes [ ] ,
struct device * alloc_devs [ ] )
{
struct vicodec_ctx * ctx = vb2_get_drv_priv ( vq ) ;
struct vicodec_q_data * q_data = get_q_data ( ctx , vq - > type ) ;
unsigned int size = q_data - > sizeimage ;
if ( * nplanes )
return sizes [ 0 ] < size ? - EINVAL : 0 ;
* nplanes = 1 ;
sizes [ 0 ] = size ;
2019-05-07 05:30:24 -04:00
q_data - > vb2_sizeimage = size ;
2018-07-20 03:52:50 -04:00
return 0 ;
}
2019-03-06 16:13:42 -05:00
static int vicodec_buf_out_validate ( struct vb2_buffer * vb )
{
struct vb2_v4l2_buffer * vbuf = to_vb2_v4l2_buffer ( vb ) ;
vbuf - > field = V4L2_FIELD_NONE ;
return 0 ;
}
2018-07-20 03:52:50 -04:00
static int vicodec_buf_prepare ( struct vb2_buffer * vb )
{
struct vb2_v4l2_buffer * vbuf = to_vb2_v4l2_buffer ( vb ) ;
struct vicodec_ctx * ctx = vb2_get_drv_priv ( vb - > vb2_queue ) ;
struct vicodec_q_data * q_data ;
dprintk ( ctx - > dev , " type: %d \n " , vb - > vb2_queue - > type ) ;
q_data = get_q_data ( ctx , vb - > vb2_queue - > type ) ;
if ( V4L2_TYPE_IS_OUTPUT ( vb - > vb2_queue - > type ) ) {
if ( vbuf - > field = = V4L2_FIELD_ANY )
vbuf - > field = V4L2_FIELD_NONE ;
if ( vbuf - > field ! = V4L2_FIELD_NONE ) {
dprintk ( ctx - > dev , " %s field isn't supported \n " ,
__func__ ) ;
return - EINVAL ;
}
}
2019-05-07 05:30:24 -04:00
if ( vb2_plane_size ( vb , 0 ) < q_data - > vb2_sizeimage ) {
2018-07-20 03:52:50 -04:00
dprintk ( ctx - > dev ,
" %s data will not fit into plane (%lu < %lu) \n " ,
__func__ , vb2_plane_size ( vb , 0 ) ,
2019-05-07 05:30:24 -04:00
( long ) q_data - > vb2_sizeimage ) ;
2018-07-20 03:52:50 -04:00
return - EINVAL ;
}
return 0 ;
}
static void vicodec_buf_queue ( struct vb2_buffer * vb )
{
struct vb2_v4l2_buffer * vbuf = to_vb2_v4l2_buffer ( vb ) ;
struct vicodec_ctx * ctx = vb2_get_drv_priv ( vb - > vb2_queue ) ;
2019-01-21 09:46:18 -02:00
unsigned int sz = vb2_get_plane_payload ( & vbuf - > vb2_buf , 0 ) ;
u8 * p_src = vb2_plane_vaddr ( & vbuf - > vb2_buf , 0 ) ;
u8 * p = p_src ;
struct vb2_queue * vq_out = v4l2_m2m_get_vq ( ctx - > fh . m2m_ctx ,
V4L2_BUF_TYPE_VIDEO_OUTPUT ) ;
struct vb2_queue * vq_cap = v4l2_m2m_get_vq ( ctx - > fh . m2m_ctx ,
V4L2_BUF_TYPE_VIDEO_CAPTURE ) ;
bool header_valid = false ;
static const struct v4l2_event rs_event = {
. type = V4L2_EVENT_SOURCE_CHANGE ,
. u . src_change . changes = V4L2_EVENT_SRC_CH_RESOLUTION ,
} ;
2019-03-25 07:27:45 -04:00
if ( vb2_is_streaming ( vq_cap ) ) {
if ( ! V4L2_TYPE_IS_OUTPUT ( vb - > vb2_queue - > type ) & &
ctx - > next_is_last ) {
unsigned int i ;
for ( i = 0 ; i < vb - > num_planes ; i + + )
vb - > planes [ i ] . bytesused = 0 ;
vbuf - > flags = V4L2_BUF_FLAG_LAST ;
vbuf - > field = V4L2_FIELD_NONE ;
vbuf - > sequence = get_q_data ( ctx , vb - > vb2_queue - > type ) - > sequence + + ;
vb2_buffer_done ( vb , VB2_BUF_STATE_DONE ) ;
ctx - > is_draining = false ;
ctx - > has_stopped = true ;
ctx - > next_is_last = false ;
v4l2_event_queue_fh ( & ctx - > fh , & vicodec_eos_event ) ;
return ;
}
}
2019-01-21 09:46:18 -02:00
/* buf_queue handles only the first source change event */
if ( ctx - > first_source_change_sent ) {
v4l2_m2m_buf_queue ( ctx - > fh . m2m_ctx , vbuf ) ;
return ;
}
/*
* if both queues are streaming , the source change event is
* handled in job_ready
*/
if ( vb2_is_streaming ( vq_cap ) & & vb2_is_streaming ( vq_out ) ) {
v4l2_m2m_buf_queue ( ctx - > fh . m2m_ctx , vbuf ) ;
return ;
}
/*
2019-03-06 16:13:42 -05:00
* source change event is relevant only for the stateful decoder
2019-01-21 09:46:18 -02:00
* in the compressed stream
*/
2019-03-06 16:13:42 -05:00
if ( ctx - > is_stateless | | ctx - > is_enc | |
! V4L2_TYPE_IS_OUTPUT ( vb - > vb2_queue - > type ) ) {
2019-01-21 09:46:18 -02:00
v4l2_m2m_buf_queue ( ctx - > fh . m2m_ctx , vbuf ) ;
return ;
}
do {
enum vb2_buffer_state state =
get_next_header ( ctx , & p , p_src + sz - p ) ;
if ( ctx - > header_size < sizeof ( struct fwht_cframe_hdr ) ) {
v4l2_m2m_buf_done ( vbuf , state ) ;
return ;
}
header_valid = is_header_valid ( & ctx - > state . header ) ;
/*
* p points right after the end of the header in the
* buffer . If the header is invalid we set p to point
* to the next byte after the start of the header
*/
if ( ! header_valid ) {
p = p - sizeof ( struct fwht_cframe_hdr ) + 1 ;
if ( p < p_src )
p = p_src ;
ctx - > header_size = 0 ;
ctx - > comp_magic_cnt = 0 ;
}
2018-07-20 03:52:50 -04:00
2019-01-21 09:46:18 -02:00
} while ( ! header_valid ) ;
ctx - > cur_buf_offset = p - p_src ;
update_capture_data_from_header ( ctx ) ;
ctx - > first_source_change_sent = true ;
v4l2_event_queue_fh ( & ctx - > fh , & rs_event ) ;
2018-07-20 03:52:50 -04:00
v4l2_m2m_buf_queue ( ctx - > fh . m2m_ctx , vbuf ) ;
}
static void vicodec_return_bufs ( struct vb2_queue * q , u32 state )
{
struct vicodec_ctx * ctx = vb2_get_drv_priv ( q ) ;
struct vb2_v4l2_buffer * vbuf ;
for ( ; ; ) {
if ( V4L2_TYPE_IS_OUTPUT ( q - > type ) )
vbuf = v4l2_m2m_src_buf_remove ( ctx - > fh . m2m_ctx ) ;
else
vbuf = v4l2_m2m_dst_buf_remove ( ctx - > fh . m2m_ctx ) ;
if ( vbuf = = NULL )
return ;
2019-03-06 16:13:42 -05:00
v4l2_ctrl_request_complete ( vbuf - > vb2_buf . req_obj . req ,
& ctx - > hdl ) ;
2018-07-20 03:52:50 -04:00
spin_lock ( ctx - > lock ) ;
v4l2_m2m_buf_done ( vbuf , state ) ;
spin_unlock ( ctx - > lock ) ;
}
}
2019-03-06 16:13:42 -05:00
static unsigned int total_frame_size ( struct vicodec_q_data * q_data )
{
unsigned int size ;
unsigned int chroma_div ;
if ( ! q_data - > info ) {
WARN_ON ( 1 ) ;
return 0 ;
}
size = q_data - > coded_width * q_data - > coded_height ;
chroma_div = q_data - > info - > width_div * q_data - > info - > height_div ;
if ( q_data - > info - > components_num = = 4 )
return 2 * size + 2 * ( size / chroma_div ) ;
else if ( q_data - > info - > components_num = = 3 )
return size + 2 * ( size / chroma_div ) ;
return size ;
}
2018-07-20 03:52:50 -04:00
static int vicodec_start_streaming ( struct vb2_queue * q ,
unsigned int count )
{
struct vicodec_ctx * ctx = vb2_get_drv_priv ( q ) ;
struct vicodec_q_data * q_data = get_q_data ( ctx , q - > type ) ;
2018-08-22 03:50:56 -04:00
struct v4l2_fwht_state * state = & ctx - > state ;
const struct v4l2_fwht_pixfmt_info * info = q_data - > info ;
2019-01-21 09:46:14 -02:00
unsigned int size = q_data - > coded_width * q_data - > coded_height ;
2019-01-21 09:46:18 -02:00
unsigned int chroma_div ;
2018-11-15 06:23:31 -05:00
unsigned int total_planes_size ;
2019-03-06 16:13:42 -05:00
u8 * new_comp_frame = NULL ;
2018-11-15 06:23:31 -05:00
2019-01-21 09:46:18 -02:00
chroma_div = info - > width_div * info - > height_div ;
q_data - > sequence = 0 ;
2019-03-13 10:47:13 -04:00
if ( V4L2_TYPE_IS_OUTPUT ( q - > type ) )
ctx - > last_src_buf = NULL ;
2019-01-21 09:46:18 -02:00
state - > gop_cnt = 0 ;
if ( ( V4L2_TYPE_IS_OUTPUT ( q - > type ) & & ! ctx - > is_enc ) | |
( ! V4L2_TYPE_IS_OUTPUT ( q - > type ) & & ctx - > is_enc ) )
return 0 ;
2019-03-06 16:13:43 -05:00
if ( info - > id = = V4L2_PIX_FMT_FWHT | |
info - > id = = V4L2_PIX_FMT_FWHT_STATELESS ) {
2019-01-21 09:46:18 -02:00
vicodec_return_bufs ( q , VB2_BUF_STATE_QUEUED ) ;
return - EINVAL ;
}
2019-03-06 16:13:42 -05:00
total_planes_size = total_frame_size ( q_data ) ;
ctx - > comp_max_size = total_planes_size ;
2018-07-20 03:52:50 -04:00
2019-01-21 09:46:18 -02:00
state - > visible_width = q_data - > visible_width ;
state - > visible_height = q_data - > visible_height ;
state - > coded_width = q_data - > coded_width ;
state - > coded_height = q_data - > coded_height ;
state - > stride = q_data - > coded_width *
info - > bytesperline_mult ;
2018-07-20 03:52:50 -04:00
2019-03-06 16:13:42 -05:00
if ( ctx - > is_stateless ) {
state - > ref_stride = state - > stride ;
return 0 ;
}
2019-03-06 16:13:32 -05:00
state - > ref_stride = q_data - > coded_width * info - > luma_alpha_step ;
2019-03-06 16:13:42 -05:00
2019-03-06 16:13:31 -05:00
state - > ref_frame . buf = kvmalloc ( total_planes_size , GFP_KERNEL ) ;
state - > ref_frame . luma = state - > ref_frame . buf ;
2019-01-21 09:46:18 -02:00
new_comp_frame = kvmalloc ( ctx - > comp_max_size , GFP_KERNEL ) ;
if ( ! state - > ref_frame . luma | | ! new_comp_frame ) {
2018-08-22 03:50:56 -04:00
kvfree ( state - > ref_frame . luma ) ;
2019-01-21 09:46:18 -02:00
kvfree ( new_comp_frame ) ;
2018-07-20 03:52:50 -04:00
vicodec_return_bufs ( q , VB2_BUF_STATE_QUEUED ) ;
return - ENOMEM ;
}
2019-01-21 09:46:18 -02:00
/*
* if state - > compressed_frame was already allocated then
* it contain data of the first frame of the new resolution
*/
if ( state - > compressed_frame ) {
if ( ctx - > comp_size > ctx - > comp_max_size )
ctx - > comp_size = ctx - > comp_max_size ;
memcpy ( new_comp_frame ,
state - > compressed_frame , ctx - > comp_size ) ;
}
kvfree ( state - > compressed_frame ) ;
state - > compressed_frame = new_comp_frame ;
if ( info - > components_num > = 3 ) {
2018-11-15 06:23:31 -05:00
state - > ref_frame . cb = state - > ref_frame . luma + size ;
state - > ref_frame . cr = state - > ref_frame . cb + size / chroma_div ;
} else {
state - > ref_frame . cb = NULL ;
state - > ref_frame . cr = NULL ;
}
2018-11-15 06:23:32 -05:00
2019-01-21 09:46:18 -02:00
if ( info - > components_num = = 4 )
2018-11-15 06:23:32 -05:00
state - > ref_frame . alpha =
state - > ref_frame . cr + size / chroma_div ;
else
state - > ref_frame . alpha = NULL ;
2018-07-20 03:52:50 -04:00
return 0 ;
}
static void vicodec_stop_streaming ( struct vb2_queue * q )
{
struct vicodec_ctx * ctx = vb2_get_drv_priv ( q ) ;
vicodec_return_bufs ( q , VB2_BUF_STATE_ERROR ) ;
2019-03-25 07:27:45 -04:00
if ( V4L2_TYPE_IS_OUTPUT ( q - > type ) ) {
if ( ctx - > is_draining ) {
struct vb2_v4l2_buffer * next_dst_buf ;
spin_lock ( ctx - > lock ) ;
ctx - > last_src_buf = NULL ;
next_dst_buf = v4l2_m2m_dst_buf_remove ( ctx - > fh . m2m_ctx ) ;
if ( ! next_dst_buf ) {
ctx - > next_is_last = true ;
} else {
next_dst_buf - > flags | = V4L2_BUF_FLAG_LAST ;
vb2_buffer_done ( & next_dst_buf - > vb2_buf , VB2_BUF_STATE_DONE ) ;
ctx - > is_draining = false ;
ctx - > has_stopped = true ;
v4l2_event_queue_fh ( & ctx - > fh , & vicodec_eos_event ) ;
}
spin_unlock ( ctx - > lock ) ;
}
} else {
ctx - > is_draining = false ;
ctx - > has_stopped = false ;
ctx - > next_is_last = false ;
}
if ( ! ctx - > is_enc & & V4L2_TYPE_IS_OUTPUT ( q - > type ) )
ctx - > first_source_change_sent = false ;
2019-01-21 09:46:18 -02:00
if ( ( ! V4L2_TYPE_IS_OUTPUT ( q - > type ) & & ! ctx - > is_enc ) | |
( V4L2_TYPE_IS_OUTPUT ( q - > type ) & & ctx - > is_enc ) ) {
2019-03-06 16:13:42 -05:00
if ( ! ctx - > is_stateless )
kvfree ( ctx - > state . ref_frame . buf ) ;
2019-03-06 16:13:31 -05:00
ctx - > state . ref_frame . buf = NULL ;
ctx - > state . ref_frame . luma = NULL ;
2019-01-21 09:46:18 -02:00
ctx - > comp_max_size = 0 ;
ctx - > source_changed = false ;
}
if ( V4L2_TYPE_IS_OUTPUT ( q - > type ) & & ! ctx - > is_enc ) {
ctx - > cur_buf_offset = 0 ;
ctx - > comp_size = 0 ;
ctx - > header_size = 0 ;
ctx - > comp_magic_cnt = 0 ;
ctx - > comp_has_frame = 0 ;
ctx - > comp_has_next_frame = 0 ;
}
2018-07-20 03:52:50 -04:00
}
2019-03-06 16:13:42 -05:00
static void vicodec_buf_request_complete ( struct vb2_buffer * vb )
{
struct vicodec_ctx * ctx = vb2_get_drv_priv ( vb - > vb2_queue ) ;
v4l2_ctrl_request_complete ( vb - > req_obj . req , & ctx - > hdl ) ;
}
2018-07-20 03:52:50 -04:00
static const struct vb2_ops vicodec_qops = {
2019-03-06 16:13:42 -05:00
. queue_setup = vicodec_queue_setup ,
. buf_out_validate = vicodec_buf_out_validate ,
. buf_prepare = vicodec_buf_prepare ,
. buf_queue = vicodec_buf_queue ,
. buf_request_complete = vicodec_buf_request_complete ,
. start_streaming = vicodec_start_streaming ,
. stop_streaming = vicodec_stop_streaming ,
. wait_prepare = vb2_ops_wait_prepare ,
. wait_finish = vb2_ops_wait_finish ,
2018-07-20 03:52:50 -04:00
} ;
static int queue_init ( void * priv , struct vb2_queue * src_vq ,
struct vb2_queue * dst_vq )
{
struct vicodec_ctx * ctx = priv ;
int ret ;
src_vq - > type = ( multiplanar ?
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
V4L2_BUF_TYPE_VIDEO_OUTPUT ) ;
src_vq - > io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF ;
src_vq - > drv_priv = ctx ;
src_vq - > buf_struct_size = sizeof ( struct v4l2_m2m_buffer ) ;
src_vq - > ops = & vicodec_qops ;
src_vq - > mem_ops = & vb2_vmalloc_memops ;
src_vq - > timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY ;
2019-03-06 16:13:41 -05:00
if ( ctx - > is_enc )
src_vq - > lock = & ctx - > dev - > stateful_enc . mutex ;
else if ( ctx - > is_stateless )
src_vq - > lock = & ctx - > dev - > stateless_dec . mutex ;
else
src_vq - > lock = & ctx - > dev - > stateful_dec . mutex ;
src_vq - > supports_requests = ctx - > is_stateless ;
src_vq - > requires_requests = ctx - > is_stateless ;
2018-07-20 03:52:50 -04:00
ret = vb2_queue_init ( src_vq ) ;
if ( ret )
return ret ;
dst_vq - > type = ( multiplanar ?
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
V4L2_BUF_TYPE_VIDEO_CAPTURE ) ;
dst_vq - > io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF ;
dst_vq - > drv_priv = ctx ;
dst_vq - > buf_struct_size = sizeof ( struct v4l2_m2m_buffer ) ;
dst_vq - > ops = & vicodec_qops ;
dst_vq - > mem_ops = & vb2_vmalloc_memops ;
dst_vq - > timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY ;
dst_vq - > lock = src_vq - > lock ;
return vb2_queue_init ( dst_vq ) ;
}
2019-03-06 16:13:42 -05:00
static int vicodec_try_ctrl ( struct v4l2_ctrl * ctrl )
{
struct vicodec_ctx * ctx = container_of ( ctrl - > handler ,
struct vicodec_ctx , hdl ) ;
const struct v4l2_ctrl_fwht_params * params ;
struct vicodec_q_data * q_dst = get_q_data ( ctx ,
V4L2_BUF_TYPE_VIDEO_CAPTURE ) ;
switch ( ctrl - > id ) {
case V4L2_CID_MPEG_VIDEO_FWHT_PARAMS :
if ( ! q_dst - > info )
return - EINVAL ;
params = ctrl - > p_new . p_fwht_params ;
if ( params - > width > q_dst - > coded_width | |
params - > width < MIN_WIDTH | |
params - > height > q_dst - > coded_height | |
params - > height < MIN_HEIGHT )
return - EINVAL ;
if ( ! validate_by_version ( params - > flags , params - > version ) )
return - EINVAL ;
if ( ! validate_stateless_params_flags ( params , q_dst - > info ) )
return - EINVAL ;
return 0 ;
default :
return 0 ;
}
return 0 ;
}
static void update_header_from_stateless_params ( struct vicodec_ctx * ctx ,
const struct v4l2_ctrl_fwht_params * params )
{
struct fwht_cframe_hdr * p_hdr = & ctx - > state . header ;
p_hdr - > magic1 = FWHT_MAGIC1 ;
p_hdr - > magic2 = FWHT_MAGIC2 ;
p_hdr - > version = htonl ( params - > version ) ;
p_hdr - > width = htonl ( params - > width ) ;
p_hdr - > height = htonl ( params - > height ) ;
p_hdr - > flags = htonl ( params - > flags ) ;
p_hdr - > colorspace = htonl ( params - > colorspace ) ;
p_hdr - > xfer_func = htonl ( params - > xfer_func ) ;
p_hdr - > ycbcr_enc = htonl ( params - > ycbcr_enc ) ;
p_hdr - > quantization = htonl ( params - > quantization ) ;
}
2018-08-19 10:18:11 -04:00
static int vicodec_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct vicodec_ctx * ctx = container_of ( ctrl - > handler ,
struct vicodec_ctx , hdl ) ;
2019-03-06 16:13:42 -05:00
const struct v4l2_ctrl_fwht_params * params ;
2018-08-19 10:18:11 -04:00
switch ( ctrl - > id ) {
case V4L2_CID_MPEG_VIDEO_GOP_SIZE :
2018-08-22 03:50:56 -04:00
ctx - > state . gop_size = ctrl - > val ;
2018-08-19 10:18:11 -04:00
return 0 ;
2019-03-06 16:13:40 -05:00
case V4L2_CID_FWHT_I_FRAME_QP :
2018-08-22 03:50:56 -04:00
ctx - > state . i_frame_qp = ctrl - > val ;
2018-08-19 10:18:11 -04:00
return 0 ;
2019-03-06 16:13:40 -05:00
case V4L2_CID_FWHT_P_FRAME_QP :
2018-08-22 03:50:56 -04:00
ctx - > state . p_frame_qp = ctrl - > val ;
2018-08-19 10:18:11 -04:00
return 0 ;
2019-03-06 16:13:42 -05:00
case V4L2_CID_MPEG_VIDEO_FWHT_PARAMS :
params = ctrl - > p_new . p_fwht_params ;
update_header_from_stateless_params ( ctx , params ) ;
ctx - > state . ref_frame_ts = params - > backward_ref_ts ;
return 0 ;
2018-08-19 10:18:11 -04:00
}
return - EINVAL ;
}
2018-10-27 08:16:39 -04:00
static const struct v4l2_ctrl_ops vicodec_ctrl_ops = {
2018-08-19 10:18:11 -04:00
. s_ctrl = vicodec_s_ctrl ,
2019-03-06 16:13:42 -05:00
. try_ctrl = vicodec_try_ctrl ,
2018-08-19 10:18:11 -04:00
} ;
2019-03-06 16:13:40 -05:00
static const struct v4l2_ctrl_config vicodec_ctrl_stateless_state = {
2019-03-06 16:13:42 -05:00
. ops = & vicodec_ctrl_ops ,
2019-03-06 16:13:40 -05:00
. id = V4L2_CID_MPEG_VIDEO_FWHT_PARAMS ,
. elem_size = sizeof ( struct v4l2_ctrl_fwht_params ) ,
2018-08-19 10:18:11 -04:00
} ;
2018-07-20 03:52:50 -04:00
/*
* File operations
*/
static int vicodec_open ( struct file * file )
{
2019-03-13 10:48:15 -04:00
const struct v4l2_fwht_pixfmt_info * info = v4l2_fwht_get_pixfmt ( 0 ) ;
2018-07-20 03:52:50 -04:00
struct video_device * vfd = video_devdata ( file ) ;
struct vicodec_dev * dev = video_drvdata ( file ) ;
struct vicodec_ctx * ctx = NULL ;
struct v4l2_ctrl_handler * hdl ;
2019-03-13 10:48:15 -04:00
unsigned int raw_size ;
unsigned int comp_size ;
2018-07-20 03:52:50 -04:00
int rc = 0 ;
if ( mutex_lock_interruptible ( vfd - > lock ) )
return - ERESTARTSYS ;
ctx = kzalloc ( sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ! ctx ) {
rc = - ENOMEM ;
goto open_unlock ;
}
2019-03-06 16:13:36 -05:00
if ( vfd = = & dev - > stateful_enc . vfd )
2018-07-20 03:52:50 -04:00
ctx - > is_enc = true ;
2019-03-06 16:13:41 -05:00
else if ( vfd = = & dev - > stateless_dec . vfd )
ctx - > is_stateless = true ;
2018-07-20 03:52:50 -04:00
v4l2_fh_init ( & ctx - > fh , video_devdata ( file ) ) ;
file - > private_data = & ctx - > fh ;
ctx - > dev = dev ;
hdl = & ctx - > hdl ;
2019-03-20 07:31:39 -04:00
v4l2_ctrl_handler_init ( hdl , 5 ) ;
2018-08-19 10:18:11 -04:00
v4l2_ctrl_new_std ( hdl , & vicodec_ctrl_ops , V4L2_CID_MPEG_VIDEO_GOP_SIZE ,
1 , 16 , 1 , 10 ) ;
2019-03-06 16:13:40 -05:00
v4l2_ctrl_new_std ( hdl , & vicodec_ctrl_ops , V4L2_CID_FWHT_I_FRAME_QP ,
1 , 31 , 1 , 20 ) ;
v4l2_ctrl_new_std ( hdl , & vicodec_ctrl_ops , V4L2_CID_FWHT_P_FRAME_QP ,
1 , 31 , 1 , 20 ) ;
2019-03-20 07:31:39 -04:00
if ( ctx - > is_enc )
v4l2_ctrl_new_std ( hdl , & vicodec_ctrl_ops ,
V4L2_CID_MIN_BUFFERS_FOR_OUTPUT , 1 , 1 , 1 , 1 ) ;
2019-03-06 16:13:41 -05:00
if ( ctx - > is_stateless )
v4l2_ctrl_new_custom ( hdl , & vicodec_ctrl_stateless_state , NULL ) ;
2018-07-20 03:52:50 -04:00
if ( hdl - > error ) {
rc = hdl - > error ;
v4l2_ctrl_handler_free ( hdl ) ;
kfree ( ctx ) ;
goto open_unlock ;
}
ctx - > fh . ctrl_handler = hdl ;
v4l2_ctrl_handler_setup ( hdl ) ;
2019-03-06 16:13:43 -05:00
if ( ctx - > is_enc )
2019-03-13 10:48:15 -04:00
ctx - > q_data [ V4L2_M2M_SRC ] . info = info ;
2019-03-06 16:13:43 -05:00
else if ( ctx - > is_stateless )
ctx - > q_data [ V4L2_M2M_SRC ] . info = & pixfmt_stateless_fwht ;
else
ctx - > q_data [ V4L2_M2M_SRC ] . info = & pixfmt_fwht ;
2019-01-21 09:46:14 -02:00
ctx - > q_data [ V4L2_M2M_SRC ] . coded_width = 1280 ;
ctx - > q_data [ V4L2_M2M_SRC ] . coded_height = 720 ;
ctx - > q_data [ V4L2_M2M_SRC ] . visible_width = 1280 ;
ctx - > q_data [ V4L2_M2M_SRC ] . visible_height = 720 ;
2019-03-13 10:48:15 -04:00
raw_size = 1280 * 720 * info - > sizeimage_mult / info - > sizeimage_div ;
comp_size = 1280 * 720 * pixfmt_fwht . sizeimage_mult /
pixfmt_fwht . sizeimage_div ;
2019-06-06 05:23:42 -04:00
if ( ctx - > is_enc )
2019-03-13 10:48:15 -04:00
ctx - > q_data [ V4L2_M2M_SRC ] . sizeimage = raw_size ;
2019-06-06 05:23:42 -04:00
else if ( ctx - > is_stateless )
ctx - > q_data [ V4L2_M2M_SRC ] . sizeimage = comp_size ;
2018-09-01 08:38:07 -04:00
else
ctx - > q_data [ V4L2_M2M_SRC ] . sizeimage =
2019-03-13 10:48:15 -04:00
comp_size + sizeof ( struct fwht_cframe_hdr ) ;
ctx - > q_data [ V4L2_M2M_DST ] = ctx - > q_data [ V4L2_M2M_SRC ] ;
2019-01-21 09:46:18 -02:00
if ( ctx - > is_enc ) {
ctx - > q_data [ V4L2_M2M_DST ] . info = & pixfmt_fwht ;
2019-03-13 10:48:15 -04:00
ctx - > q_data [ V4L2_M2M_DST ] . sizeimage =
comp_size + sizeof ( struct fwht_cframe_hdr ) ;
2019-01-21 09:46:18 -02:00
} else {
2019-03-13 10:48:15 -04:00
ctx - > q_data [ V4L2_M2M_DST ] . info = info ;
ctx - > q_data [ V4L2_M2M_DST ] . sizeimage = raw_size ;
2019-01-21 09:46:18 -02:00
}
2018-08-22 03:50:56 -04:00
ctx - > state . colorspace = V4L2_COLORSPACE_REC709 ;
2018-07-20 03:52:50 -04:00
if ( ctx - > is_enc ) {
2019-03-06 16:13:36 -05:00
ctx - > fh . m2m_ctx = v4l2_m2m_ctx_init ( dev - > stateful_enc . m2m_dev ,
ctx , & queue_init ) ;
ctx - > lock = & dev - > stateful_enc . lock ;
2019-03-06 16:13:41 -05:00
} else if ( ctx - > is_stateless ) {
ctx - > fh . m2m_ctx = v4l2_m2m_ctx_init ( dev - > stateless_dec . m2m_dev ,
ctx , & queue_init ) ;
ctx - > lock = & dev - > stateless_dec . lock ;
2018-07-20 03:52:50 -04:00
} else {
2019-03-06 16:13:36 -05:00
ctx - > fh . m2m_ctx = v4l2_m2m_ctx_init ( dev - > stateful_dec . m2m_dev ,
ctx , & queue_init ) ;
ctx - > lock = & dev - > stateful_dec . lock ;
2018-07-20 03:52:50 -04:00
}
if ( IS_ERR ( ctx - > fh . m2m_ctx ) ) {
rc = PTR_ERR ( ctx - > fh . m2m_ctx ) ;
v4l2_ctrl_handler_free ( hdl ) ;
v4l2_fh_exit ( & ctx - > fh ) ;
kfree ( ctx ) ;
goto open_unlock ;
}
v4l2_fh_add ( & ctx - > fh ) ;
open_unlock :
mutex_unlock ( vfd - > lock ) ;
return rc ;
}
static int vicodec_release ( struct file * file )
{
struct video_device * vfd = video_devdata ( file ) ;
struct vicodec_ctx * ctx = file2ctx ( file ) ;
mutex_lock ( vfd - > lock ) ;
v4l2_m2m_ctx_release ( ctx - > fh . m2m_ctx ) ;
mutex_unlock ( vfd - > lock ) ;
2019-03-06 16:13:25 -05:00
v4l2_fh_del ( & ctx - > fh ) ;
v4l2_fh_exit ( & ctx - > fh ) ;
v4l2_ctrl_handler_free ( & ctx - > hdl ) ;
2019-03-06 16:13:29 -05:00
kvfree ( ctx - > state . compressed_frame ) ;
2018-07-20 03:52:50 -04:00
kfree ( ctx ) ;
return 0 ;
}
2019-03-06 16:13:42 -05:00
static int vicodec_request_validate ( struct media_request * req )
{
struct media_request_object * obj ;
struct v4l2_ctrl_handler * parent_hdl , * hdl ;
struct vicodec_ctx * ctx = NULL ;
struct v4l2_ctrl * ctrl ;
unsigned int count ;
list_for_each_entry ( obj , & req - > objects , list ) {
struct vb2_buffer * vb ;
if ( vb2_request_object_is_buffer ( obj ) ) {
vb = container_of ( obj , struct vb2_buffer , req_obj ) ;
ctx = vb2_get_drv_priv ( vb - > vb2_queue ) ;
break ;
}
}
if ( ! ctx ) {
pr_err ( " No buffer was provided with the request \n " ) ;
return - ENOENT ;
}
count = vb2_request_buffer_cnt ( req ) ;
if ( ! count ) {
v4l2_info ( & ctx - > dev - > v4l2_dev ,
" No buffer was provided with the request \n " ) ;
return - ENOENT ;
} else if ( count > 1 ) {
v4l2_info ( & ctx - > dev - > v4l2_dev ,
" More than one buffer was provided with the request \n " ) ;
return - EINVAL ;
}
parent_hdl = & ctx - > hdl ;
hdl = v4l2_ctrl_request_hdl_find ( req , parent_hdl ) ;
if ( ! hdl ) {
v4l2_info ( & ctx - > dev - > v4l2_dev , " Missing codec control \n " ) ;
return - ENOENT ;
}
ctrl = v4l2_ctrl_request_hdl_ctrl_find ( hdl ,
vicodec_ctrl_stateless_state . id ) ;
if ( ! ctrl ) {
v4l2_info ( & ctx - > dev - > v4l2_dev ,
" Missing required codec control \n " ) ;
return - ENOENT ;
}
return vb2_request_validate ( req ) ;
}
2018-07-20 03:52:50 -04:00
static const struct v4l2_file_operations vicodec_fops = {
. owner = THIS_MODULE ,
. open = vicodec_open ,
. release = vicodec_release ,
. poll = v4l2_m2m_fop_poll ,
. unlocked_ioctl = video_ioctl2 ,
. mmap = v4l2_m2m_fop_mmap ,
} ;
static const struct video_device vicodec_videodev = {
. name = VICODEC_NAME ,
. vfl_dir = VFL_DIR_M2M ,
. fops = & vicodec_fops ,
. ioctl_ops = & vicodec_ioctl_ops ,
. minor = - 1 ,
. release = video_device_release_empty ,
} ;
2019-03-06 16:13:42 -05:00
static const struct media_device_ops vicodec_m2m_media_ops = {
. req_validate = vicodec_request_validate ,
. req_queue = v4l2_m2m_request_queue ,
} ;
2018-07-20 03:52:50 -04:00
static const struct v4l2_m2m_ops m2m_ops = {
. device_run = device_run ,
. job_ready = job_ready ,
} ;
2019-03-06 16:13:36 -05:00
static int register_instance ( struct vicodec_dev * dev ,
struct vicodec_dev_instance * dev_instance ,
const char * name , bool is_enc )
{
struct video_device * vfd ;
int ret ;
spin_lock_init ( & dev_instance - > lock ) ;
mutex_init ( & dev_instance - > mutex ) ;
dev_instance - > m2m_dev = v4l2_m2m_init ( & m2m_ops ) ;
if ( IS_ERR ( dev_instance - > m2m_dev ) ) {
v4l2_err ( & dev - > v4l2_dev , " Failed to init vicodec enc device \n " ) ;
return PTR_ERR ( dev_instance - > m2m_dev ) ;
}
dev_instance - > vfd = vicodec_videodev ;
vfd = & dev_instance - > vfd ;
vfd - > lock = & dev_instance - > mutex ;
vfd - > v4l2_dev = & dev - > v4l2_dev ;
strscpy ( vfd - > name , name , sizeof ( vfd - > name ) ) ;
vfd - > device_caps = V4L2_CAP_STREAMING |
( multiplanar ? V4L2_CAP_VIDEO_M2M_MPLANE : V4L2_CAP_VIDEO_M2M ) ;
if ( is_enc ) {
v4l2_disable_ioctl ( vfd , VIDIOC_DECODER_CMD ) ;
v4l2_disable_ioctl ( vfd , VIDIOC_TRY_DECODER_CMD ) ;
} else {
v4l2_disable_ioctl ( vfd , VIDIOC_ENCODER_CMD ) ;
v4l2_disable_ioctl ( vfd , VIDIOC_TRY_ENCODER_CMD ) ;
}
video_set_drvdata ( vfd , dev ) ;
ret = video_register_device ( vfd , VFL_TYPE_GRABBER , 0 ) ;
if ( ret ) {
v4l2_err ( & dev - > v4l2_dev , " Failed to register video device '%s' \n " , name ) ;
v4l2_m2m_release ( dev_instance - > m2m_dev ) ;
return ret ;
}
v4l2_info ( & dev - > v4l2_dev , " Device '%s' registered as /dev/video%d \n " ,
name , vfd - > num ) ;
return 0 ;
}
2019-05-03 10:22:49 -04:00
static void vicodec_v4l2_dev_release ( struct v4l2_device * v4l2_dev )
{
struct vicodec_dev * dev = container_of ( v4l2_dev , struct vicodec_dev , v4l2_dev ) ;
v4l2_device_unregister ( & dev - > v4l2_dev ) ;
v4l2_m2m_release ( dev - > stateful_enc . m2m_dev ) ;
v4l2_m2m_release ( dev - > stateful_dec . m2m_dev ) ;
v4l2_m2m_release ( dev - > stateless_dec . m2m_dev ) ;
kfree ( dev ) ;
}
2018-07-20 03:52:50 -04:00
static int vicodec_probe ( struct platform_device * pdev )
{
struct vicodec_dev * dev ;
int ret ;
2019-05-03 10:22:49 -04:00
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
2018-07-20 03:52:50 -04:00
if ( ! dev )
return - ENOMEM ;
ret = v4l2_device_register ( & pdev - > dev , & dev - > v4l2_dev ) ;
if ( ret )
2019-05-03 10:22:49 -04:00
goto free_dev ;
dev - > v4l2_dev . release = vicodec_v4l2_dev_release ;
2018-07-20 03:52:50 -04:00
# ifdef CONFIG_MEDIA_CONTROLLER
dev - > mdev . dev = & pdev - > dev ;
2018-09-10 08:19:14 -04:00
strscpy ( dev - > mdev . model , " vicodec " , sizeof ( dev - > mdev . model ) ) ;
2019-01-30 08:38:27 -05:00
strscpy ( dev - > mdev . bus_info , " platform:vicodec " ,
sizeof ( dev - > mdev . bus_info ) ) ;
2018-07-20 03:52:50 -04:00
media_device_init ( & dev - > mdev ) ;
2019-03-06 16:13:42 -05:00
dev - > mdev . ops = & vicodec_m2m_media_ops ;
2018-07-20 03:52:50 -04:00
dev - > v4l2_dev . mdev = & dev - > mdev ;
# endif
platform_set_drvdata ( pdev , dev ) ;
2019-03-06 16:13:36 -05:00
if ( register_instance ( dev , & dev - > stateful_enc ,
" stateful-encoder " , true ) )
2018-07-20 03:52:50 -04:00
goto unreg_dev ;
2019-03-06 16:13:36 -05:00
if ( register_instance ( dev , & dev - > stateful_dec ,
" stateful-decoder " , false ) )
goto unreg_sf_enc ;
2018-07-20 03:52:50 -04:00
2019-03-06 16:13:41 -05:00
if ( register_instance ( dev , & dev - > stateless_dec ,
" stateless-decoder " , false ) )
goto unreg_sf_dec ;
2018-07-20 03:52:50 -04:00
# ifdef CONFIG_MEDIA_CONTROLLER
2019-03-06 16:13:36 -05:00
ret = v4l2_m2m_register_media_controller ( dev - > stateful_enc . m2m_dev ,
& dev - > stateful_enc . vfd ,
MEDIA_ENT_F_PROC_VIDEO_ENCODER ) ;
2018-07-20 03:52:50 -04:00
if ( ret ) {
2019-03-06 16:13:36 -05:00
v4l2_err ( & dev - > v4l2_dev , " Failed to init mem2mem media controller for enc \n " ) ;
2018-07-20 03:52:50 -04:00
goto unreg_m2m ;
}
2019-03-06 16:13:36 -05:00
ret = v4l2_m2m_register_media_controller ( dev - > stateful_dec . m2m_dev ,
& dev - > stateful_dec . vfd ,
MEDIA_ENT_F_PROC_VIDEO_DECODER ) ;
2018-07-20 03:52:50 -04:00
if ( ret ) {
2019-03-06 16:13:36 -05:00
v4l2_err ( & dev - > v4l2_dev , " Failed to init mem2mem media controller for dec \n " ) ;
goto unreg_m2m_sf_enc_mc ;
2018-07-20 03:52:50 -04:00
}
2019-03-06 16:13:41 -05:00
ret = v4l2_m2m_register_media_controller ( dev - > stateless_dec . m2m_dev ,
& dev - > stateless_dec . vfd ,
MEDIA_ENT_F_PROC_VIDEO_DECODER ) ;
if ( ret ) {
v4l2_err ( & dev - > v4l2_dev , " Failed to init mem2mem media controller for stateless dec \n " ) ;
goto unreg_m2m_sf_dec_mc ;
}
2018-07-20 03:52:50 -04:00
ret = media_device_register ( & dev - > mdev ) ;
if ( ret ) {
v4l2_err ( & dev - > v4l2_dev , " Failed to register mem2mem media device \n " ) ;
2019-03-06 16:13:41 -05:00
goto unreg_m2m_sl_dec_mc ;
2018-07-20 03:52:50 -04:00
}
# endif
return 0 ;
# ifdef CONFIG_MEDIA_CONTROLLER
2019-03-06 16:13:41 -05:00
unreg_m2m_sl_dec_mc :
v4l2_m2m_unregister_media_controller ( dev - > stateless_dec . m2m_dev ) ;
2019-03-06 16:13:36 -05:00
unreg_m2m_sf_dec_mc :
v4l2_m2m_unregister_media_controller ( dev - > stateful_dec . m2m_dev ) ;
unreg_m2m_sf_enc_mc :
v4l2_m2m_unregister_media_controller ( dev - > stateful_enc . m2m_dev ) ;
2018-07-20 03:52:50 -04:00
unreg_m2m :
2019-03-06 16:13:41 -05:00
video_unregister_device ( & dev - > stateless_dec . vfd ) ;
v4l2_m2m_release ( dev - > stateless_dec . m2m_dev ) ;
# endif
unreg_sf_dec :
2019-03-06 16:13:36 -05:00
video_unregister_device ( & dev - > stateful_dec . vfd ) ;
v4l2_m2m_release ( dev - > stateful_dec . m2m_dev ) ;
unreg_sf_enc :
video_unregister_device ( & dev - > stateful_enc . vfd ) ;
v4l2_m2m_release ( dev - > stateful_enc . m2m_dev ) ;
2018-07-20 03:52:50 -04:00
unreg_dev :
v4l2_device_unregister ( & dev - > v4l2_dev ) ;
2019-05-03 10:22:49 -04:00
free_dev :
kfree ( dev ) ;
2018-07-20 03:52:50 -04:00
return ret ;
}
static int vicodec_remove ( struct platform_device * pdev )
{
struct vicodec_dev * dev = platform_get_drvdata ( pdev ) ;
v4l2_info ( & dev - > v4l2_dev , " Removing " VICODEC_NAME ) ;
# ifdef CONFIG_MEDIA_CONTROLLER
media_device_unregister ( & dev - > mdev ) ;
2019-03-06 16:13:36 -05:00
v4l2_m2m_unregister_media_controller ( dev - > stateful_enc . m2m_dev ) ;
v4l2_m2m_unregister_media_controller ( dev - > stateful_dec . m2m_dev ) ;
2019-03-06 16:13:41 -05:00
v4l2_m2m_unregister_media_controller ( dev - > stateless_dec . m2m_dev ) ;
2018-07-20 03:52:50 -04:00
media_device_cleanup ( & dev - > mdev ) ;
# endif
2019-03-06 16:13:36 -05:00
video_unregister_device ( & dev - > stateful_enc . vfd ) ;
video_unregister_device ( & dev - > stateful_dec . vfd ) ;
2019-03-06 16:13:41 -05:00
video_unregister_device ( & dev - > stateless_dec . vfd ) ;
2019-05-03 10:22:49 -04:00
v4l2_device_put ( & dev - > v4l2_dev ) ;
2018-07-20 03:52:50 -04:00
return 0 ;
}
static struct platform_driver vicodec_pdrv = {
. probe = vicodec_probe ,
. remove = vicodec_remove ,
. driver = {
. name = VICODEC_NAME ,
} ,
} ;
static void __exit vicodec_exit ( void )
{
platform_driver_unregister ( & vicodec_pdrv ) ;
platform_device_unregister ( & vicodec_pdev ) ;
}
static int __init vicodec_init ( void )
{
int ret ;
ret = platform_device_register ( & vicodec_pdev ) ;
if ( ret )
return ret ;
ret = platform_driver_register ( & vicodec_pdrv ) ;
if ( ret )
platform_device_unregister ( & vicodec_pdev ) ;
return ret ;
}
module_init ( vicodec_init ) ;
module_exit ( vicodec_exit ) ;