2023-08-30 17:31:07 +02:00
/*
* vpif - display - VPIF display driver
* Display driver for TI DaVinci VPIF
*
* Copyright ( C ) 2009 Texas Instruments Incorporated - https : //www.ti.com/
* Copyright ( C ) 2014 Lad , Prabhakar < prabhakar . csengg @ gmail . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed . as is . WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <media/v4l2-ioctl.h>
# include "vpif.h"
# include "vpif_display.h"
MODULE_DESCRIPTION ( " TI DaVinci VPIF Display driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( VPIF_DISPLAY_VERSION ) ;
# define VPIF_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
# define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
# define vpif_dbg(level, debug, fmt, arg...) \
v4l2_dbg ( level , debug , & vpif_obj . v4l2_dev , fmt , # # arg )
static int debug = 1 ;
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " Debug level 0-1 " ) ;
# define VPIF_DRIVER_NAME "vpif_display"
MODULE_ALIAS ( " platform: " VPIF_DRIVER_NAME ) ;
/* Is set to 1 in case of SDTV formats, 2 in case of HDTV formats. */
static int ycmux_mode ;
static u8 channel_first_int [ VPIF_NUMOBJECTS ] [ 2 ] = { { 1 , 1 } } ;
static struct vpif_device vpif_obj = { { NULL } } ;
static struct device * vpif_dev ;
static void vpif_calculate_offsets ( struct channel_obj * ch ) ;
static void vpif_config_addr ( struct channel_obj * ch , int muxmode ) ;
static inline
struct vpif_disp_buffer * to_vpif_buffer ( struct vb2_v4l2_buffer * vb )
{
return container_of ( vb , struct vpif_disp_buffer , vb ) ;
}
/**
* vpif_buffer_prepare : callback function for buffer prepare
* @ vb : ptr to vb2_buffer
*
* This is the callback function for buffer prepare when vb2_qbuf ( )
* function is called . The buffer is prepared and user space virtual address
* or user address is converted into physical address
*/
static int vpif_buffer_prepare ( struct vb2_buffer * vb )
{
struct vb2_v4l2_buffer * vbuf = to_vb2_v4l2_buffer ( vb ) ;
struct channel_obj * ch = vb2_get_drv_priv ( vb - > vb2_queue ) ;
struct common_obj * common ;
common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
vb2_set_plane_payload ( vb , 0 , common - > fmt . fmt . pix . sizeimage ) ;
if ( vb2_get_plane_payload ( vb , 0 ) > vb2_plane_size ( vb , 0 ) )
return - EINVAL ;
vbuf - > field = common - > fmt . fmt . pix . field ;
if ( vb - > vb2_queue - > type ! = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ) {
unsigned long addr = vb2_dma_contig_plane_dma_addr ( vb , 0 ) ;
if ( ! ISALIGNED ( addr + common - > ytop_off ) | |
! ISALIGNED ( addr + common - > ybtm_off ) | |
! ISALIGNED ( addr + common - > ctop_off ) | |
! ISALIGNED ( addr + common - > cbtm_off ) ) {
vpif_err ( " buffer offset not aligned to 8 bytes \n " ) ;
return - EINVAL ;
}
}
return 0 ;
}
/**
* vpif_buffer_queue_setup : Callback function for buffer setup .
* @ vq : vb2_queue ptr
* @ nbuffers : ptr to number of buffers requested by application
* @ nplanes : contains number of distinct video planes needed to hold a frame
* @ sizes : contains the size ( in bytes ) of each plane .
* @ alloc_devs : ptr to allocation context
*
* This callback function is called when reqbuf ( ) is called to adjust
* the buffer count and buffer size
*/
static int vpif_buffer_queue_setup ( struct vb2_queue * vq ,
unsigned int * nbuffers , unsigned int * nplanes ,
unsigned int sizes [ ] , struct device * alloc_devs [ ] )
{
struct channel_obj * ch = vb2_get_drv_priv ( vq ) ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
unsigned size = common - > fmt . fmt . pix . sizeimage ;
if ( * nplanes ) {
if ( sizes [ 0 ] < size )
return - EINVAL ;
size = sizes [ 0 ] ;
}
if ( vq - > num_buffers + * nbuffers < 3 )
* nbuffers = 3 - vq - > num_buffers ;
* nplanes = 1 ;
sizes [ 0 ] = size ;
/* Calculate the offset for Y and C data in the buffer */
vpif_calculate_offsets ( ch ) ;
return 0 ;
}
/**
* vpif_buffer_queue : Callback function to add buffer to DMA queue
* @ vb : ptr to vb2_buffer
*
* This callback function queues the buffer to DMA engine
*/
static void vpif_buffer_queue ( struct vb2_buffer * vb )
{
struct vb2_v4l2_buffer * vbuf = to_vb2_v4l2_buffer ( vb ) ;
struct vpif_disp_buffer * buf = to_vpif_buffer ( vbuf ) ;
struct channel_obj * ch = vb2_get_drv_priv ( vb - > vb2_queue ) ;
struct common_obj * common ;
unsigned long flags ;
common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
/* add the buffer to the DMA queue */
spin_lock_irqsave ( & common - > irqlock , flags ) ;
list_add_tail ( & buf - > list , & common - > dma_queue ) ;
spin_unlock_irqrestore ( & common - > irqlock , flags ) ;
}
/**
* vpif_start_streaming : Starts the DMA engine for streaming
* @ vq : ptr to vb2_buffer
* @ count : number of buffers
*/
static int vpif_start_streaming ( struct vb2_queue * vq , unsigned int count )
{
struct vpif_display_config * vpif_config_data =
vpif_dev - > platform_data ;
struct channel_obj * ch = vb2_get_drv_priv ( vq ) ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
struct vpif_params * vpif = & ch - > vpifparams ;
struct vpif_disp_buffer * buf , * tmp ;
unsigned long addr , flags ;
int ret ;
spin_lock_irqsave ( & common - > irqlock , flags ) ;
/* Initialize field_id */
ch - > field_id = 0 ;
/* clock settings */
if ( vpif_config_data - > set_clock ) {
ret = vpif_config_data - > set_clock ( ch - > vpifparams . std_info .
ycmux_mode , ch - > vpifparams . std_info . hd_sd ) ;
if ( ret < 0 ) {
vpif_err ( " can't set clock \n " ) ;
goto err ;
}
}
/* set the parameters and addresses */
ret = vpif_set_video_params ( vpif , ch - > channel_id + 2 ) ;
if ( ret < 0 )
goto err ;
ycmux_mode = ret ;
vpif_config_addr ( ch , ret ) ;
/* Get the next frame from the buffer queue */
common - > next_frm = common - > cur_frm =
list_entry ( common - > dma_queue . next ,
struct vpif_disp_buffer , list ) ;
list_del ( & common - > cur_frm - > list ) ;
spin_unlock_irqrestore ( & common - > irqlock , flags ) ;
addr = vb2_dma_contig_plane_dma_addr ( & common - > cur_frm - > vb . vb2_buf , 0 ) ;
common - > set_addr ( ( addr + common - > ytop_off ) ,
( addr + common - > ybtm_off ) ,
( addr + common - > ctop_off ) ,
( addr + common - > cbtm_off ) ) ;
/*
* Set interrupt for both the fields in VPIF
* Register enable channel in VPIF register
*/
channel_first_int [ VPIF_VIDEO_INDEX ] [ ch - > channel_id ] = 1 ;
if ( VPIF_CHANNEL2_VIDEO = = ch - > channel_id ) {
channel2_intr_assert ( ) ;
channel2_intr_enable ( 1 ) ;
enable_channel2 ( 1 ) ;
if ( vpif_config_data - > chan_config [ VPIF_CHANNEL2_VIDEO ] . clip_en )
channel2_clipping_enable ( 1 ) ;
}
if ( VPIF_CHANNEL3_VIDEO = = ch - > channel_id | | ycmux_mode = = 2 ) {
channel3_intr_assert ( ) ;
channel3_intr_enable ( 1 ) ;
enable_channel3 ( 1 ) ;
if ( vpif_config_data - > chan_config [ VPIF_CHANNEL3_VIDEO ] . clip_en )
channel3_clipping_enable ( 1 ) ;
}
return 0 ;
err :
list_for_each_entry_safe ( buf , tmp , & common - > dma_queue , list ) {
list_del ( & buf - > list ) ;
vb2_buffer_done ( & buf - > vb . vb2_buf , VB2_BUF_STATE_QUEUED ) ;
}
spin_unlock_irqrestore ( & common - > irqlock , flags ) ;
return ret ;
}
/**
* vpif_stop_streaming : Stop the DMA engine
* @ vq : ptr to vb2_queue
*
* This callback stops the DMA engine and any remaining buffers
* in the DMA queue are released .
*/
static void vpif_stop_streaming ( struct vb2_queue * vq )
{
struct channel_obj * ch = vb2_get_drv_priv ( vq ) ;
struct common_obj * common ;
unsigned long flags ;
common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
/* Disable channel */
if ( VPIF_CHANNEL2_VIDEO = = ch - > channel_id ) {
enable_channel2 ( 0 ) ;
channel2_intr_enable ( 0 ) ;
}
if ( VPIF_CHANNEL3_VIDEO = = ch - > channel_id | | ycmux_mode = = 2 ) {
enable_channel3 ( 0 ) ;
channel3_intr_enable ( 0 ) ;
}
/* release all active buffers */
spin_lock_irqsave ( & common - > irqlock , flags ) ;
if ( common - > cur_frm = = common - > next_frm ) {
vb2_buffer_done ( & common - > cur_frm - > vb . vb2_buf ,
VB2_BUF_STATE_ERROR ) ;
} else {
if ( common - > cur_frm )
vb2_buffer_done ( & common - > cur_frm - > vb . vb2_buf ,
VB2_BUF_STATE_ERROR ) ;
if ( common - > next_frm )
vb2_buffer_done ( & common - > next_frm - > vb . vb2_buf ,
VB2_BUF_STATE_ERROR ) ;
}
while ( ! list_empty ( & common - > dma_queue ) ) {
common - > next_frm = list_entry ( common - > dma_queue . next ,
struct vpif_disp_buffer , list ) ;
list_del ( & common - > next_frm - > list ) ;
vb2_buffer_done ( & common - > next_frm - > vb . vb2_buf ,
VB2_BUF_STATE_ERROR ) ;
}
spin_unlock_irqrestore ( & common - > irqlock , flags ) ;
}
static const struct vb2_ops video_qops = {
. queue_setup = vpif_buffer_queue_setup ,
. wait_prepare = vb2_ops_wait_prepare ,
. wait_finish = vb2_ops_wait_finish ,
. buf_prepare = vpif_buffer_prepare ,
. start_streaming = vpif_start_streaming ,
. stop_streaming = vpif_stop_streaming ,
. buf_queue = vpif_buffer_queue ,
} ;
static void process_progressive_mode ( struct common_obj * common )
{
unsigned long addr ;
spin_lock ( & common - > irqlock ) ;
/* Get the next buffer from buffer queue */
common - > next_frm = list_entry ( common - > dma_queue . next ,
struct vpif_disp_buffer , list ) ;
/* Remove that buffer from the buffer queue */
list_del ( & common - > next_frm - > list ) ;
spin_unlock ( & common - > irqlock ) ;
/* Set top and bottom field addrs in VPIF registers */
addr = vb2_dma_contig_plane_dma_addr ( & common - > next_frm - > vb . vb2_buf , 0 ) ;
common - > set_addr ( addr + common - > ytop_off ,
addr + common - > ybtm_off ,
addr + common - > ctop_off ,
addr + common - > cbtm_off ) ;
}
static void process_interlaced_mode ( int fid , struct common_obj * common )
{
/* device field id and local field id are in sync */
/* If this is even field */
if ( 0 = = fid ) {
if ( common - > cur_frm = = common - > next_frm )
return ;
/* one frame is displayed If next frame is
* available , release cur_frm and move on */
/* Copy frame display time */
common - > cur_frm - > vb . vb2_buf . timestamp = ktime_get_ns ( ) ;
/* Change status of the cur_frm */
vb2_buffer_done ( & common - > cur_frm - > vb . vb2_buf ,
VB2_BUF_STATE_DONE ) ;
/* Make cur_frm pointing to next_frm */
common - > cur_frm = common - > next_frm ;
} else if ( 1 = = fid ) { /* odd field */
spin_lock ( & common - > irqlock ) ;
if ( list_empty ( & common - > dma_queue )
| | ( common - > cur_frm ! = common - > next_frm ) ) {
spin_unlock ( & common - > irqlock ) ;
return ;
}
spin_unlock ( & common - > irqlock ) ;
/* one field is displayed configure the next
* frame if it is available else hold on current
* frame */
/* Get next from the buffer queue */
process_progressive_mode ( common ) ;
}
}
/*
* vpif_channel_isr : It changes status of the displayed buffer , takes next
* buffer from the queue and sets its address in VPIF registers
*/
static irqreturn_t vpif_channel_isr ( int irq , void * dev_id )
{
struct vpif_device * dev = & vpif_obj ;
struct channel_obj * ch ;
struct common_obj * common ;
int fid = - 1 , i ;
int channel_id ;
channel_id = * ( int * ) ( dev_id ) ;
if ( ! vpif_intr_status ( channel_id + 2 ) )
return IRQ_NONE ;
ch = dev - > dev [ channel_id ] ;
for ( i = 0 ; i < VPIF_NUMOBJECTS ; i + + ) {
common = & ch - > common [ i ] ;
/* If streaming is started in this channel */
if ( 1 = = ch - > vpifparams . std_info . frm_fmt ) {
spin_lock ( & common - > irqlock ) ;
if ( list_empty ( & common - > dma_queue ) ) {
spin_unlock ( & common - > irqlock ) ;
continue ;
}
spin_unlock ( & common - > irqlock ) ;
/* Progressive mode */
if ( ! channel_first_int [ i ] [ channel_id ] ) {
/* Mark status of the cur_frm to
* done and unlock semaphore on it */
common - > cur_frm - > vb . vb2_buf . timestamp =
ktime_get_ns ( ) ;
vb2_buffer_done ( & common - > cur_frm - > vb . vb2_buf ,
VB2_BUF_STATE_DONE ) ;
/* Make cur_frm pointing to next_frm */
common - > cur_frm = common - > next_frm ;
}
channel_first_int [ i ] [ channel_id ] = 0 ;
process_progressive_mode ( common ) ;
} else {
/* Interlaced mode */
/* If it is first interrupt, ignore it */
if ( channel_first_int [ i ] [ channel_id ] ) {
channel_first_int [ i ] [ channel_id ] = 0 ;
continue ;
}
if ( 0 = = i ) {
ch - > field_id ^ = 1 ;
/* Get field id from VPIF registers */
fid = vpif_channel_getfid ( ch - > channel_id + 2 ) ;
/* If fid does not match with stored field id */
if ( fid ! = ch - > field_id ) {
/* Make them in sync */
if ( 0 = = fid )
ch - > field_id = fid ;
return IRQ_HANDLED ;
}
}
process_interlaced_mode ( fid , common ) ;
}
}
return IRQ_HANDLED ;
}
static int vpif_update_std_info ( struct channel_obj * ch )
{
struct video_obj * vid_ch = & ch - > video ;
struct vpif_params * vpifparams = & ch - > vpifparams ;
struct vpif_channel_config_params * std_info = & vpifparams - > std_info ;
const struct vpif_channel_config_params * config ;
int i ;
for ( i = 0 ; i < vpif_ch_params_count ; i + + ) {
config = & vpif_ch_params [ i ] ;
if ( config - > hd_sd = = 0 ) {
vpif_dbg ( 2 , debug , " SD format \n " ) ;
if ( config - > stdid & vid_ch - > stdid ) {
memcpy ( std_info , config , sizeof ( * config ) ) ;
break ;
}
}
}
if ( i = = vpif_ch_params_count ) {
vpif_dbg ( 1 , debug , " Format not found \n " ) ;
return - EINVAL ;
}
return 0 ;
}
static int vpif_update_resolution ( struct channel_obj * ch )
{
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
struct video_obj * vid_ch = & ch - > video ;
struct vpif_params * vpifparams = & ch - > vpifparams ;
struct vpif_channel_config_params * std_info = & vpifparams - > std_info ;
if ( ! vid_ch - > stdid & & ! vid_ch - > dv_timings . bt . height )
return - EINVAL ;
if ( vid_ch - > stdid ) {
if ( vpif_update_std_info ( ch ) )
return - EINVAL ;
}
common - > fmt . fmt . pix . pixelformat = V4L2_PIX_FMT_YUV422P ;
common - > fmt . fmt . pix . width = std_info - > width ;
common - > fmt . fmt . pix . height = std_info - > height ;
vpif_dbg ( 1 , debug , " Pixel details: Width = %d,Height = %d \n " ,
common - > fmt . fmt . pix . width , common - > fmt . fmt . pix . height ) ;
/* Set height and width paramateres */
common - > height = std_info - > height ;
common - > width = std_info - > width ;
common - > fmt . fmt . pix . sizeimage = common - > height * common - > width * 2 ;
if ( vid_ch - > stdid )
common - > fmt . fmt . pix . colorspace = V4L2_COLORSPACE_SMPTE170M ;
else
common - > fmt . fmt . pix . colorspace = V4L2_COLORSPACE_REC709 ;
if ( ch - > vpifparams . std_info . frm_fmt )
common - > fmt . fmt . pix . field = V4L2_FIELD_NONE ;
else
common - > fmt . fmt . pix . field = V4L2_FIELD_INTERLACED ;
return 0 ;
}
/*
* vpif_calculate_offsets : This function calculates buffers offset for Y and C
* in the top and bottom field
*/
static void vpif_calculate_offsets ( struct channel_obj * ch )
{
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
struct vpif_params * vpifparams = & ch - > vpifparams ;
enum v4l2_field field = common - > fmt . fmt . pix . field ;
struct video_obj * vid_ch = & ch - > video ;
unsigned int hpitch , sizeimage ;
if ( V4L2_FIELD_ANY = = common - > fmt . fmt . pix . field ) {
if ( ch - > vpifparams . std_info . frm_fmt )
vid_ch - > buf_field = V4L2_FIELD_NONE ;
else
vid_ch - > buf_field = V4L2_FIELD_INTERLACED ;
} else {
vid_ch - > buf_field = common - > fmt . fmt . pix . field ;
}
sizeimage = common - > fmt . fmt . pix . sizeimage ;
hpitch = common - > fmt . fmt . pix . bytesperline ;
if ( ( V4L2_FIELD_NONE = = vid_ch - > buf_field ) | |
( V4L2_FIELD_INTERLACED = = vid_ch - > buf_field ) ) {
common - > ytop_off = 0 ;
common - > ybtm_off = hpitch ;
common - > ctop_off = sizeimage / 2 ;
common - > cbtm_off = sizeimage / 2 + hpitch ;
} else if ( V4L2_FIELD_SEQ_TB = = vid_ch - > buf_field ) {
common - > ytop_off = 0 ;
common - > ybtm_off = sizeimage / 4 ;
common - > ctop_off = sizeimage / 2 ;
common - > cbtm_off = common - > ctop_off + sizeimage / 4 ;
} else if ( V4L2_FIELD_SEQ_BT = = vid_ch - > buf_field ) {
common - > ybtm_off = 0 ;
common - > ytop_off = sizeimage / 4 ;
common - > cbtm_off = sizeimage / 2 ;
common - > ctop_off = common - > cbtm_off + sizeimage / 4 ;
}
if ( ( V4L2_FIELD_NONE = = vid_ch - > buf_field ) | |
( V4L2_FIELD_INTERLACED = = vid_ch - > buf_field ) ) {
vpifparams - > video_params . storage_mode = 1 ;
} else {
vpifparams - > video_params . storage_mode = 0 ;
}
if ( ch - > vpifparams . std_info . frm_fmt = = 1 ) {
vpifparams - > video_params . hpitch =
common - > fmt . fmt . pix . bytesperline ;
} else {
if ( ( field = = V4L2_FIELD_ANY ) | |
( field = = V4L2_FIELD_INTERLACED ) )
vpifparams - > video_params . hpitch =
common - > fmt . fmt . pix . bytesperline * 2 ;
else
vpifparams - > video_params . hpitch =
common - > fmt . fmt . pix . bytesperline ;
}
ch - > vpifparams . video_params . stdid = ch - > vpifparams . std_info . stdid ;
}
static void vpif_config_addr ( struct channel_obj * ch , int muxmode )
{
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
if ( VPIF_CHANNEL3_VIDEO = = ch - > channel_id ) {
common - > set_addr = ch3_set_video_buf_addr ;
} else {
if ( 2 = = muxmode )
common - > set_addr = ch2_set_video_buf_addr_yc_nmux ;
else
common - > set_addr = ch2_set_video_buf_addr ;
}
}
/* functions implementing ioctls */
/**
* vpif_querycap ( ) - QUERYCAP handler
* @ file : file ptr
* @ priv : file handle
* @ cap : ptr to v4l2_capability structure
*/
static int vpif_querycap ( struct file * file , void * priv ,
struct v4l2_capability * cap )
{
struct vpif_display_config * config = vpif_dev - > platform_data ;
strscpy ( cap - > driver , VPIF_DRIVER_NAME , sizeof ( cap - > driver ) ) ;
strscpy ( cap - > card , config - > card_name , sizeof ( cap - > card ) ) ;
return 0 ;
}
static int vpif_enum_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_fmtdesc * fmt )
{
if ( fmt - > index ! = 0 )
return - EINVAL ;
/* Fill in the information about format */
fmt - > pixelformat = V4L2_PIX_FMT_YUV422P ;
return 0 ;
}
static int vpif_g_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_format * fmt )
{
struct video_device * vdev = video_devdata ( file ) ;
struct channel_obj * ch = video_get_drvdata ( vdev ) ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
/* Check the validity of the buffer type */
if ( common - > fmt . type ! = fmt - > type )
return - EINVAL ;
if ( vpif_update_resolution ( ch ) )
return - EINVAL ;
* fmt = common - > fmt ;
return 0 ;
}
static int vpif_try_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_format * fmt )
{
struct video_device * vdev = video_devdata ( file ) ;
struct channel_obj * ch = video_get_drvdata ( vdev ) ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
struct v4l2_pix_format * pixfmt = & fmt - > fmt . pix ;
/*
* to suppress v4l - compliance warnings silently correct
* the pixelformat
*/
if ( pixfmt - > pixelformat ! = V4L2_PIX_FMT_YUV422P )
pixfmt - > pixelformat = common - > fmt . fmt . pix . pixelformat ;
if ( vpif_update_resolution ( ch ) )
return - EINVAL ;
pixfmt - > colorspace = common - > fmt . fmt . pix . colorspace ;
pixfmt - > field = common - > fmt . fmt . pix . field ;
pixfmt - > bytesperline = common - > fmt . fmt . pix . width ;
pixfmt - > width = common - > fmt . fmt . pix . width ;
pixfmt - > height = common - > fmt . fmt . pix . height ;
pixfmt - > sizeimage = pixfmt - > bytesperline * pixfmt - > height * 2 ;
return 0 ;
}
static int vpif_s_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_format * fmt )
{
struct video_device * vdev = video_devdata ( file ) ;
struct channel_obj * ch = video_get_drvdata ( vdev ) ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
struct v4l2_pix_format * pixfmt = & fmt - > fmt . pix ;
int ret ;
if ( vb2_is_busy ( & common - > buffer_queue ) )
return - EBUSY ;
ret = vpif_try_fmt_vid_out ( file , priv , fmt ) ;
if ( ret )
return ret ;
/* store the pix format in the channel object */
common - > fmt . fmt . pix = * pixfmt ;
/* store the format in the channel object */
common - > fmt = * fmt ;
return 0 ;
}
static int vpif_s_std ( struct file * file , void * priv , v4l2_std_id std_id )
{
struct vpif_display_config * config = vpif_dev - > platform_data ;
struct video_device * vdev = video_devdata ( file ) ;
struct channel_obj * ch = video_get_drvdata ( vdev ) ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
struct vpif_display_chan_config * chan_cfg ;
struct v4l2_output output ;
int ret ;
if ( ! config - > chan_config [ ch - > channel_id ] . outputs )
return - ENODATA ;
chan_cfg = & config - > chan_config [ ch - > channel_id ] ;
output = chan_cfg - > outputs [ ch - > output_idx ] . output ;
if ( output . capabilities ! = V4L2_OUT_CAP_STD )
return - ENODATA ;
if ( vb2_is_busy ( & common - > buffer_queue ) )
return - EBUSY ;
if ( ! ( std_id & VPIF_V4L2_STD ) )
return - EINVAL ;
/* Call encoder subdevice function to set the standard */
ch - > video . stdid = std_id ;
memset ( & ch - > video . dv_timings , 0 , sizeof ( ch - > video . dv_timings ) ) ;
/* Get the information about the standard */
if ( vpif_update_resolution ( ch ) )
return - EINVAL ;
common - > fmt . fmt . pix . bytesperline = common - > fmt . fmt . pix . width ;
ret = v4l2_device_call_until_err ( & vpif_obj . v4l2_dev , 1 , video ,
s_std_output , std_id ) ;
if ( ret < 0 ) {
vpif_err ( " Failed to set output standard \n " ) ;
return ret ;
}
ret = v4l2_device_call_until_err ( & vpif_obj . v4l2_dev , 1 , video ,
s_std , std_id ) ;
if ( ret < 0 )
vpif_err ( " Failed to set standard for sub devices \n " ) ;
return ret ;
}
static int vpif_g_std ( struct file * file , void * priv , v4l2_std_id * std )
{
struct vpif_display_config * config = vpif_dev - > platform_data ;
struct video_device * vdev = video_devdata ( file ) ;
struct channel_obj * ch = video_get_drvdata ( vdev ) ;
struct vpif_display_chan_config * chan_cfg ;
struct v4l2_output output ;
if ( ! config - > chan_config [ ch - > channel_id ] . outputs )
return - ENODATA ;
chan_cfg = & config - > chan_config [ ch - > channel_id ] ;
output = chan_cfg - > outputs [ ch - > output_idx ] . output ;
if ( output . capabilities ! = V4L2_OUT_CAP_STD )
return - ENODATA ;
* std = ch - > video . stdid ;
return 0 ;
}
static int vpif_enum_output ( struct file * file , void * fh ,
struct v4l2_output * output )
{
struct vpif_display_config * config = vpif_dev - > platform_data ;
struct video_device * vdev = video_devdata ( file ) ;
struct channel_obj * ch = video_get_drvdata ( vdev ) ;
struct vpif_display_chan_config * chan_cfg ;
chan_cfg = & config - > chan_config [ ch - > channel_id ] ;
if ( output - > index > = chan_cfg - > output_count ) {
vpif_dbg ( 1 , debug , " Invalid output index \n " ) ;
return - EINVAL ;
}
* output = chan_cfg - > outputs [ output - > index ] . output ;
return 0 ;
}
/**
* vpif_output_to_subdev ( ) - Maps output to sub device
* @ vpif_cfg : global config ptr
* @ chan_cfg : channel config ptr
* @ index : Given output index from application
*
* lookup the sub device information for a given output index .
* we report all the output to application . output table also
* has sub device name for the each output
*/
static int
vpif_output_to_subdev ( struct vpif_display_config * vpif_cfg ,
struct vpif_display_chan_config * chan_cfg , int index )
{
struct vpif_subdev_info * subdev_info ;
const char * subdev_name ;
int i ;
vpif_dbg ( 2 , debug , " vpif_output_to_subdev \n " ) ;
if ( ! chan_cfg - > outputs )
return - 1 ;
subdev_name = chan_cfg - > outputs [ index ] . subdev_name ;
if ( ! subdev_name )
return - 1 ;
/* loop through the sub device list to get the sub device info */
for ( i = 0 ; i < vpif_cfg - > subdev_count ; i + + ) {
subdev_info = & vpif_cfg - > subdevinfo [ i ] ;
if ( ! strcmp ( subdev_info - > name , subdev_name ) )
return i ;
}
return - 1 ;
}
/**
* vpif_set_output ( ) - Select an output
* @ vpif_cfg : global config ptr
* @ ch : channel
* @ index : Given output index from application
*
* Select the given output .
*/
static int vpif_set_output ( struct vpif_display_config * vpif_cfg ,
struct channel_obj * ch , int index )
{
struct vpif_display_chan_config * chan_cfg =
& vpif_cfg - > chan_config [ ch - > channel_id ] ;
struct v4l2_subdev * sd = NULL ;
u32 input = 0 , output = 0 ;
int sd_index ;
int ret ;
sd_index = vpif_output_to_subdev ( vpif_cfg , chan_cfg , index ) ;
if ( sd_index > = 0 )
sd = vpif_obj . sd [ sd_index ] ;
if ( sd ) {
input = chan_cfg - > outputs [ index ] . input_route ;
output = chan_cfg - > outputs [ index ] . output_route ;
ret = v4l2_subdev_call ( sd , video , s_routing , input , output , 0 ) ;
if ( ret < 0 & & ret ! = - ENOIOCTLCMD ) {
vpif_err ( " Failed to set output \n " ) ;
return ret ;
}
}
ch - > output_idx = index ;
ch - > sd = sd ;
if ( chan_cfg - > outputs )
/* update tvnorms from the sub device output info */
ch - > video_dev . tvnorms = chan_cfg - > outputs [ index ] . output . std ;
return 0 ;
}
static int vpif_s_output ( struct file * file , void * priv , unsigned int i )
{
struct vpif_display_config * config = vpif_dev - > platform_data ;
struct video_device * vdev = video_devdata ( file ) ;
struct channel_obj * ch = video_get_drvdata ( vdev ) ;
struct vpif_display_chan_config * chan_cfg ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
if ( vb2_is_busy ( & common - > buffer_queue ) )
return - EBUSY ;
chan_cfg = & config - > chan_config [ ch - > channel_id ] ;
if ( i > = chan_cfg - > output_count )
return - EINVAL ;
return vpif_set_output ( config , ch , i ) ;
}
static int vpif_g_output ( struct file * file , void * priv , unsigned int * i )
{
struct video_device * vdev = video_devdata ( file ) ;
struct channel_obj * ch = video_get_drvdata ( vdev ) ;
* i = ch - > output_idx ;
return 0 ;
}
/**
* vpif_enum_dv_timings ( ) - ENUM_DV_TIMINGS handler
* @ file : file ptr
* @ priv : file handle
* @ timings : input timings
*/
static int
vpif_enum_dv_timings ( struct file * file , void * priv ,
struct v4l2_enum_dv_timings * timings )
{
struct vpif_display_config * config = vpif_dev - > platform_data ;
struct video_device * vdev = video_devdata ( file ) ;
struct channel_obj * ch = video_get_drvdata ( vdev ) ;
struct vpif_display_chan_config * chan_cfg ;
struct v4l2_output output ;
int ret ;
if ( ! config - > chan_config [ ch - > channel_id ] . outputs )
return - ENODATA ;
chan_cfg = & config - > chan_config [ ch - > channel_id ] ;
output = chan_cfg - > outputs [ ch - > output_idx ] . output ;
if ( output . capabilities ! = V4L2_OUT_CAP_DV_TIMINGS )
return - ENODATA ;
timings - > pad = 0 ;
ret = v4l2_subdev_call ( ch - > sd , pad , enum_dv_timings , timings ) ;
if ( ret = = - ENOIOCTLCMD | | ret = = - ENODEV )
return - EINVAL ;
return ret ;
}
/**
* vpif_s_dv_timings ( ) - S_DV_TIMINGS handler
* @ file : file ptr
* @ priv : file handle
* @ timings : digital video timings
*/
static int vpif_s_dv_timings ( struct file * file , void * priv ,
struct v4l2_dv_timings * timings )
{
struct vpif_display_config * config = vpif_dev - > platform_data ;
struct video_device * vdev = video_devdata ( file ) ;
struct channel_obj * ch = video_get_drvdata ( vdev ) ;
struct vpif_params * vpifparams = & ch - > vpifparams ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
struct vpif_channel_config_params * std_info = & vpifparams - > std_info ;
struct video_obj * vid_ch = & ch - > video ;
struct v4l2_bt_timings * bt = & vid_ch - > dv_timings . bt ;
struct vpif_display_chan_config * chan_cfg ;
struct v4l2_output output ;
int ret ;
if ( ! config - > chan_config [ ch - > channel_id ] . outputs )
return - ENODATA ;
chan_cfg = & config - > chan_config [ ch - > channel_id ] ;
output = chan_cfg - > outputs [ ch - > output_idx ] . output ;
if ( output . capabilities ! = V4L2_OUT_CAP_DV_TIMINGS )
return - ENODATA ;
if ( vb2_is_busy ( & common - > buffer_queue ) )
return - EBUSY ;
if ( timings - > type ! = V4L2_DV_BT_656_1120 ) {
vpif_dbg ( 2 , debug , " Timing type not defined \n " ) ;
return - EINVAL ;
}
/* Configure subdevice timings, if any */
ret = v4l2_subdev_call ( ch - > sd , video , s_dv_timings , timings ) ;
if ( ret = = - ENOIOCTLCMD | | ret = = - ENODEV )
ret = 0 ;
if ( ret < 0 ) {
vpif_dbg ( 2 , debug , " Error setting custom DV timings \n " ) ;
return ret ;
}
if ( ! ( timings - > bt . width & & timings - > bt . height & &
( timings - > bt . hbackporch | |
timings - > bt . hfrontporch | |
timings - > bt . hsync ) & &
timings - > bt . vfrontporch & &
( timings - > bt . vbackporch | |
timings - > bt . vsync ) ) ) {
vpif_dbg ( 2 , debug , " Timings for width, height, horizontal back porch, horizontal sync, horizontal front porch, vertical back porch, vertical sync and vertical back porch must be defined \n " ) ;
return - EINVAL ;
}
vid_ch - > dv_timings = * timings ;
/* Configure video port timings */
std_info - > eav2sav = V4L2_DV_BT_BLANKING_WIDTH ( bt ) - 8 ;
std_info - > sav2eav = bt - > width ;
std_info - > l1 = 1 ;
std_info - > l3 = bt - > vsync + bt - > vbackporch + 1 ;
std_info - > vsize = V4L2_DV_BT_FRAME_HEIGHT ( bt ) ;
if ( bt - > interlaced ) {
if ( bt - > il_vbackporch | | bt - > il_vfrontporch | | bt - > il_vsync ) {
std_info - > l5 = std_info - > vsize / 2 -
( bt - > vfrontporch - 1 ) ;
std_info - > l7 = std_info - > vsize / 2 + 1 ;
std_info - > l9 = std_info - > l7 + bt - > il_vsync +
bt - > il_vbackporch + 1 ;
std_info - > l11 = std_info - > vsize -
( bt - > il_vfrontporch - 1 ) ;
} else {
vpif_dbg ( 2 , debug , " Required timing values for interlaced BT format missing \n " ) ;
return - EINVAL ;
}
} else {
std_info - > l5 = std_info - > vsize - ( bt - > vfrontporch - 1 ) ;
}
strscpy ( std_info - > name , " Custom timings BT656/1120 " ,
sizeof ( std_info - > name ) ) ;
std_info - > width = bt - > width ;
std_info - > height = bt - > height ;
std_info - > frm_fmt = bt - > interlaced ? 0 : 1 ;
std_info - > ycmux_mode = 0 ;
std_info - > capture_format = 0 ;
std_info - > vbi_supported = 0 ;
std_info - > hd_sd = 1 ;
std_info - > stdid = 0 ;
vid_ch - > stdid = 0 ;
return 0 ;
}
/**
* vpif_g_dv_timings ( ) - G_DV_TIMINGS handler
* @ file : file ptr
* @ priv : file handle
* @ timings : digital video timings
*/
static int vpif_g_dv_timings ( struct file * file , void * priv ,
struct v4l2_dv_timings * timings )
{
struct vpif_display_config * config = vpif_dev - > platform_data ;
struct video_device * vdev = video_devdata ( file ) ;
struct channel_obj * ch = video_get_drvdata ( vdev ) ;
struct vpif_display_chan_config * chan_cfg ;
struct video_obj * vid_ch = & ch - > video ;
struct v4l2_output output ;
if ( ! config - > chan_config [ ch - > channel_id ] . outputs )
goto error ;
chan_cfg = & config - > chan_config [ ch - > channel_id ] ;
output = chan_cfg - > outputs [ ch - > output_idx ] . output ;
if ( output . capabilities ! = V4L2_OUT_CAP_DV_TIMINGS )
goto error ;
* timings = vid_ch - > dv_timings ;
return 0 ;
error :
return - ENODATA ;
}
/*
* vpif_log_status ( ) - Status information
* @ file : file ptr
* @ priv : file handle
*
* Returns zero .
*/
static int vpif_log_status ( struct file * filep , void * priv )
{
/* status for sub devices */
v4l2_device_call_all ( & vpif_obj . v4l2_dev , 0 , core , log_status ) ;
return 0 ;
}
/* vpif display ioctl operations */
static const struct v4l2_ioctl_ops vpif_ioctl_ops = {
. vidioc_querycap = vpif_querycap ,
. vidioc_enum_fmt_vid_out = vpif_enum_fmt_vid_out ,
. vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out ,
. vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out ,
. vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out ,
. vidioc_reqbufs = vb2_ioctl_reqbufs ,
. vidioc_create_bufs = vb2_ioctl_create_bufs ,
. vidioc_querybuf = vb2_ioctl_querybuf ,
. vidioc_qbuf = vb2_ioctl_qbuf ,
. vidioc_dqbuf = vb2_ioctl_dqbuf ,
. vidioc_expbuf = vb2_ioctl_expbuf ,
. vidioc_streamon = vb2_ioctl_streamon ,
. vidioc_streamoff = vb2_ioctl_streamoff ,
. vidioc_s_std = vpif_s_std ,
. vidioc_g_std = vpif_g_std ,
. vidioc_enum_output = vpif_enum_output ,
. vidioc_s_output = vpif_s_output ,
. vidioc_g_output = vpif_g_output ,
. vidioc_enum_dv_timings = vpif_enum_dv_timings ,
. vidioc_s_dv_timings = vpif_s_dv_timings ,
. vidioc_g_dv_timings = vpif_g_dv_timings ,
. vidioc_log_status = vpif_log_status ,
} ;
static const struct v4l2_file_operations vpif_fops = {
. owner = THIS_MODULE ,
. open = v4l2_fh_open ,
. release = vb2_fop_release ,
. unlocked_ioctl = video_ioctl2 ,
. mmap = vb2_fop_mmap ,
. poll = vb2_fop_poll
} ;
/*Configure the channels, buffer sizei, request irq */
static int initialize_vpif ( void )
{
int free_channel_objects_index ;
int err , i , j ;
/* Allocate memory for six channel objects */
for ( i = 0 ; i < VPIF_DISPLAY_MAX_DEVICES ; i + + ) {
vpif_obj . dev [ i ] =
kzalloc ( sizeof ( struct channel_obj ) , GFP_KERNEL ) ;
/* If memory allocation fails, return error */
if ( ! vpif_obj . dev [ i ] ) {
free_channel_objects_index = i ;
err = - ENOMEM ;
goto vpif_init_free_channel_objects ;
}
}
return 0 ;
vpif_init_free_channel_objects :
for ( j = 0 ; j < free_channel_objects_index ; j + + )
kfree ( vpif_obj . dev [ j ] ) ;
return err ;
}
static void free_vpif_objs ( void )
{
int i ;
for ( i = 0 ; i < VPIF_DISPLAY_MAX_DEVICES ; i + + )
kfree ( vpif_obj . dev [ i ] ) ;
}
static int vpif_probe_complete ( void )
{
struct common_obj * common ;
struct video_device * vdev ;
struct channel_obj * ch ;
struct vb2_queue * q ;
int j , err , k ;
for ( j = 0 ; j < VPIF_DISPLAY_MAX_DEVICES ; j + + ) {
ch = vpif_obj . dev [ j ] ;
/* Initialize field of the channel objects */
for ( k = 0 ; k < VPIF_NUMOBJECTS ; k + + ) {
common = & ch - > common [ k ] ;
spin_lock_init ( & common - > irqlock ) ;
mutex_init ( & common - > lock ) ;
common - > set_addr = NULL ;
common - > ytop_off = 0 ;
common - > ybtm_off = 0 ;
common - > ctop_off = 0 ;
common - > cbtm_off = 0 ;
common - > cur_frm = NULL ;
common - > next_frm = NULL ;
memset ( & common - > fmt , 0 , sizeof ( common - > fmt ) ) ;
}
ch - > initialized = 0 ;
if ( vpif_obj . config - > subdev_count )
ch - > sd = vpif_obj . sd [ 0 ] ;
ch - > channel_id = j ;
memset ( & ch - > vpifparams , 0 , sizeof ( ch - > vpifparams ) ) ;
ch - > common [ VPIF_VIDEO_INDEX ] . fmt . type =
V4L2_BUF_TYPE_VIDEO_OUTPUT ;
/* select output 0 */
err = vpif_set_output ( vpif_obj . config , ch , 0 ) ;
if ( err )
goto probe_out ;
/* set initial format */
ch - > video . stdid = V4L2_STD_525_60 ;
memset ( & ch - > video . dv_timings , 0 , sizeof ( ch - > video . dv_timings ) ) ;
vpif_update_resolution ( ch ) ;
/* Initialize vb2 queue */
q = & common - > buffer_queue ;
q - > type = V4L2_BUF_TYPE_VIDEO_OUTPUT ;
q - > io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF ;
q - > drv_priv = ch ;
q - > ops = & video_qops ;
q - > mem_ops = & vb2_dma_contig_memops ;
q - > buf_struct_size = sizeof ( struct vpif_disp_buffer ) ;
q - > timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC ;
q - > min_buffers_needed = 1 ;
q - > lock = & common - > lock ;
q - > dev = vpif_dev ;
err = vb2_queue_init ( q ) ;
if ( err ) {
vpif_err ( " vpif_display: vb2_queue_init() failed \n " ) ;
goto probe_out ;
}
INIT_LIST_HEAD ( & common - > dma_queue ) ;
/* register video device */
vpif_dbg ( 1 , debug , " channel=%p,channel->video_dev=%p \n " ,
ch , & ch - > video_dev ) ;
/* Initialize the video_device structure */
vdev = & ch - > video_dev ;
strscpy ( vdev - > name , VPIF_DRIVER_NAME , sizeof ( vdev - > name ) ) ;
vdev - > release = video_device_release_empty ;
vdev - > fops = & vpif_fops ;
vdev - > ioctl_ops = & vpif_ioctl_ops ;
vdev - > v4l2_dev = & vpif_obj . v4l2_dev ;
vdev - > vfl_dir = VFL_DIR_TX ;
vdev - > queue = q ;
vdev - > lock = & common - > lock ;
vdev - > device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING ;
video_set_drvdata ( & ch - > video_dev , ch ) ;
err = video_register_device ( vdev , VFL_TYPE_VIDEO ,
( j ? 3 : 2 ) ) ;
if ( err < 0 )
goto probe_out ;
}
return 0 ;
probe_out :
for ( k = 0 ; k < j ; k + + ) {
ch = vpif_obj . dev [ k ] ;
video_unregister_device ( & ch - > video_dev ) ;
}
return err ;
}
/*
* vpif_probe : This function creates device entries by register itself to the
* V4L2 driver and initializes fields of each channel objects
*/
static __init int vpif_probe ( struct platform_device * pdev )
{
struct vpif_subdev_info * subdevdata ;
struct i2c_adapter * i2c_adap ;
int subdev_count ;
int res_idx = 0 ;
int i , err ;
if ( ! pdev - > dev . platform_data ) {
dev_warn ( & pdev - > dev , " Missing platform data. Giving up. \n " ) ;
return - EINVAL ;
}
vpif_dev = & pdev - > dev ;
err = initialize_vpif ( ) ;
if ( err ) {
v4l2_err ( vpif_dev - > driver , " Error initializing vpif \n " ) ;
return err ;
}
err = v4l2_device_register ( vpif_dev , & vpif_obj . v4l2_dev ) ;
if ( err ) {
v4l2_err ( vpif_dev - > driver , " Error registering v4l2 device \n " ) ;
goto vpif_free ;
}
do {
int irq ;
err = platform_get_irq_optional ( pdev , res_idx ) ;
if ( err < 0 & & err ! = - ENXIO )
goto vpif_unregister ;
if ( err > 0 )
irq = err ;
else
break ;
err = devm_request_irq ( & pdev - > dev , irq , vpif_channel_isr ,
IRQF_SHARED , VPIF_DRIVER_NAME ,
( void * ) ( & vpif_obj . dev [ res_idx ] - > channel_id ) ) ;
if ( err ) {
vpif_err ( " VPIF IRQ request failed \n " ) ;
goto vpif_unregister ;
}
} while ( + + res_idx ) ;
vpif_obj . config = pdev - > dev . platform_data ;
subdev_count = vpif_obj . config - > subdev_count ;
subdevdata = vpif_obj . config - > subdevinfo ;
vpif_obj . sd = kcalloc ( subdev_count , sizeof ( * vpif_obj . sd ) , GFP_KERNEL ) ;
if ( ! vpif_obj . sd ) {
err = - ENOMEM ;
goto vpif_unregister ;
}
i2c_adap = i2c_get_adapter ( vpif_obj . config - > i2c_adapter_id ) ;
for ( i = 0 ; i < subdev_count ; i + + ) {
vpif_obj . sd [ i ] =
v4l2_i2c_new_subdev_board ( & vpif_obj . v4l2_dev ,
i2c_adap ,
& subdevdata [ i ] . board_info ,
NULL ) ;
if ( ! vpif_obj . sd [ i ] ) {
vpif_err ( " Error registering v4l2 subdevice \n " ) ;
err = - ENODEV ;
goto probe_subdev_out ;
}
vpif_obj . sd [ i ] - > grp_id = 1 < < i ;
}
err = vpif_probe_complete ( ) ;
if ( err )
goto probe_subdev_out ;
return 0 ;
probe_subdev_out :
kfree ( vpif_obj . sd ) ;
vpif_unregister :
v4l2_device_unregister ( & vpif_obj . v4l2_dev ) ;
vpif_free :
free_vpif_objs ( ) ;
return err ;
}
/*
* vpif_remove : It un - register channels from V4L2 driver
*/
2023-10-24 12:59:35 +02:00
static void vpif_remove ( struct platform_device * device )
2023-08-30 17:31:07 +02:00
{
struct channel_obj * ch ;
int i ;
v4l2_device_unregister ( & vpif_obj . v4l2_dev ) ;
kfree ( vpif_obj . sd ) ;
/* un-register device */
for ( i = 0 ; i < VPIF_DISPLAY_MAX_DEVICES ; i + + ) {
/* Get the pointer to the channel object */
ch = vpif_obj . dev [ i ] ;
/* Unregister video device */
video_unregister_device ( & ch - > video_dev ) ;
}
free_vpif_objs ( ) ;
}
# ifdef CONFIG_PM_SLEEP
static int vpif_suspend ( struct device * dev )
{
struct common_obj * common ;
struct channel_obj * ch ;
int i ;
for ( i = 0 ; i < VPIF_DISPLAY_MAX_DEVICES ; i + + ) {
/* Get the pointer to the channel object */
ch = vpif_obj . dev [ i ] ;
common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
if ( ! vb2_start_streaming_called ( & common - > buffer_queue ) )
continue ;
mutex_lock ( & common - > lock ) ;
/* Disable channel */
if ( ch - > channel_id = = VPIF_CHANNEL2_VIDEO ) {
enable_channel2 ( 0 ) ;
channel2_intr_enable ( 0 ) ;
}
if ( ch - > channel_id = = VPIF_CHANNEL3_VIDEO | |
ycmux_mode = = 2 ) {
enable_channel3 ( 0 ) ;
channel3_intr_enable ( 0 ) ;
}
mutex_unlock ( & common - > lock ) ;
}
return 0 ;
}
static int vpif_resume ( struct device * dev )
{
struct common_obj * common ;
struct channel_obj * ch ;
int i ;
for ( i = 0 ; i < VPIF_DISPLAY_MAX_DEVICES ; i + + ) {
/* Get the pointer to the channel object */
ch = vpif_obj . dev [ i ] ;
common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
if ( ! vb2_start_streaming_called ( & common - > buffer_queue ) )
continue ;
mutex_lock ( & common - > lock ) ;
/* Enable channel */
if ( ch - > channel_id = = VPIF_CHANNEL2_VIDEO ) {
enable_channel2 ( 1 ) ;
channel2_intr_enable ( 1 ) ;
}
if ( ch - > channel_id = = VPIF_CHANNEL3_VIDEO | |
ycmux_mode = = 2 ) {
enable_channel3 ( 1 ) ;
channel3_intr_enable ( 1 ) ;
}
mutex_unlock ( & common - > lock ) ;
}
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( vpif_pm_ops , vpif_suspend , vpif_resume ) ;
static __refdata struct platform_driver vpif_driver = {
. driver = {
. name = VPIF_DRIVER_NAME ,
. pm = & vpif_pm_ops ,
} ,
. probe = vpif_probe ,
2023-10-24 12:59:35 +02:00
. remove_new = vpif_remove ,
2023-08-30 17:31:07 +02:00
} ;
module_platform_driver ( vpif_driver ) ;