2015-08-02 18:37:01 -03:00
/*
* vsp1_drm . c - - R - Car VSP1 DRM API
*
* Copyright ( C ) 2015 Renesas Electronics Corporation
*
* Contact : Laurent Pinchart ( laurent . pinchart @ ideasonboard . com )
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/device.h>
2017-05-17 02:20:06 +03:00
# include <linux/dma-mapping.h>
2015-08-02 18:37:01 -03:00
# include <linux/slab.h>
# include <media/media-entity.h>
# include <media/v4l2-subdev.h>
2016-02-19 23:13:45 -02:00
# include <media/vsp1.h>
2015-08-02 18:37:01 -03:00
# include "vsp1.h"
2018-02-26 11:06:21 -05:00
# include "vsp1_brx.h"
2015-09-07 01:40:25 -03:00
# include "vsp1_dl.h"
2015-08-02 18:37:01 -03:00
# include "vsp1_drm.h"
# include "vsp1_lif.h"
# include "vsp1_pipe.h"
# include "vsp1_rwpf.h"
2018-02-26 11:06:21 -05:00
# define BRX_NAME(e) (e)->type == VSP1_ENTITY_BRU ? "BRU" : "BRS"
2015-11-08 20:06:57 -02:00
/* -----------------------------------------------------------------------------
* Interrupt Handling
*/
2017-06-30 13:14:11 +01:00
static void vsp1_du_pipeline_frame_end ( struct vsp1_pipeline * pipe ,
2018-04-04 17:30:49 -04:00
unsigned int completion )
2017-03-04 02:01:17 +00:00
{
2017-06-23 16:20:47 +03:00
struct vsp1_drm_pipeline * drm_pipe = to_vsp1_drm_pipeline ( pipe ) ;
2018-02-22 14:26:21 -05:00
bool complete = completion = = VSP1_DL_FRAME_END_COMPLETED ;
2017-03-04 02:01:17 +00:00
2017-06-23 16:20:47 +03:00
if ( drm_pipe - > du_complete )
2018-02-22 14:26:21 -05:00
drm_pipe - > du_complete ( drm_pipe - > du_private , complete ) ;
if ( completion & VSP1_DL_FRAME_END_INTERNAL ) {
2018-02-26 11:06:21 -05:00
drm_pipe - > force_brx_release = false ;
2018-02-22 14:26:21 -05:00
wake_up ( & drm_pipe - > wait_queue ) ;
}
2017-03-04 02:01:17 +00:00
}
2018-02-23 15:35:00 -05:00
/* -----------------------------------------------------------------------------
* Pipeline Configuration
*/
2018-02-26 11:06:21 -05:00
/* Setup one RPF and the connected BRx sink pad. */
2018-02-23 15:35:00 -05:00
static int vsp1_du_pipeline_setup_rpf ( struct vsp1_device * vsp1 ,
struct vsp1_pipeline * pipe ,
struct vsp1_rwpf * rpf ,
2018-02-26 11:06:21 -05:00
unsigned int brx_input )
2018-02-23 15:35:00 -05:00
{
struct v4l2_subdev_selection sel ;
struct v4l2_subdev_format format ;
const struct v4l2_rect * crop ;
int ret ;
/*
* Configure the format on the RPF sink pad and propagate it up to the
2018-02-26 11:06:21 -05:00
* BRx sink pad .
2018-02-23 15:35:00 -05:00
*/
crop = & vsp1 - > drm - > inputs [ rpf - > entity . index ] . crop ;
memset ( & format , 0 , sizeof ( format ) ) ;
format . which = V4L2_SUBDEV_FORMAT_ACTIVE ;
format . pad = RWPF_PAD_SINK ;
format . format . width = crop - > width + crop - > left ;
format . format . height = crop - > height + crop - > top ;
format . format . code = rpf - > fmtinfo - > mbus ;
format . format . field = V4L2_FIELD_NONE ;
ret = v4l2_subdev_call ( & rpf - > entity . subdev , pad , set_fmt , NULL ,
& format ) ;
if ( ret < 0 )
return ret ;
dev_dbg ( vsp1 - > dev ,
" %s: set format %ux%u (%x) on RPF%u sink \n " ,
__func__ , format . format . width , format . format . height ,
format . format . code , rpf - > entity . index ) ;
memset ( & sel , 0 , sizeof ( sel ) ) ;
sel . which = V4L2_SUBDEV_FORMAT_ACTIVE ;
sel . pad = RWPF_PAD_SINK ;
sel . target = V4L2_SEL_TGT_CROP ;
sel . r = * crop ;
ret = v4l2_subdev_call ( & rpf - > entity . subdev , pad , set_selection , NULL ,
& sel ) ;
if ( ret < 0 )
return ret ;
dev_dbg ( vsp1 - > dev ,
" %s: set selection (%u,%u)/%ux%u on RPF%u sink \n " ,
__func__ , sel . r . left , sel . r . top , sel . r . width , sel . r . height ,
rpf - > entity . index ) ;
/*
* RPF source , hardcode the format to ARGB8888 to turn on format
* conversion if needed .
*/
format . pad = RWPF_PAD_SOURCE ;
ret = v4l2_subdev_call ( & rpf - > entity . subdev , pad , get_fmt , NULL ,
& format ) ;
if ( ret < 0 )
return ret ;
dev_dbg ( vsp1 - > dev ,
" %s: got format %ux%u (%x) on RPF%u source \n " ,
__func__ , format . format . width , format . format . height ,
format . format . code , rpf - > entity . index ) ;
format . format . code = MEDIA_BUS_FMT_ARGB8888_1X32 ;
ret = v4l2_subdev_call ( & rpf - > entity . subdev , pad , set_fmt , NULL ,
& format ) ;
if ( ret < 0 )
return ret ;
2018-02-26 11:06:21 -05:00
/* BRx sink, propagate the format from the RPF source. */
format . pad = brx_input ;
2018-02-23 15:35:00 -05:00
2018-02-26 11:06:21 -05:00
ret = v4l2_subdev_call ( & pipe - > brx - > subdev , pad , set_fmt , NULL ,
2018-02-23 15:35:00 -05:00
& format ) ;
if ( ret < 0 )
return ret ;
dev_dbg ( vsp1 - > dev , " %s: set format %ux%u (%x) on %s pad %u \n " ,
__func__ , format . format . width , format . format . height ,
2018-02-26 11:06:21 -05:00
format . format . code , BRX_NAME ( pipe - > brx ) , format . pad ) ;
2018-02-23 15:35:00 -05:00
2018-02-26 11:06:21 -05:00
sel . pad = brx_input ;
2018-02-23 15:35:00 -05:00
sel . target = V4L2_SEL_TGT_COMPOSE ;
sel . r = vsp1 - > drm - > inputs [ rpf - > entity . index ] . compose ;
2018-02-26 11:06:21 -05:00
ret = v4l2_subdev_call ( & pipe - > brx - > subdev , pad , set_selection , NULL ,
2018-02-23 15:35:00 -05:00
& sel ) ;
if ( ret < 0 )
return ret ;
dev_dbg ( vsp1 - > dev , " %s: set selection (%u,%u)/%ux%u on %s pad %u \n " ,
__func__ , sel . r . left , sel . r . top , sel . r . width , sel . r . height ,
2018-02-26 11:06:21 -05:00
BRX_NAME ( pipe - > brx ) , sel . pad ) ;
2018-02-23 15:35:00 -05:00
return 0 ;
}
2018-02-26 11:06:21 -05:00
/* Setup the BRx source pad. */
2018-02-22 14:26:21 -05:00
static int vsp1_du_pipeline_setup_inputs ( struct vsp1_device * vsp1 ,
struct vsp1_pipeline * pipe ) ;
static void vsp1_du_pipeline_configure ( struct vsp1_pipeline * pipe ) ;
2018-02-26 11:06:21 -05:00
static int vsp1_du_pipeline_setup_brx ( struct vsp1_device * vsp1 ,
2018-02-22 14:26:21 -05:00
struct vsp1_pipeline * pipe )
{
struct vsp1_drm_pipeline * drm_pipe = to_vsp1_drm_pipeline ( pipe ) ;
struct v4l2_subdev_format format = {
. which = V4L2_SUBDEV_FORMAT_ACTIVE ,
} ;
2018-02-26 11:06:21 -05:00
struct vsp1_entity * brx ;
2018-02-22 14:26:21 -05:00
int ret ;
2018-02-22 14:26:21 -05:00
/*
2018-02-26 11:06:21 -05:00
* Pick a BRx :
* - If we need more than two inputs , use the BRU .
* - Otherwise , if we are not forced to release our BRx , keep it .
* - Else , use any free BRx ( randomly starting with the BRU ) .
2018-02-22 14:26:21 -05:00
*/
if ( pipe - > num_inputs > 2 )
2018-02-26 11:06:21 -05:00
brx = & vsp1 - > bru - > entity ;
else if ( pipe - > brx & & ! drm_pipe - > force_brx_release )
brx = pipe - > brx ;
2018-02-22 14:26:21 -05:00
else if ( ! vsp1 - > bru - > entity . pipe )
2018-02-26 11:06:21 -05:00
brx = & vsp1 - > bru - > entity ;
2018-02-22 14:26:21 -05:00
else
2018-02-26 11:06:21 -05:00
brx = & vsp1 - > brs - > entity ;
2018-02-22 14:26:21 -05:00
2018-02-26 11:06:21 -05:00
/* Switch BRx if needed. */
if ( brx ! = pipe - > brx ) {
struct vsp1_entity * released_brx = NULL ;
2018-02-22 14:26:21 -05:00
2018-02-26 11:06:21 -05:00
/* Release our BRx if we have one. */
if ( pipe - > brx ) {
2018-02-23 18:58:48 -05:00
dev_dbg ( vsp1 - > dev , " %s: pipe %u: releasing %s \n " ,
__func__ , pipe - > lif - > index ,
2018-02-26 11:06:21 -05:00
BRX_NAME ( pipe - > brx ) ) ;
2018-02-23 18:58:48 -05:00
2018-02-22 14:26:21 -05:00
/*
2018-02-26 11:06:21 -05:00
* The BRx might be acquired by the other pipeline in
2018-02-22 14:26:21 -05:00
* the next step . We must thus remove it from the list
* of entities for this pipeline . The other pipeline ' s
2018-02-26 11:06:21 -05:00
* hardware configuration will reconfigure the BRx
2018-02-22 14:26:21 -05:00
* routing .
*
* However , if the other pipeline doesn ' t acquire our
2018-02-26 11:06:21 -05:00
* BRx , we need to keep it in the list , otherwise the
2018-02-22 14:26:21 -05:00
* hardware configuration step won ' t disconnect it from
2018-02-26 11:06:21 -05:00
* the pipeline . To solve this , store the released BRx
2018-02-22 14:26:21 -05:00
* pointer to add it back to the list of entities later
* if it isn ' t acquired by the other pipeline .
*/
2018-02-26 11:06:21 -05:00
released_brx = pipe - > brx ;
2018-02-22 14:26:21 -05:00
2018-02-26 11:06:21 -05:00
list_del ( & pipe - > brx - > list_pipe ) ;
pipe - > brx - > sink = NULL ;
pipe - > brx - > pipe = NULL ;
pipe - > brx = NULL ;
2018-02-22 14:26:21 -05:00
}
/*
2018-02-26 11:06:21 -05:00
* If the BRx we need is in use , force the owner pipeline to
* switch to the other BRx and wait until the switch completes .
2018-02-22 14:26:21 -05:00
*/
2018-02-26 11:06:21 -05:00
if ( brx - > pipe ) {
2018-02-22 14:26:21 -05:00
struct vsp1_drm_pipeline * owner_pipe ;
2018-02-23 18:58:48 -05:00
dev_dbg ( vsp1 - > dev , " %s: pipe %u: waiting for %s \n " ,
2018-02-26 11:06:21 -05:00
__func__ , pipe - > lif - > index , BRX_NAME ( brx ) ) ;
2018-02-23 18:58:48 -05:00
2018-02-26 11:06:21 -05:00
owner_pipe = to_vsp1_drm_pipeline ( brx - > pipe ) ;
owner_pipe - > force_brx_release = true ;
2018-02-22 14:26:21 -05:00
vsp1_du_pipeline_setup_inputs ( vsp1 , & owner_pipe - > pipe ) ;
vsp1_du_pipeline_configure ( & owner_pipe - > pipe ) ;
ret = wait_event_timeout ( owner_pipe - > wait_queue ,
2018-02-26 11:06:21 -05:00
! owner_pipe - > force_brx_release ,
2018-02-22 14:26:21 -05:00
msecs_to_jiffies ( 500 ) ) ;
if ( ret = = 0 )
dev_warn ( vsp1 - > dev ,
" DRM pipeline %u reconfiguration timeout \n " ,
owner_pipe - > pipe . lif - > index ) ;
}
/*
2018-02-26 11:06:21 -05:00
* If the BRx we have released previously hasn ' t been acquired
2018-02-22 14:26:21 -05:00
* by the other pipeline , add it back to the entities list ( with
* the pipe pointer NULL ) to let vsp1_du_pipeline_configure ( )
* disconnect it from the hardware pipeline .
*/
2018-02-26 11:06:21 -05:00
if ( released_brx & & ! released_brx - > pipe )
list_add_tail ( & released_brx - > list_pipe ,
2018-02-22 14:26:21 -05:00
& pipe - > entities ) ;
2018-02-26 11:06:21 -05:00
/* Add the BRx to the pipeline. */
2018-02-23 18:58:48 -05:00
dev_dbg ( vsp1 - > dev , " %s: pipe %u: acquired %s \n " ,
2018-02-26 11:06:21 -05:00
__func__ , pipe - > lif - > index , BRX_NAME ( brx ) ) ;
2018-02-23 18:58:48 -05:00
2018-02-26 11:06:21 -05:00
pipe - > brx = brx ;
pipe - > brx - > pipe = pipe ;
pipe - > brx - > sink = & pipe - > output - > entity ;
pipe - > brx - > sink_pad = 0 ;
2018-02-22 14:26:21 -05:00
2018-02-26 11:06:21 -05:00
list_add_tail ( & pipe - > brx - > list_pipe , & pipe - > entities ) ;
2018-02-22 14:26:21 -05:00
}
2018-02-22 14:26:21 -05:00
/*
2018-02-26 11:06:21 -05:00
* Configure the format on the BRx source and verify that it matches the
2018-02-22 14:26:21 -05:00
* requested format . We don ' t set the media bus code as it is configured
2018-02-26 11:06:21 -05:00
* on the BRx sink pad 0 and propagated inside the entity , not on the
2018-02-22 14:26:21 -05:00
* source pad .
*/
2018-02-26 11:06:21 -05:00
format . pad = pipe - > brx - > source_pad ;
2018-02-22 14:26:21 -05:00
format . format . width = drm_pipe - > width ;
format . format . height = drm_pipe - > height ;
format . format . field = V4L2_FIELD_NONE ;
2018-02-26 11:06:21 -05:00
ret = v4l2_subdev_call ( & pipe - > brx - > subdev , pad , set_fmt , NULL ,
2018-02-22 14:26:21 -05:00
& format ) ;
if ( ret < 0 )
return ret ;
dev_dbg ( vsp1 - > dev , " %s: set format %ux%u (%x) on %s pad %u \n " ,
__func__ , format . format . width , format . format . height ,
2018-02-26 11:06:21 -05:00
format . format . code , BRX_NAME ( pipe - > brx ) , pipe - > brx - > source_pad ) ;
2018-02-22 14:26:21 -05:00
if ( format . format . width ! = drm_pipe - > width | |
format . format . height ! = drm_pipe - > height ) {
dev_dbg ( vsp1 - > dev , " %s: format mismatch \n " , __func__ ) ;
return - EPIPE ;
}
return 0 ;
}
2018-02-23 15:35:00 -05:00
static unsigned int rpf_zpos ( struct vsp1_device * vsp1 , struct vsp1_rwpf * rpf )
{
return vsp1 - > drm - > inputs [ rpf - > entity . index ] . zpos ;
}
2018-02-26 11:06:21 -05:00
/* Setup the input side of the pipeline (RPFs and BRx). */
2018-02-23 15:35:00 -05:00
static int vsp1_du_pipeline_setup_inputs ( struct vsp1_device * vsp1 ,
2018-02-26 11:06:21 -05:00
struct vsp1_pipeline * pipe )
2018-02-23 15:35:00 -05:00
{
struct vsp1_rwpf * inputs [ VSP1_MAX_RPF ] = { NULL , } ;
2018-02-26 11:06:21 -05:00
struct vsp1_brx * brx ;
2018-02-23 15:35:00 -05:00
unsigned int i ;
int ret ;
/* Count the number of enabled inputs and sort them by Z-order. */
pipe - > num_inputs = 0 ;
for ( i = 0 ; i < vsp1 - > info - > rpf_count ; + + i ) {
struct vsp1_rwpf * rpf = vsp1 - > rpf [ i ] ;
unsigned int j ;
if ( ! pipe - > inputs [ i ] )
continue ;
/* Insert the RPF in the sorted RPFs array. */
for ( j = pipe - > num_inputs + + ; j > 0 ; - - j ) {
if ( rpf_zpos ( vsp1 , inputs [ j - 1 ] ) < = rpf_zpos ( vsp1 , rpf ) )
break ;
inputs [ j ] = inputs [ j - 1 ] ;
}
inputs [ j ] = rpf ;
}
2018-02-22 14:26:21 -05:00
/*
2018-02-26 11:06:21 -05:00
* Setup the BRx . This must be done before setting up the RPF input
* pipelines as the BRx sink compose rectangles depend on the BRx source
2018-02-22 14:26:21 -05:00
* format .
*/
2018-02-26 11:06:21 -05:00
ret = vsp1_du_pipeline_setup_brx ( vsp1 , pipe ) ;
2018-02-22 14:26:21 -05:00
if ( ret < 0 ) {
dev_err ( vsp1 - > dev , " %s: failed to setup %s source \n " , __func__ ,
2018-02-26 11:06:21 -05:00
BRX_NAME ( pipe - > brx ) ) ;
2018-02-22 14:26:21 -05:00
return ret ;
}
2018-02-26 11:06:21 -05:00
brx = to_brx ( & pipe - > brx - > subdev ) ;
2018-02-22 14:26:21 -05:00
2018-02-23 15:35:00 -05:00
/* Setup the RPF input pipeline for every enabled input. */
2018-02-26 11:06:21 -05:00
for ( i = 0 ; i < pipe - > brx - > source_pad ; + + i ) {
2018-02-23 15:35:00 -05:00
struct vsp1_rwpf * rpf = inputs [ i ] ;
if ( ! rpf ) {
2018-02-26 11:06:21 -05:00
brx - > inputs [ i ] . rpf = NULL ;
2018-02-23 15:35:00 -05:00
continue ;
}
if ( ! rpf - > entity . pipe ) {
rpf - > entity . pipe = pipe ;
list_add_tail ( & rpf - > entity . list_pipe , & pipe - > entities ) ;
}
2018-02-26 11:06:21 -05:00
brx - > inputs [ i ] . rpf = rpf ;
rpf - > brx_input = i ;
rpf - > entity . sink = pipe - > brx ;
2018-02-23 15:35:00 -05:00
rpf - > entity . sink_pad = i ;
dev_dbg ( vsp1 - > dev , " %s: connecting RPF.%u to %s:%u \n " ,
2018-02-26 11:06:21 -05:00
__func__ , rpf - > entity . index , BRX_NAME ( pipe - > brx ) , i ) ;
2018-02-23 15:35:00 -05:00
ret = vsp1_du_pipeline_setup_rpf ( vsp1 , pipe , rpf , i ) ;
if ( ret < 0 ) {
dev_err ( vsp1 - > dev ,
" %s: failed to setup RPF.%u \n " ,
__func__ , rpf - > entity . index ) ;
return ret ;
}
}
return 0 ;
}
2018-02-22 14:26:21 -05:00
/* Setup the output side of the pipeline (WPF and LIF). */
static int vsp1_du_pipeline_setup_output ( struct vsp1_device * vsp1 ,
struct vsp1_pipeline * pipe )
{
struct vsp1_drm_pipeline * drm_pipe = to_vsp1_drm_pipeline ( pipe ) ;
struct v4l2_subdev_format format = { 0 , } ;
int ret ;
format . which = V4L2_SUBDEV_FORMAT_ACTIVE ;
format . pad = RWPF_PAD_SINK ;
format . format . width = drm_pipe - > width ;
format . format . height = drm_pipe - > height ;
format . format . code = MEDIA_BUS_FMT_ARGB8888_1X32 ;
format . format . field = V4L2_FIELD_NONE ;
ret = v4l2_subdev_call ( & pipe - > output - > entity . subdev , pad , set_fmt , NULL ,
& format ) ;
if ( ret < 0 )
return ret ;
dev_dbg ( vsp1 - > dev , " %s: set format %ux%u (%x) on WPF%u sink \n " ,
__func__ , format . format . width , format . format . height ,
format . format . code , pipe - > output - > entity . index ) ;
format . pad = RWPF_PAD_SOURCE ;
ret = v4l2_subdev_call ( & pipe - > output - > entity . subdev , pad , get_fmt , NULL ,
& format ) ;
if ( ret < 0 )
return ret ;
dev_dbg ( vsp1 - > dev , " %s: got format %ux%u (%x) on WPF%u source \n " ,
__func__ , format . format . width , format . format . height ,
format . format . code , pipe - > output - > entity . index ) ;
format . pad = LIF_PAD_SINK ;
ret = v4l2_subdev_call ( & pipe - > lif - > subdev , pad , set_fmt , NULL ,
& format ) ;
if ( ret < 0 )
return ret ;
dev_dbg ( vsp1 - > dev , " %s: set format %ux%u (%x) on LIF%u sink \n " ,
__func__ , format . format . width , format . format . height ,
format . format . code , pipe - > lif - > index ) ;
/*
* Verify that the format at the output of the pipeline matches the
* requested frame size and media bus code .
*/
if ( format . format . width ! = drm_pipe - > width | |
format . format . height ! = drm_pipe - > height | |
format . format . code ! = MEDIA_BUS_FMT_ARGB8888_1X32 ) {
dev_dbg ( vsp1 - > dev , " %s: format mismatch on LIF%u \n " , __func__ ,
pipe - > lif - > index ) ;
return - EPIPE ;
}
return 0 ;
}
2018-02-23 15:35:00 -05:00
/* Configure all entities in the pipeline. */
static void vsp1_du_pipeline_configure ( struct vsp1_pipeline * pipe )
{
2018-02-22 14:26:21 -05:00
struct vsp1_drm_pipeline * drm_pipe = to_vsp1_drm_pipeline ( pipe ) ;
2018-02-23 15:35:00 -05:00
struct vsp1_entity * entity ;
struct vsp1_entity * next ;
struct vsp1_dl_list * dl ;
dl = vsp1_dl_list_get ( pipe - > output - > dlm ) ;
list_for_each_entry_safe ( entity , next , & pipe - > entities , list_pipe ) {
2018-02-26 04:22:40 -05:00
/* Disconnect unused entities from the pipeline. */
if ( ! entity - > pipe ) {
2018-02-23 15:35:00 -05:00
vsp1_dl_list_write ( dl , entity - > route - > reg ,
VI6_DPR_NODE_UNUSED ) ;
2018-02-26 04:22:40 -05:00
entity - > sink = NULL ;
2018-02-23 15:35:00 -05:00
list_del ( & entity - > list_pipe ) ;
continue ;
}
vsp1_entity_route_setup ( entity , pipe , dl ) ;
if ( entity - > ops - > configure ) {
entity - > ops - > configure ( entity , pipe , dl ,
VSP1_ENTITY_PARAMS_INIT ) ;
entity - > ops - > configure ( entity , pipe , dl ,
VSP1_ENTITY_PARAMS_RUNTIME ) ;
entity - > ops - > configure ( entity , pipe , dl ,
VSP1_ENTITY_PARAMS_PARTITION ) ;
}
}
2018-02-26 11:06:21 -05:00
vsp1_dl_list_commit ( dl , drm_pipe - > force_brx_release ) ;
2018-02-23 15:35:00 -05:00
}
2015-08-02 18:37:01 -03:00
/* -----------------------------------------------------------------------------
* DU Driver API
*/
int vsp1_du_init ( struct device * dev )
{
struct vsp1_device * vsp1 = dev_get_drvdata ( dev ) ;
if ( ! vsp1 )
return - EPROBE_DEFER ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( vsp1_du_init ) ;
/**
* vsp1_du_setup_lif - Setup the output part of the VSP pipeline
* @ dev : the VSP device
2017-05-25 22:14:24 +03:00
* @ pipe_index : the DRM pipeline index
2017-03-03 06:31:48 -03:00
* @ cfg : the LIF configuration
2015-08-02 18:37:01 -03:00
*
2017-03-03 06:31:48 -03:00
* Configure the output part of VSP DRM pipeline for the given frame @ cfg . width
2018-02-26 11:06:21 -05:00
* and @ cfg . height . This sets up formats on the BRx source pad , the WPF sink and
* source pads , and the LIF sink pad .
2015-08-02 18:37:01 -03:00
*
2017-05-25 22:14:24 +03:00
* The @ pipe_index argument selects which DRM pipeline to setup . The number of
* available pipelines depend on the VSP instance .
*
2017-05-25 00:16:57 +03:00
* As the media bus code on the blend unit source pad is conditioned by the
* configuration of its sink 0 pad , we also set up the formats on all blend unit
2015-08-02 18:37:01 -03:00
* sinks , even if the configuration will be overwritten later by
2017-05-25 00:16:57 +03:00
* vsp1_du_setup_rpf ( ) . This ensures that the blend unit configuration is set to
* a well defined state .
2015-08-02 18:37:01 -03:00
*
* Return 0 on success or a negative error code on failure .
*/
2017-05-25 22:14:24 +03:00
int vsp1_du_setup_lif ( struct device * dev , unsigned int pipe_index ,
const struct vsp1_du_lif_config * cfg )
2015-08-02 18:37:01 -03:00
{
struct vsp1_device * vsp1 = dev_get_drvdata ( dev ) ;
2017-06-23 16:20:47 +03:00
struct vsp1_drm_pipeline * drm_pipe ;
struct vsp1_pipeline * pipe ;
2017-08-15 14:20:11 +03:00
unsigned long flags ;
2015-08-02 18:37:01 -03:00
unsigned int i ;
int ret ;
2017-06-23 16:20:47 +03:00
if ( pipe_index > = vsp1 - > info - > lif_count )
2017-05-25 22:14:24 +03:00
return - EINVAL ;
2017-06-23 16:20:47 +03:00
drm_pipe = & vsp1 - > drm - > pipe [ pipe_index ] ;
pipe = & drm_pipe - > pipe ;
2017-03-03 06:31:48 -03:00
if ( ! cfg ) {
2018-02-26 11:06:21 -05:00
struct vsp1_brx * brx ;
2018-02-22 14:26:21 -05:00
mutex_lock ( & vsp1 - > drm - > lock ) ;
2018-02-26 11:06:21 -05:00
brx = to_brx ( & pipe - > brx - > subdev ) ;
2018-02-22 14:26:21 -05:00
2017-02-26 10:29:50 -03:00
/*
* NULL configuration means the CRTC is being disabled , stop
2015-08-02 18:37:01 -03:00
* the pipeline and turn the light off .
*/
ret = vsp1_pipeline_stop ( pipe ) ;
if ( ret = = - ETIMEDOUT )
dev_err ( vsp1 - > dev , " DRM pipeline stop timeout \n " ) ;
2017-06-23 16:20:47 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( pipe - > inputs ) ; + + i ) {
struct vsp1_rwpf * rpf = pipe - > inputs [ i ] ;
if ( ! rpf )
continue ;
/*
2018-02-26 11:06:21 -05:00
* Remove the RPF from the pipe and the list of BRx
2017-06-23 16:20:47 +03:00
* inputs .
*/
2018-02-22 17:27:47 -05:00
WARN_ON ( ! rpf - > entity . pipe ) ;
2018-02-22 17:22:43 -05:00
rpf - > entity . pipe = NULL ;
2018-02-22 17:27:47 -05:00
list_del ( & rpf - > entity . list_pipe ) ;
2015-08-02 18:37:01 -03:00
pipe - > inputs [ i ] = NULL ;
2017-06-23 16:20:47 +03:00
2018-02-26 11:06:21 -05:00
brx - > inputs [ rpf - > brx_input ] . rpf = NULL ;
2015-08-02 18:37:01 -03:00
}
2017-06-23 16:20:47 +03:00
drm_pipe - > du_complete = NULL ;
2015-08-02 18:37:01 -03:00
pipe - > num_inputs = 0 ;
2018-02-23 18:58:48 -05:00
dev_dbg ( vsp1 - > dev , " %s: pipe %u: releasing %s \n " ,
__func__ , pipe - > lif - > index ,
2018-02-26 11:06:21 -05:00
BRX_NAME ( pipe - > brx ) ) ;
2018-02-23 18:58:48 -05:00
2018-02-26 11:06:21 -05:00
list_del ( & pipe - > brx - > list_pipe ) ;
pipe - > brx - > pipe = NULL ;
pipe - > brx = NULL ;
2018-02-22 14:26:21 -05:00
mutex_unlock ( & vsp1 - > drm - > lock ) ;
2015-11-14 22:27:52 -02:00
vsp1_dlm_reset ( pipe - > output - > dlm ) ;
2015-08-02 18:37:01 -03:00
vsp1_device_put ( vsp1 ) ;
dev_dbg ( vsp1 - > dev , " %s: pipeline disabled \n " , __func__ ) ;
return 0 ;
}
2018-02-22 14:26:21 -05:00
drm_pipe - > width = cfg - > width ;
drm_pipe - > height = cfg - > height ;
2017-06-23 16:20:47 +03:00
dev_dbg ( vsp1 - > dev , " %s: configuring LIF%u with format %ux%u \n " ,
__func__ , pipe_index , cfg - > width , cfg - > height ) ;
2017-03-03 06:31:48 -03:00
2018-02-22 14:26:21 -05:00
mutex_lock ( & vsp1 - > drm - > lock ) ;
2018-02-22 14:26:21 -05:00
/* Setup formats through the pipeline. */
ret = vsp1_du_pipeline_setup_inputs ( vsp1 , pipe ) ;
if ( ret < 0 )
2018-02-22 14:26:21 -05:00
goto unlock ;
2018-02-22 14:26:21 -05:00
2018-02-22 14:26:21 -05:00
ret = vsp1_du_pipeline_setup_output ( vsp1 , pipe ) ;
2015-08-02 18:37:01 -03:00
if ( ret < 0 )
2018-02-22 14:26:21 -05:00
goto unlock ;
2015-08-02 18:37:01 -03:00
2018-02-22 12:12:36 -05:00
/* Enable the VSP1. */
2015-08-02 18:37:01 -03:00
ret = vsp1_device_get ( vsp1 ) ;
if ( ret < 0 )
2018-02-22 14:26:21 -05:00
goto unlock ;
2015-08-02 18:37:01 -03:00
2017-03-04 02:01:17 +00:00
/*
* Register a callback to allow us to notify the DRM driver of frame
* completion events .
*/
2017-06-23 16:20:47 +03:00
drm_pipe - > du_complete = cfg - > callback ;
drm_pipe - > du_private = cfg - > callback_data ;
2017-03-04 02:01:17 +00:00
2017-06-21 14:32:45 +03:00
/* Disable the display interrupts. */
vsp1_write ( vsp1 , VI6_DISP_IRQ_STA , 0 ) ;
vsp1_write ( vsp1 , VI6_DISP_IRQ_ENB , 0 ) ;
2017-08-15 14:20:11 +03:00
/* Configure all entities in the pipeline. */
2018-02-23 15:35:00 -05:00
vsp1_du_pipeline_configure ( pipe ) ;
2017-08-15 14:20:11 +03:00
2018-02-22 14:26:21 -05:00
unlock :
mutex_unlock ( & vsp1 - > drm - > lock ) ;
if ( ret < 0 )
return ret ;
2017-08-15 14:20:11 +03:00
/* Start the pipeline. */
spin_lock_irqsave ( & pipe - > irqlock , flags ) ;
vsp1_pipeline_run ( pipe ) ;
spin_unlock_irqrestore ( & pipe - > irqlock , flags ) ;
2015-08-02 18:37:01 -03:00
dev_dbg ( vsp1 - > dev , " %s: pipeline enabled \n " , __func__ ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( vsp1_du_setup_lif ) ;
/**
2015-09-10 09:28:39 -03:00
* vsp1_du_atomic_begin - Prepare for an atomic update
* @ dev : the VSP device
2017-05-25 22:14:24 +03:00
* @ pipe_index : the DRM pipeline index
2015-09-10 09:28:39 -03:00
*/
2017-05-25 22:14:24 +03:00
void vsp1_du_atomic_begin ( struct device * dev , unsigned int pipe_index )
2015-09-10 09:28:39 -03:00
{
2018-02-22 14:26:21 -05:00
struct vsp1_device * vsp1 = dev_get_drvdata ( dev ) ;
mutex_lock ( & vsp1 - > drm - > lock ) ;
2015-09-10 09:28:39 -03:00
}
EXPORT_SYMBOL_GPL ( vsp1_du_atomic_begin ) ;
/**
* vsp1_du_atomic_update - Setup one RPF input of the VSP pipeline
2015-08-02 18:37:01 -03:00
* @ dev : the VSP device
2017-05-25 22:14:24 +03:00
* @ pipe_index : the DRM pipeline index
2015-08-02 18:37:01 -03:00
* @ rpf_index : index of the RPF to setup ( 0 - based )
2016-04-23 19:08:59 -03:00
* @ cfg : the RPF configuration
2015-08-02 18:37:01 -03:00
*
2016-04-23 19:08:59 -03:00
* Configure the VSP to perform image composition through RPF @ rpf_index as
* described by the @ cfg configuration . The image to compose is referenced by
* @ cfg . mem and composed using the @ cfg . src crop rectangle and the @ cfg . dst
2016-03-24 05:15:59 -03:00
* composition rectangle . The Z - order is configurable with higher @ zpos values
* displayed on top .
2015-08-02 18:37:01 -03:00
*
2016-04-23 19:08:59 -03:00
* If the @ cfg configuration is NULL , the RPF will be disabled . Calling the
2015-08-02 18:37:01 -03:00
* function on a disabled RPF is allowed .
*
2016-04-23 19:08:59 -03:00
* Image format as stored in memory is expressed as a V4L2 @ cfg . pixelformat
* value . The memory pitch is configurable to allow for padding at end of lines ,
* or simply for images that extend beyond the crop rectangle boundaries . The
* @ cfg . pitch value is expressed in bytes and applies to all planes for
* multiplanar formats .
2015-08-02 18:37:01 -03:00
*
* The source memory buffer is referenced by the DMA address of its planes in
2016-04-23 19:08:59 -03:00
* the @ cfg . mem array . Up to two planes are supported . The second plane DMA
* address is ignored for formats using a single plane .
2015-08-02 18:37:01 -03:00
*
* This function isn ' t reentrant , the caller needs to serialize calls .
*
* Return 0 on success or a negative error code on failure .
*/
2017-05-25 22:14:24 +03:00
int vsp1_du_atomic_update ( struct device * dev , unsigned int pipe_index ,
unsigned int rpf_index ,
2016-04-23 20:11:59 -03:00
const struct vsp1_du_atomic_config * cfg )
2015-08-02 18:37:01 -03:00
{
struct vsp1_device * vsp1 = dev_get_drvdata ( dev ) ;
2017-06-23 16:20:47 +03:00
struct vsp1_drm_pipeline * drm_pipe = & vsp1 - > drm - > pipe [ pipe_index ] ;
2015-08-02 18:37:01 -03:00
const struct vsp1_format_info * fmtinfo ;
struct vsp1_rwpf * rpf ;
2015-12-05 20:17:10 -02:00
if ( rpf_index > = vsp1 - > info - > rpf_count )
2015-08-02 18:37:01 -03:00
return - EINVAL ;
rpf = vsp1 - > rpf [ rpf_index ] ;
2016-04-23 19:08:59 -03:00
if ( ! cfg ) {
2015-08-02 18:37:01 -03:00
dev_dbg ( vsp1 - > dev , " %s: RPF%u: disable requested \n " , __func__ ,
rpf_index ) ;
2017-06-23 16:20:47 +03:00
/*
2018-02-26 04:22:40 -05:00
* Remove the RPF from the pipeline ' s inputs . Keep it in the
* pipeline ' s entity list to let vsp1_du_pipeline_configure ( )
* remove it from the hardware pipeline .
2017-06-23 16:20:47 +03:00
*/
2018-02-26 04:22:40 -05:00
rpf - > entity . pipe = NULL ;
2017-06-23 16:20:47 +03:00
drm_pipe - > pipe . inputs [ rpf_index ] = NULL ;
2015-08-02 18:37:01 -03:00
return 0 ;
}
dev_dbg ( vsp1 - > dev ,
2016-08-18 10:16:17 -03:00
" %s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad, %pad } zpos %u \n " ,
2015-08-02 18:37:01 -03:00
__func__ , rpf_index ,
2016-04-23 19:08:59 -03:00
cfg - > src . left , cfg - > src . top , cfg - > src . width , cfg - > src . height ,
cfg - > dst . left , cfg - > dst . top , cfg - > dst . width , cfg - > dst . height ,
cfg - > pixelformat , cfg - > pitch , & cfg - > mem [ 0 ] , & cfg - > mem [ 1 ] ,
2016-08-18 10:16:17 -03:00
& cfg - > mem [ 2 ] , cfg - > zpos ) ;
2015-08-02 18:37:01 -03:00
2016-09-19 15:18:01 -03:00
/*
* Store the format , stride , memory buffer address , crop and compose
2016-03-24 05:15:59 -03:00
* rectangles and Z - order position and for the input .
*/
2016-09-15 16:08:09 -03:00
fmtinfo = vsp1_get_format_info ( vsp1 , cfg - > pixelformat ) ;
2015-08-02 18:37:01 -03:00
if ( ! fmtinfo ) {
dev_dbg ( vsp1 - > dev , " Unsupport pixel format %08x for RPF \n " ,
2016-04-23 19:08:59 -03:00
cfg - > pixelformat ) ;
2015-08-02 18:37:01 -03:00
return - EINVAL ;
}
rpf - > fmtinfo = fmtinfo ;
rpf - > format . num_planes = fmtinfo - > planes ;
2016-04-23 19:08:59 -03:00
rpf - > format . plane_fmt [ 0 ] . bytesperline = cfg - > pitch ;
rpf - > format . plane_fmt [ 1 ] . bytesperline = cfg - > pitch ;
rpf - > alpha = cfg - > alpha ;
2015-08-02 18:37:01 -03:00
2016-04-23 19:08:59 -03:00
rpf - > mem . addr [ 0 ] = cfg - > mem [ 0 ] ;
rpf - > mem . addr [ 1 ] = cfg - > mem [ 1 ] ;
2016-08-18 10:16:17 -03:00
rpf - > mem . addr [ 2 ] = cfg - > mem [ 2 ] ;
2016-03-24 05:15:59 -03:00
2016-04-23 19:08:59 -03:00
vsp1 - > drm - > inputs [ rpf_index ] . crop = cfg - > src ;
vsp1 - > drm - > inputs [ rpf_index ] . compose = cfg - > dst ;
vsp1 - > drm - > inputs [ rpf_index ] . zpos = cfg - > zpos ;
2017-06-23 16:20:47 +03:00
drm_pipe - > pipe . inputs [ rpf_index ] = rpf ;
2016-03-24 05:15:59 -03:00
return 0 ;
}
2016-04-23 20:11:59 -03:00
EXPORT_SYMBOL_GPL ( vsp1_du_atomic_update ) ;
2016-03-24 05:15:59 -03:00
2015-09-10 09:28:39 -03:00
/**
* vsp1_du_atomic_flush - Commit an atomic update
* @ dev : the VSP device
2017-05-25 22:14:24 +03:00
* @ pipe_index : the DRM pipeline index
2015-09-10 09:28:39 -03:00
*/
2017-05-25 22:14:24 +03:00
void vsp1_du_atomic_flush ( struct device * dev , unsigned int pipe_index )
2015-09-10 09:28:39 -03:00
{
struct vsp1_device * vsp1 = dev_get_drvdata ( dev ) ;
2017-06-23 16:20:47 +03:00
struct vsp1_drm_pipeline * drm_pipe = & vsp1 - > drm - > pipe [ pipe_index ] ;
struct vsp1_pipeline * pipe = & drm_pipe - > pipe ;
2015-09-10 09:28:39 -03:00
2018-02-23 15:35:00 -05:00
vsp1_du_pipeline_setup_inputs ( vsp1 , pipe ) ;
2018-02-23 15:35:00 -05:00
vsp1_du_pipeline_configure ( pipe ) ;
2018-02-22 14:26:21 -05:00
mutex_unlock ( & vsp1 - > drm - > lock ) ;
2015-08-02 18:37:01 -03:00
}
2015-09-10 09:28:39 -03:00
EXPORT_SYMBOL_GPL ( vsp1_du_atomic_flush ) ;
2015-08-02 18:37:01 -03:00
2017-05-17 02:20:06 +03:00
int vsp1_du_map_sg ( struct device * dev , struct sg_table * sgt )
{
struct vsp1_device * vsp1 = dev_get_drvdata ( dev ) ;
/*
* As all the buffers allocated by the DU driver are coherent , we can
* skip cache sync . This will need to be revisited when support for
* non - coherent buffers will be added to the DU driver .
*/
return dma_map_sg_attrs ( vsp1 - > bus_master , sgt - > sgl , sgt - > nents ,
DMA_TO_DEVICE , DMA_ATTR_SKIP_CPU_SYNC ) ;
}
EXPORT_SYMBOL_GPL ( vsp1_du_map_sg ) ;
void vsp1_du_unmap_sg ( struct device * dev , struct sg_table * sgt )
{
struct vsp1_device * vsp1 = dev_get_drvdata ( dev ) ;
dma_unmap_sg_attrs ( vsp1 - > bus_master , sgt - > sgl , sgt - > nents ,
DMA_TO_DEVICE , DMA_ATTR_SKIP_CPU_SYNC ) ;
}
EXPORT_SYMBOL_GPL ( vsp1_du_unmap_sg ) ;
2015-08-02 18:37:01 -03:00
/* -----------------------------------------------------------------------------
* Initialization
*/
int vsp1_drm_init ( struct vsp1_device * vsp1 )
{
unsigned int i ;
vsp1 - > drm = devm_kzalloc ( vsp1 - > dev , sizeof ( * vsp1 - > drm ) , GFP_KERNEL ) ;
if ( ! vsp1 - > drm )
return - ENOMEM ;
2018-02-22 14:26:21 -05:00
mutex_init ( & vsp1 - > drm - > lock ) ;
2017-06-23 16:20:47 +03:00
/* Create one DRM pipeline per LIF. */
for ( i = 0 ; i < vsp1 - > info - > lif_count ; + + i ) {
struct vsp1_drm_pipeline * drm_pipe = & vsp1 - > drm - > pipe [ i ] ;
struct vsp1_pipeline * pipe = & drm_pipe - > pipe ;
2015-08-02 18:37:01 -03:00
2018-02-22 14:26:21 -05:00
init_waitqueue_head ( & drm_pipe - > wait_queue ) ;
2017-06-23 16:20:47 +03:00
vsp1_pipeline_init ( pipe ) ;
2015-08-02 18:37:01 -03:00
2018-02-22 17:22:43 -05:00
pipe - > frame_end = vsp1_du_pipeline_frame_end ;
2017-06-23 16:20:47 +03:00
/*
2018-02-22 14:26:21 -05:00
* The output side of the DRM pipeline is static , add the
* corresponding entities manually .
2017-06-23 16:20:47 +03:00
*/
pipe - > output = vsp1 - > wpf [ i ] ;
2018-02-22 17:22:43 -05:00
pipe - > lif = & vsp1 - > lif [ i ] - > entity ;
2017-06-23 16:20:47 +03:00
2018-02-22 17:22:43 -05:00
pipe - > output - > entity . pipe = pipe ;
2017-06-23 16:20:47 +03:00
pipe - > output - > entity . sink = pipe - > lif ;
pipe - > output - > entity . sink_pad = 0 ;
2018-02-22 17:22:43 -05:00
list_add_tail ( & pipe - > output - > entity . list_pipe , & pipe - > entities ) ;
2017-06-23 16:20:47 +03:00
2018-02-22 17:22:43 -05:00
pipe - > lif - > pipe = pipe ;
2017-06-23 16:20:47 +03:00
list_add_tail ( & pipe - > lif - > list_pipe , & pipe - > entities ) ;
}
/* Disable all RPFs initially. */
2015-12-05 20:17:10 -02:00
for ( i = 0 ; i < vsp1 - > info - > rpf_count ; + + i ) {
2015-08-02 18:37:01 -03:00
struct vsp1_rwpf * input = vsp1 - > rpf [ i ] ;
2017-06-23 16:20:47 +03:00
INIT_LIST_HEAD ( & input - > entity . list_pipe ) ;
2015-08-02 18:37:01 -03:00
}
return 0 ;
}
2015-09-07 01:40:25 -03:00
void vsp1_drm_cleanup ( struct vsp1_device * vsp1 )
{
2018-02-22 14:26:21 -05:00
mutex_destroy ( & vsp1 - > drm - > lock ) ;
2015-09-07 01:40:25 -03:00
}