2017-12-08 07:35:37 -05:00
// SPDX-License-Identifier: GPL-2.0
2013-05-15 11:36:19 -03:00
/*
* Xilinx Video IP Composite Device
*
* Copyright ( C ) 2013 - 2015 Ideas on Board
* Copyright ( C ) 2013 - 2015 Xilinx , Inc .
*
* Contacts : Hyun Kwon < hyun . kwon @ xilinx . com >
* Laurent Pinchart < laurent . pinchart @ ideasonboard . com >
*/
# include <linux/list.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_graph.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <media/v4l2-async.h>
# include <media/v4l2-common.h>
# include <media/v4l2-device.h>
2016-08-26 20:17:25 -03:00
# include <media/v4l2-fwnode.h>
2013-05-15 11:36:19 -03:00
# include "xilinx-dma.h"
# include "xilinx-vipp.h"
# define XVIPP_DMA_S2MM 0
# define XVIPP_DMA_MM2S 1
/**
* struct xvip_graph_entity - Entity in the video graph
* @ asd : subdev asynchronous registration information
2018-09-29 15:54:18 -04:00
* @ entity : media entity , from the corresponding V4L2 subdev
2013-05-15 11:36:19 -03:00
* @ subdev : V4L2 subdev
*/
struct xvip_graph_entity {
2018-09-29 15:54:18 -04:00
struct v4l2_async_subdev asd ; /* must be first */
2013-05-15 11:36:19 -03:00
struct media_entity * entity ;
struct v4l2_subdev * subdev ;
} ;
2018-09-29 15:54:18 -04:00
static inline struct xvip_graph_entity *
to_xvip_entity ( struct v4l2_async_subdev * asd )
{
return container_of ( asd , struct xvip_graph_entity , asd ) ;
}
2013-05-15 11:36:19 -03:00
/* -----------------------------------------------------------------------------
* Graph Management
*/
static struct xvip_graph_entity *
xvip_graph_find_entity ( struct xvip_composite_device * xdev ,
2018-09-29 15:54:18 -04:00
const struct fwnode_handle * fwnode )
2013-05-15 11:36:19 -03:00
{
struct xvip_graph_entity * entity ;
2018-09-29 15:54:18 -04:00
struct v4l2_async_subdev * asd ;
2013-05-15 11:36:19 -03:00
2018-09-29 15:54:18 -04:00
list_for_each_entry ( asd , & xdev - > notifier . asd_list , asd_list ) {
entity = to_xvip_entity ( asd ) ;
if ( entity - > asd . match . fwnode = = fwnode )
2013-05-15 11:36:19 -03:00
return entity ;
}
return NULL ;
}
static int xvip_graph_build_one ( struct xvip_composite_device * xdev ,
struct xvip_graph_entity * entity )
{
u32 link_flags = MEDIA_LNK_FL_ENABLED ;
struct media_entity * local = entity - > entity ;
struct media_entity * remote ;
struct media_pad * local_pad ;
struct media_pad * remote_pad ;
struct xvip_graph_entity * ent ;
2016-08-26 20:17:25 -03:00
struct v4l2_fwnode_link link ;
2018-09-29 15:54:18 -04:00
struct fwnode_handle * ep = NULL ;
2013-05-15 11:36:19 -03:00
int ret = 0 ;
dev_dbg ( xdev - > dev , " creating links for entity %s \n " , local - > name ) ;
while ( 1 ) {
/* Get the next endpoint and parse its link. */
2018-09-29 15:54:18 -04:00
ep = fwnode_graph_get_next_endpoint ( entity - > asd . match . fwnode ,
ep ) ;
2017-10-12 12:04:44 -04:00
if ( ep = = NULL )
2013-05-15 11:36:19 -03:00
break ;
2018-09-29 15:54:18 -04:00
dev_dbg ( xdev - > dev , " processing endpoint %p \n " , ep ) ;
2013-05-15 11:36:19 -03:00
2018-09-29 15:54:18 -04:00
ret = v4l2_fwnode_parse_link ( ep , & link ) ;
2013-05-15 11:36:19 -03:00
if ( ret < 0 ) {
2018-09-29 15:54:18 -04:00
dev_err ( xdev - > dev , " failed to parse link for %p \n " ,
2017-07-21 15:28:33 -04:00
ep ) ;
2013-05-15 11:36:19 -03:00
continue ;
}
/* Skip sink ports, they will be processed from the other end of
* the link .
*/
if ( link . local_port > = local - > num_pads ) {
2018-09-29 15:54:18 -04:00
dev_err ( xdev - > dev , " invalid port number %u for %p \n " ,
link . local_port , link . local_node ) ;
2016-08-26 20:17:25 -03:00
v4l2_fwnode_put_link ( & link ) ;
2013-05-15 11:36:19 -03:00
ret = - EINVAL ;
break ;
}
local_pad = & local - > pads [ link . local_port ] ;
if ( local_pad - > flags & MEDIA_PAD_FL_SINK ) {
2018-09-29 15:54:18 -04:00
dev_dbg ( xdev - > dev , " skipping sink port %p:%u \n " ,
link . local_node , link . local_port ) ;
2016-08-26 20:17:25 -03:00
v4l2_fwnode_put_link ( & link ) ;
2013-05-15 11:36:19 -03:00
continue ;
}
/* Skip DMA engines, they will be processed separately. */
2016-08-26 20:17:25 -03:00
if ( link . remote_node = = of_fwnode_handle ( xdev - > dev - > of_node ) ) {
2018-09-29 15:54:18 -04:00
dev_dbg ( xdev - > dev , " skipping DMA port %p:%u \n " ,
link . local_node , link . local_port ) ;
2016-08-26 20:17:25 -03:00
v4l2_fwnode_put_link ( & link ) ;
2013-05-15 11:36:19 -03:00
continue ;
}
/* Find the remote entity. */
2018-09-29 15:54:18 -04:00
ent = xvip_graph_find_entity ( xdev , link . remote_node ) ;
2013-05-15 11:36:19 -03:00
if ( ent = = NULL ) {
2018-09-29 15:54:18 -04:00
dev_err ( xdev - > dev , " no entity found for %p \n " ,
link . remote_node ) ;
2016-08-26 20:17:25 -03:00
v4l2_fwnode_put_link ( & link ) ;
2013-05-15 11:36:19 -03:00
ret = - ENODEV ;
break ;
}
remote = ent - > entity ;
if ( link . remote_port > = remote - > num_pads ) {
2018-09-29 15:54:18 -04:00
dev_err ( xdev - > dev , " invalid port number %u on %p \n " ,
link . remote_port , link . remote_node ) ;
2016-08-26 20:17:25 -03:00
v4l2_fwnode_put_link ( & link ) ;
2013-05-15 11:36:19 -03:00
ret = - EINVAL ;
break ;
}
remote_pad = & remote - > pads [ link . remote_port ] ;
2016-08-26 20:17:25 -03:00
v4l2_fwnode_put_link ( & link ) ;
2013-05-15 11:36:19 -03:00
/* Create the media link. */
dev_dbg ( xdev - > dev , " creating %s:%u -> %s:%u link \n " ,
local - > name , local_pad - > index ,
remote - > name , remote_pad - > index ) ;
2015-08-07 08:14:38 -03:00
ret = media_create_pad_link ( local , local_pad - > index ,
2013-05-15 11:36:19 -03:00
remote , remote_pad - > index ,
link_flags ) ;
if ( ret < 0 ) {
dev_err ( xdev - > dev ,
" failed to create %s:%u -> %s:%u link \n " ,
local - > name , local_pad - > index ,
remote - > name , remote_pad - > index ) ;
break ;
}
}
2018-09-29 15:54:18 -04:00
fwnode_handle_put ( ep ) ;
2013-05-15 11:36:19 -03:00
return ret ;
}
static struct xvip_dma *
xvip_graph_find_dma ( struct xvip_composite_device * xdev , unsigned int port )
{
struct xvip_dma * dma ;
list_for_each_entry ( dma , & xdev - > dmas , list ) {
if ( dma - > port = = port )
return dma ;
}
return NULL ;
}
static int xvip_graph_build_dma ( struct xvip_composite_device * xdev )
{
u32 link_flags = MEDIA_LNK_FL_ENABLED ;
struct device_node * node = xdev - > dev - > of_node ;
struct media_entity * source ;
struct media_entity * sink ;
struct media_pad * source_pad ;
struct media_pad * sink_pad ;
struct xvip_graph_entity * ent ;
2016-08-26 20:17:25 -03:00
struct v4l2_fwnode_link link ;
2013-05-15 11:36:19 -03:00
struct device_node * ep = NULL ;
struct xvip_dma * dma ;
int ret = 0 ;
dev_dbg ( xdev - > dev , " creating links for DMA engines \n " ) ;
while ( 1 ) {
/* Get the next endpoint and parse its link. */
2017-10-12 12:04:44 -04:00
ep = of_graph_get_next_endpoint ( node , ep ) ;
if ( ep = = NULL )
2013-05-15 11:36:19 -03:00
break ;
2017-07-21 15:28:33 -04:00
dev_dbg ( xdev - > dev , " processing endpoint %pOF \n " , ep ) ;
2013-05-15 11:36:19 -03:00
2016-08-26 20:17:25 -03:00
ret = v4l2_fwnode_parse_link ( of_fwnode_handle ( ep ) , & link ) ;
2013-05-15 11:36:19 -03:00
if ( ret < 0 ) {
2017-07-21 15:28:33 -04:00
dev_err ( xdev - > dev , " failed to parse link for %pOF \n " ,
ep ) ;
2013-05-15 11:36:19 -03:00
continue ;
}
/* Find the DMA engine. */
dma = xvip_graph_find_dma ( xdev , link . local_port ) ;
if ( dma = = NULL ) {
dev_err ( xdev - > dev , " no DMA engine found for port %u \n " ,
link . local_port ) ;
2016-08-26 20:17:25 -03:00
v4l2_fwnode_put_link ( & link ) ;
2013-05-15 11:36:19 -03:00
ret = - EINVAL ;
break ;
}
dev_dbg ( xdev - > dev , " creating link for DMA engine %s \n " ,
dma - > video . name ) ;
/* Find the remote entity. */
2018-09-29 15:54:18 -04:00
ent = xvip_graph_find_entity ( xdev , link . remote_node ) ;
2013-05-15 11:36:19 -03:00
if ( ent = = NULL ) {
2017-07-21 15:28:33 -04:00
dev_err ( xdev - > dev , " no entity found for %pOF \n " ,
to_of_node ( link . remote_node ) ) ;
2016-08-26 20:17:25 -03:00
v4l2_fwnode_put_link ( & link ) ;
2013-05-15 11:36:19 -03:00
ret = - ENODEV ;
break ;
}
if ( link . remote_port > = ent - > entity - > num_pads ) {
2017-07-21 15:28:33 -04:00
dev_err ( xdev - > dev , " invalid port number %u on %pOF \n " ,
2016-08-26 20:17:25 -03:00
link . remote_port ,
2017-07-21 15:28:33 -04:00
to_of_node ( link . remote_node ) ) ;
2016-08-26 20:17:25 -03:00
v4l2_fwnode_put_link ( & link ) ;
2013-05-15 11:36:19 -03:00
ret = - EINVAL ;
break ;
}
if ( dma - > pad . flags & MEDIA_PAD_FL_SOURCE ) {
source = & dma - > video . entity ;
source_pad = & dma - > pad ;
sink = ent - > entity ;
sink_pad = & sink - > pads [ link . remote_port ] ;
} else {
source = ent - > entity ;
source_pad = & source - > pads [ link . remote_port ] ;
sink = & dma - > video . entity ;
sink_pad = & dma - > pad ;
}
2016-08-26 20:17:25 -03:00
v4l2_fwnode_put_link ( & link ) ;
2013-05-15 11:36:19 -03:00
/* Create the media link. */
dev_dbg ( xdev - > dev , " creating %s:%u -> %s:%u link \n " ,
source - > name , source_pad - > index ,
sink - > name , sink_pad - > index ) ;
2015-08-07 08:14:38 -03:00
ret = media_create_pad_link ( source , source_pad - > index ,
2013-05-15 11:36:19 -03:00
sink , sink_pad - > index ,
link_flags ) ;
if ( ret < 0 ) {
dev_err ( xdev - > dev ,
" failed to create %s:%u -> %s:%u link \n " ,
source - > name , source_pad - > index ,
sink - > name , sink_pad - > index ) ;
break ;
}
}
of_node_put ( ep ) ;
return ret ;
}
static int xvip_graph_notify_complete ( struct v4l2_async_notifier * notifier )
{
struct xvip_composite_device * xdev =
container_of ( notifier , struct xvip_composite_device , notifier ) ;
struct xvip_graph_entity * entity ;
2018-09-29 15:54:18 -04:00
struct v4l2_async_subdev * asd ;
2013-05-15 11:36:19 -03:00
int ret ;
dev_dbg ( xdev - > dev , " notify complete, all subdevs registered \n " ) ;
/* Create links for every entity. */
2018-09-29 15:54:18 -04:00
list_for_each_entry ( asd , & xdev - > notifier . asd_list , asd_list ) {
entity = to_xvip_entity ( asd ) ;
2013-05-15 11:36:19 -03:00
ret = xvip_graph_build_one ( xdev , entity ) ;
if ( ret < 0 )
return ret ;
}
/* Create links for DMA channels. */
ret = xvip_graph_build_dma ( xdev ) ;
if ( ret < 0 )
return ret ;
ret = v4l2_device_register_subdev_nodes ( & xdev - > v4l2_dev ) ;
if ( ret < 0 )
dev_err ( xdev - > dev , " failed to register subdev nodes \n " ) ;
2015-12-11 20:57:08 -02:00
return media_device_register ( & xdev - > media_dev ) ;
2013-05-15 11:36:19 -03:00
}
static int xvip_graph_notify_bound ( struct v4l2_async_notifier * notifier ,
struct v4l2_subdev * subdev ,
2018-09-29 15:54:18 -04:00
struct v4l2_async_subdev * unused )
2013-05-15 11:36:19 -03:00
{
struct xvip_composite_device * xdev =
container_of ( notifier , struct xvip_composite_device , notifier ) ;
struct xvip_graph_entity * entity ;
2018-09-29 15:54:18 -04:00
struct v4l2_async_subdev * asd ;
2013-05-15 11:36:19 -03:00
/* Locate the entity corresponding to the bound subdev and store the
* subdev pointer .
*/
2018-09-29 15:54:18 -04:00
list_for_each_entry ( asd , & xdev - > notifier . asd_list , asd_list ) {
entity = to_xvip_entity ( asd ) ;
if ( entity - > asd . match . fwnode ! = subdev - > fwnode )
2013-05-15 11:36:19 -03:00
continue ;
if ( entity - > subdev ) {
2018-09-29 15:54:18 -04:00
dev_err ( xdev - > dev , " duplicate subdev for node %p \n " ,
entity - > asd . match . fwnode ) ;
2013-05-15 11:36:19 -03:00
return - EINVAL ;
}
dev_dbg ( xdev - > dev , " subdev %s bound \n " , subdev - > name ) ;
entity - > entity = & subdev - > entity ;
entity - > subdev = subdev ;
return 0 ;
}
dev_err ( xdev - > dev , " no entity for subdev %s \n " , subdev - > name ) ;
return - EINVAL ;
}
2017-08-30 13:18:04 -04:00
static const struct v4l2_async_notifier_operations xvip_graph_notify_ops = {
. bound = xvip_graph_notify_bound ,
. complete = xvip_graph_notify_complete ,
} ;
2013-05-15 11:36:19 -03:00
static int xvip_graph_parse_one ( struct xvip_composite_device * xdev ,
2018-09-29 15:54:18 -04:00
struct fwnode_handle * fwnode )
2013-05-15 11:36:19 -03:00
{
2018-09-29 15:54:18 -04:00
struct fwnode_handle * remote ;
struct fwnode_handle * ep = NULL ;
2013-05-15 11:36:19 -03:00
int ret = 0 ;
2018-09-29 15:54:18 -04:00
dev_dbg ( xdev - > dev , " parsing node %p \n " , fwnode ) ;
2013-05-15 11:36:19 -03:00
while ( 1 ) {
2021-01-18 02:52:58 +01:00
struct xvip_graph_entity * xge ;
2018-09-29 15:54:18 -04:00
ep = fwnode_graph_get_next_endpoint ( fwnode , ep ) ;
2016-03-22 07:43:58 -03:00
if ( ep = = NULL )
2013-05-15 11:36:19 -03:00
break ;
2018-09-29 15:54:18 -04:00
dev_dbg ( xdev - > dev , " handling endpoint %p \n " , ep ) ;
2013-05-15 11:36:19 -03:00
2018-09-29 15:54:18 -04:00
remote = fwnode_graph_get_remote_port_parent ( ep ) ;
2013-05-15 11:36:19 -03:00
if ( remote = = NULL ) {
ret = - EINVAL ;
2018-09-29 15:54:18 -04:00
goto err_notifier_cleanup ;
2013-05-15 11:36:19 -03:00
}
2018-09-29 15:54:18 -04:00
fwnode_handle_put ( ep ) ;
2013-05-15 11:36:19 -03:00
/* Skip entities that we have already processed. */
2018-09-29 15:54:18 -04:00
if ( remote = = of_fwnode_handle ( xdev - > dev - > of_node ) | |
2013-05-15 11:36:19 -03:00
xvip_graph_find_entity ( xdev , remote ) ) {
2018-09-29 15:54:18 -04:00
fwnode_handle_put ( remote ) ;
2013-05-15 11:36:19 -03:00
continue ;
}
2021-03-05 18:13:12 +01:00
xge = v4l2_async_nf_add_fwnode ( & xdev - > notifier , remote ,
struct xvip_graph_entity ) ;
2019-04-04 19:43:29 -04:00
fwnode_handle_put ( remote ) ;
2021-01-18 02:52:58 +01:00
if ( IS_ERR ( xge ) ) {
ret = PTR_ERR ( xge ) ;
2018-09-29 15:54:18 -04:00
goto err_notifier_cleanup ;
2013-05-15 11:36:19 -03:00
}
}
2018-09-29 15:54:18 -04:00
return 0 ;
err_notifier_cleanup :
2021-03-05 18:13:12 +01:00
v4l2_async_nf_cleanup ( & xdev - > notifier ) ;
2018-09-29 15:54:18 -04:00
fwnode_handle_put ( ep ) ;
2013-05-15 11:36:19 -03:00
return ret ;
}
static int xvip_graph_parse ( struct xvip_composite_device * xdev )
{
struct xvip_graph_entity * entity ;
2018-09-29 15:54:18 -04:00
struct v4l2_async_subdev * asd ;
2013-05-15 11:36:19 -03:00
int ret ;
/*
* Walk the links to parse the full graph . Start by parsing the
* composite node and then parse entities in turn . The list_for_each
* loop will handle entities added at the end of the list while walking
* the links .
*/
2018-09-29 15:54:18 -04:00
ret = xvip_graph_parse_one ( xdev , of_fwnode_handle ( xdev - > dev - > of_node ) ) ;
2013-05-15 11:36:19 -03:00
if ( ret < 0 )
return 0 ;
2018-09-29 15:54:18 -04:00
list_for_each_entry ( asd , & xdev - > notifier . asd_list , asd_list ) {
entity = to_xvip_entity ( asd ) ;
ret = xvip_graph_parse_one ( xdev , entity - > asd . match . fwnode ) ;
if ( ret < 0 ) {
2021-03-05 18:13:12 +01:00
v4l2_async_nf_cleanup ( & xdev - > notifier ) ;
2013-05-15 11:36:19 -03:00
break ;
2018-09-29 15:54:18 -04:00
}
2013-05-15 11:36:19 -03:00
}
return ret ;
}
static int xvip_graph_dma_init_one ( struct xvip_composite_device * xdev ,
struct device_node * node )
{
struct xvip_dma * dma ;
enum v4l2_buf_type type ;
const char * direction ;
unsigned int index ;
int ret ;
ret = of_property_read_string ( node , " direction " , & direction ) ;
if ( ret < 0 )
return ret ;
if ( strcmp ( direction , " input " ) = = 0 )
type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
else if ( strcmp ( direction , " output " ) = = 0 )
type = V4L2_BUF_TYPE_VIDEO_OUTPUT ;
else
return - EINVAL ;
of_property_read_u32 ( node , " reg " , & index ) ;
dma = devm_kzalloc ( xdev - > dev , sizeof ( * dma ) , GFP_KERNEL ) ;
if ( dma = = NULL )
return - ENOMEM ;
ret = xvip_dma_init ( xdev , dma , type , index ) ;
if ( ret < 0 ) {
2017-07-21 15:28:33 -04:00
dev_err ( xdev - > dev , " %pOF initialization failed \n " , node ) ;
2013-05-15 11:36:19 -03:00
return ret ;
}
list_add_tail ( & dma - > list , & xdev - > dmas ) ;
xdev - > v4l2_caps | = type = = V4L2_BUF_TYPE_VIDEO_CAPTURE
? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_VIDEO_OUTPUT ;
return 0 ;
}
static int xvip_graph_dma_init ( struct xvip_composite_device * xdev )
{
struct device_node * ports ;
struct device_node * port ;
int ret ;
ports = of_get_child_by_name ( xdev - > dev - > of_node , " ports " ) ;
if ( ports = = NULL ) {
dev_err ( xdev - > dev , " ports node not present \n " ) ;
return - EINVAL ;
}
for_each_child_of_node ( ports , port ) {
ret = xvip_graph_dma_init_one ( xdev , port ) ;
2015-10-25 11:57:04 -02:00
if ( ret < 0 ) {
of_node_put ( port ) ;
2013-05-15 11:36:19 -03:00
return ret ;
2015-10-25 11:57:04 -02:00
}
2013-05-15 11:36:19 -03:00
}
return 0 ;
}
static void xvip_graph_cleanup ( struct xvip_composite_device * xdev )
{
struct xvip_dma * dmap ;
struct xvip_dma * dma ;
2021-03-05 18:13:12 +01:00
v4l2_async_nf_unregister ( & xdev - > notifier ) ;
v4l2_async_nf_cleanup ( & xdev - > notifier ) ;
2013-05-15 11:36:19 -03:00
list_for_each_entry_safe ( dma , dmap , & xdev - > dmas , list ) {
xvip_dma_cleanup ( dma ) ;
list_del ( & dma - > list ) ;
}
}
static int xvip_graph_init ( struct xvip_composite_device * xdev )
{
int ret ;
/* Init the DMA channels. */
ret = xvip_graph_dma_init ( xdev ) ;
if ( ret < 0 ) {
dev_err ( xdev - > dev , " DMA initialization failed \n " ) ;
goto done ;
}
/* Parse the graph to extract a list of subdevice DT nodes. */
ret = xvip_graph_parse ( xdev ) ;
if ( ret < 0 ) {
dev_err ( xdev - > dev , " graph parsing failed \n " ) ;
goto done ;
}
2018-09-29 15:54:18 -04:00
if ( list_empty ( & xdev - > notifier . asd_list ) ) {
2013-05-15 11:36:19 -03:00
dev_err ( xdev - > dev , " no subdev found in graph \n " ) ;
2021-03-05 10:48:19 +01:00
ret = - ENOENT ;
2013-05-15 11:36:19 -03:00
goto done ;
}
/* Register the subdevices notifier. */
2017-08-30 13:18:04 -04:00
xdev - > notifier . ops = & xvip_graph_notify_ops ;
2013-05-15 11:36:19 -03:00
2021-03-05 18:13:12 +01:00
ret = v4l2_async_nf_register ( & xdev - > v4l2_dev , & xdev - > notifier ) ;
2013-05-15 11:36:19 -03:00
if ( ret < 0 ) {
dev_err ( xdev - > dev , " notifier registration failed \n " ) ;
goto done ;
}
ret = 0 ;
done :
if ( ret < 0 )
xvip_graph_cleanup ( xdev ) ;
return ret ;
}
/* -----------------------------------------------------------------------------
* Media Controller and V4L2
*/
static void xvip_composite_v4l2_cleanup ( struct xvip_composite_device * xdev )
{
v4l2_device_unregister ( & xdev - > v4l2_dev ) ;
media_device_unregister ( & xdev - > media_dev ) ;
2015-12-11 20:57:08 -02:00
media_device_cleanup ( & xdev - > media_dev ) ;
2013-05-15 11:36:19 -03:00
}
static int xvip_composite_v4l2_init ( struct xvip_composite_device * xdev )
{
int ret ;
xdev - > media_dev . dev = xdev - > dev ;
2018-09-10 08:19:14 -04:00
strscpy ( xdev - > media_dev . model , " Xilinx Video Composite Device " ,
2013-05-15 11:36:19 -03:00
sizeof ( xdev - > media_dev . model ) ) ;
xdev - > media_dev . hw_revision = 0 ;
2015-12-11 20:57:08 -02:00
media_device_init ( & xdev - > media_dev ) ;
2013-05-15 11:36:19 -03:00
xdev - > v4l2_dev . mdev = & xdev - > media_dev ;
ret = v4l2_device_register ( xdev - > dev , & xdev - > v4l2_dev ) ;
if ( ret < 0 ) {
dev_err ( xdev - > dev , " V4L2 device registration failed (%d) \n " ,
ret ) ;
2015-12-11 20:57:08 -02:00
media_device_cleanup ( & xdev - > media_dev ) ;
2013-05-15 11:36:19 -03:00
return ret ;
}
return 0 ;
}
/* -----------------------------------------------------------------------------
* Platform Device Driver
*/
static int xvip_composite_probe ( struct platform_device * pdev )
{
struct xvip_composite_device * xdev ;
int ret ;
xdev = devm_kzalloc ( & pdev - > dev , sizeof ( * xdev ) , GFP_KERNEL ) ;
if ( ! xdev )
return - ENOMEM ;
xdev - > dev = & pdev - > dev ;
INIT_LIST_HEAD ( & xdev - > dmas ) ;
2021-03-05 18:13:12 +01:00
v4l2_async_nf_init ( & xdev - > notifier ) ;
2013-05-15 11:36:19 -03:00
ret = xvip_composite_v4l2_init ( xdev ) ;
if ( ret < 0 )
return ret ;
ret = xvip_graph_init ( xdev ) ;
if ( ret < 0 )
goto error ;
platform_set_drvdata ( pdev , xdev ) ;
dev_info ( xdev - > dev , " device registered \n " ) ;
return 0 ;
error :
xvip_composite_v4l2_cleanup ( xdev ) ;
return ret ;
}
static int xvip_composite_remove ( struct platform_device * pdev )
{
struct xvip_composite_device * xdev = platform_get_drvdata ( pdev ) ;
xvip_graph_cleanup ( xdev ) ;
xvip_composite_v4l2_cleanup ( xdev ) ;
return 0 ;
}
static const struct of_device_id xvip_composite_of_id_table [ ] = {
{ . compatible = " xlnx,video " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , xvip_composite_of_id_table ) ;
static struct platform_driver xvip_composite_driver = {
. driver = {
. name = " xilinx-video " ,
. of_match_table = xvip_composite_of_id_table ,
} ,
. probe = xvip_composite_probe ,
. remove = xvip_composite_remove ,
} ;
module_platform_driver ( xvip_composite_driver ) ;
MODULE_AUTHOR ( " Laurent Pinchart <laurent.pinchart@ideasonboard.com> " ) ;
MODULE_DESCRIPTION ( " Xilinx Video IP Composite Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;