2018-04-22 17:33:20 -04:00
// SPDX-License-Identifier: GPL-2.0+
2013-06-04 11:22:30 -03:00
/*
* vsp1_rpf . c - - R - Car VSP1 Read Pixel Formatter
*
2014-02-06 14:42:31 -03:00
* Copyright ( C ) 2013 - 2014 Renesas Electronics Corporation
2013-06-04 11:22:30 -03:00
*
* Contact : Laurent Pinchart ( laurent . pinchart @ ideasonboard . com )
*/
# include <linux/device.h>
# include <media/v4l2-subdev.h>
# include "vsp1.h"
2015-11-22 20:29:25 -02:00
# include "vsp1_dl.h"
2016-01-17 19:53:56 -02:00
# include "vsp1_pipe.h"
2013-06-04 11:22:30 -03:00
# include "vsp1_rwpf.h"
# include "vsp1_video.h"
# define RPF_MAX_WIDTH 8190
# define RPF_MAX_HEIGHT 8190
2018-08-03 07:37:29 -04:00
/* Pre extended display list command data structure. */
struct vsp1_extcmd_auto_fld_body {
u32 top_y0 ;
u32 bottom_y0 ;
u32 top_c0 ;
u32 bottom_c0 ;
u32 top_c1 ;
u32 bottom_c1 ;
u32 reserved0 ;
u32 reserved1 ;
} __packed ;
2013-06-04 11:22:30 -03:00
/* -----------------------------------------------------------------------------
* Device Access
*/
2015-11-22 20:29:25 -02:00
static inline void vsp1_rpf_write ( struct vsp1_rwpf * rpf ,
2018-05-18 16:42:02 -04:00
struct vsp1_dl_body * dlb , u32 reg , u32 data )
2013-06-04 11:22:30 -03:00
{
2018-05-18 16:42:02 -04:00
vsp1_dl_body_write ( dlb , reg + rpf - > entity . index * VI6_RPF_OFFSET ,
data ) ;
2013-06-04 11:22:30 -03:00
}
/* -----------------------------------------------------------------------------
2015-11-17 13:10:26 -02:00
* V4L2 Subdevice Operations
2013-06-04 11:22:30 -03:00
*/
2016-06-17 21:11:26 -03:00
static const struct v4l2_subdev_ops rpf_ops = {
2015-11-22 13:37:45 -02:00
. pad = & vsp1_rwpf_pad_ops ,
2015-11-17 13:10:26 -02:00
} ;
/* -----------------------------------------------------------------------------
* VSP1 Entity Operations
*/
2018-05-18 16:42:01 -04:00
static void rpf_configure_stream ( struct vsp1_entity * entity ,
struct vsp1_pipeline * pipe ,
2019-03-11 20:13:43 +02:00
struct vsp1_dl_list * dl ,
2018-05-18 16:42:02 -04:00
struct vsp1_dl_body * dlb )
2015-11-17 13:10:26 -02:00
{
struct vsp1_rwpf * rpf = to_rwpf ( & entity - > subdev ) ;
2015-07-28 14:00:43 -03:00
const struct vsp1_format_info * fmtinfo = rpf - > fmtinfo ;
const struct v4l2_pix_format_mplane * format = & rpf - > format ;
2015-11-15 19:14:22 -02:00
const struct v4l2_mbus_framefmt * source_format ;
const struct v4l2_mbus_framefmt * sink_format ;
2015-11-15 19:14:22 -02:00
unsigned int left = 0 ;
unsigned int top = 0 ;
2013-06-04 11:22:30 -03:00
u32 pstride ;
u32 infmt ;
2014-05-21 19:00:05 -03:00
2016-09-12 09:50:13 -03:00
/* Stride */
2013-06-04 11:22:30 -03:00
pstride = format - > plane_fmt [ 0 ] . bytesperline
< < VI6_RPF_SRCM_PSTRIDE_Y_SHIFT ;
2016-09-12 09:50:13 -03:00
if ( format - > num_planes > 1 )
2013-06-04 11:22:30 -03:00
pstride | = format - > plane_fmt [ 1 ] . bytesperline
< < VI6_RPF_SRCM_PSTRIDE_C_SHIFT ;
2018-08-03 07:37:29 -04:00
/*
* pstride has both STRIDE_Y and STRIDE_C , but multiplying the whole
* of pstride by 2 is conveniently OK here as we are multiplying both
* values .
*/
if ( pipe - > interlaced )
pstride * = 2 ;
2018-05-18 16:42:02 -04:00
vsp1_rpf_write ( rpf , dlb , VI6_RPF_SRCM_PSTRIDE , pstride ) ;
2013-06-04 11:22:30 -03:00
/* Format */
2015-11-15 19:14:22 -02:00
sink_format = vsp1_entity_get_pad_format ( & rpf - > entity ,
rpf - > entity . config ,
RWPF_PAD_SINK ) ;
source_format = vsp1_entity_get_pad_format ( & rpf - > entity ,
rpf - > entity . config ,
RWPF_PAD_SOURCE ) ;
2013-06-04 11:22:30 -03:00
infmt = VI6_RPF_INFMT_CIPM
| ( fmtinfo - > hwfmt < < VI6_RPF_INFMT_RDFMT_SHIFT ) ;
if ( fmtinfo - > swap_yc )
infmt | = VI6_RPF_INFMT_SPYCS ;
if ( fmtinfo - > swap_uv )
infmt | = VI6_RPF_INFMT_SPUVS ;
2015-11-15 19:14:22 -02:00
if ( sink_format - > code ! = source_format - > code )
2013-06-04 11:22:30 -03:00
infmt | = VI6_RPF_INFMT_CSC ;
2018-05-18 16:42:02 -04:00
vsp1_rpf_write ( rpf , dlb , VI6_RPF_INFMT , infmt ) ;
vsp1_rpf_write ( rpf , dlb , VI6_RPF_DSWAP , fmtinfo - > swap ) ;
2013-06-04 11:22:30 -03:00
2018-08-31 10:40:44 -04:00
/* Output location. */
2018-02-26 11:06:21 -05:00
if ( pipe - > brx ) {
2015-11-15 19:14:22 -02:00
const struct v4l2_rect * compose ;
2018-02-26 11:06:21 -05:00
compose = vsp1_entity_get_pad_selection ( pipe - > brx ,
pipe - > brx - > config ,
rpf - > brx_input ,
2016-03-03 20:17:49 -03:00
V4L2_SEL_TGT_COMPOSE ) ;
2015-11-15 19:14:22 -02:00
left = compose - > left ;
top = compose - > top ;
}
2018-08-03 07:37:29 -04:00
if ( pipe - > interlaced )
top / = 2 ;
2018-05-18 16:42:02 -04:00
vsp1_rpf_write ( rpf , dlb , VI6_RPF_LOC ,
2015-11-15 19:14:22 -02:00
( left < < VI6_RPF_LOC_HCOORD_SHIFT ) |
( top < < VI6_RPF_LOC_VCOORD_SHIFT ) ) ;
2013-06-04 11:22:30 -03:00
2017-02-26 10:29:50 -03:00
/*
* On Gen2 use the alpha channel ( extended to 8 bits ) when available or
2016-03-22 11:10:27 -03:00
* a fixed alpha value set through the V4L2_CID_ALPHA_COMPONENT control
* otherwise .
*
* The Gen3 RPF has extended alpha capability and can both multiply the
* alpha channel by a fixed global alpha value , and multiply the pixel
* components to convert the input to premultiplied alpha .
*
2018-02-26 11:06:21 -05:00
* As alpha premultiplication is available in the BRx for both Gen2 and
2016-03-22 11:10:27 -03:00
* Gen3 we handle it there and use the Gen3 alpha multiplier for global
* alpha multiplication only . This however prevents conversion to
2018-02-26 11:06:21 -05:00
* premultiplied alpha if no BRx is present in the pipeline . If that use
2016-03-22 11:10:27 -03:00
* case turns out to be useful we will revisit the implementation ( for
* Gen3 only ) .
*
* We enable alpha multiplication on Gen3 using the fixed alpha value
* set through the V4L2_CID_ALPHA_COMPONENT control when the input
* contains an alpha channel . On Gen2 the global alpha is ignored in
* that case .
*
* In all cases , disable color keying .
2013-06-04 11:22:30 -03:00
*/
2018-05-18 16:42:02 -04:00
vsp1_rpf_write ( rpf , dlb , VI6_RPF_ALPH_SEL , VI6_RPF_ALPH_SEL_AEXT_EXT |
2014-05-26 20:12:53 -03:00
( fmtinfo - > alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
: VI6_RPF_ALPH_SEL_ASEL_FIXED ) ) ;
2015-08-05 16:57:35 -03:00
2016-03-22 11:10:27 -03:00
if ( entity - > vsp1 - > info - > gen = = 3 ) {
u32 mult ;
if ( fmtinfo - > alpha ) {
2017-02-26 10:29:50 -03:00
/*
* When the input contains an alpha channel enable the
2016-03-22 11:10:27 -03:00
* alpha multiplier . If the input is premultiplied we
* need to multiply both the alpha channel and the pixel
* components by the global alpha value to keep them
* premultiplied . Otherwise multiply the alpha channel
* only .
*/
bool premultiplied = format - > flags
& V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ;
mult = VI6_RPF_MULT_ALPHA_A_MMD_RATIO
| ( premultiplied ?
VI6_RPF_MULT_ALPHA_P_MMD_RATIO :
2016-06-20 05:04:38 -03:00
VI6_RPF_MULT_ALPHA_P_MMD_NONE ) ;
2016-03-22 11:10:27 -03:00
} else {
2017-02-26 10:29:50 -03:00
/*
* When the input doesn ' t contain an alpha channel the
2016-03-22 11:10:27 -03:00
* global alpha value is applied in the unpacking unit ,
* the alpha multiplier isn ' t needed and must be
* disabled .
*/
mult = VI6_RPF_MULT_ALPHA_A_MMD_NONE
| VI6_RPF_MULT_ALPHA_P_MMD_NONE ;
}
2016-06-20 05:04:38 -03:00
rpf - > mult_alpha = mult ;
2016-03-22 11:10:27 -03:00
}
2018-05-18 16:42:02 -04:00
vsp1_rpf_write ( rpf , dlb , VI6_RPF_MSK_CTRL , 0 ) ;
vsp1_rpf_write ( rpf , dlb , VI6_RPF_CKEY_CTRL , 0 ) ;
2016-03-22 11:10:27 -03:00
2013-06-04 11:22:30 -03:00
}
2018-08-03 07:37:29 -04:00
static void vsp1_rpf_configure_autofld ( struct vsp1_rwpf * rpf ,
struct vsp1_dl_list * dl )
{
const struct v4l2_pix_format_mplane * format = & rpf - > format ;
struct vsp1_dl_ext_cmd * cmd ;
struct vsp1_extcmd_auto_fld_body * auto_fld ;
u32 offset_y , offset_c ;
cmd = vsp1_dl_get_pre_cmd ( dl ) ;
if ( WARN_ONCE ( ! cmd , " Failed to obtain an autofld cmd " ) )
return ;
/* Re-index our auto_fld to match the current RPF. */
auto_fld = cmd - > data ;
auto_fld = & auto_fld [ rpf - > entity . index ] ;
auto_fld - > top_y0 = rpf - > mem . addr [ 0 ] ;
auto_fld - > top_c0 = rpf - > mem . addr [ 1 ] ;
auto_fld - > top_c1 = rpf - > mem . addr [ 2 ] ;
offset_y = format - > plane_fmt [ 0 ] . bytesperline ;
offset_c = format - > plane_fmt [ 1 ] . bytesperline ;
auto_fld - > bottom_y0 = rpf - > mem . addr [ 0 ] + offset_y ;
auto_fld - > bottom_c0 = rpf - > mem . addr [ 1 ] + offset_c ;
auto_fld - > bottom_c1 = rpf - > mem . addr [ 2 ] + offset_c ;
cmd - > flags | = VI6_DL_EXT_AUTOFLD_INT | BIT ( 16 + rpf - > entity . index ) ;
}
2018-05-18 16:42:01 -04:00
static void rpf_configure_frame ( struct vsp1_entity * entity ,
struct vsp1_pipeline * pipe ,
2018-05-18 16:42:02 -04:00
struct vsp1_dl_list * dl ,
struct vsp1_dl_body * dlb )
2018-05-18 16:42:01 -04:00
{
struct vsp1_rwpf * rpf = to_rwpf ( & entity - > subdev ) ;
2018-05-18 16:42:02 -04:00
vsp1_rpf_write ( rpf , dlb , VI6_RPF_VRTCOL_SET ,
2018-05-18 16:42:01 -04:00
rpf - > alpha < < VI6_RPF_VRTCOL_SET_LAYA_SHIFT ) ;
2018-05-18 16:42:02 -04:00
vsp1_rpf_write ( rpf , dlb , VI6_RPF_MULT_ALPHA , rpf - > mult_alpha |
2018-05-18 16:42:01 -04:00
( rpf - > alpha < < VI6_RPF_MULT_ALPHA_RATIO_SHIFT ) ) ;
2018-05-18 16:42:02 -04:00
vsp1_pipeline_propagate_alpha ( pipe , dlb , rpf - > alpha ) ;
2018-05-18 16:42:01 -04:00
}
static void rpf_configure_partition ( struct vsp1_entity * entity ,
struct vsp1_pipeline * pipe ,
2018-05-18 16:42:02 -04:00
struct vsp1_dl_list * dl ,
struct vsp1_dl_body * dlb )
2018-05-18 16:42:01 -04:00
{
struct vsp1_rwpf * rpf = to_rwpf ( & entity - > subdev ) ;
struct vsp1_rwpf_memory mem = rpf - > mem ;
struct vsp1_device * vsp1 = rpf - > entity . vsp1 ;
const struct vsp1_format_info * fmtinfo = rpf - > fmtinfo ;
const struct v4l2_pix_format_mplane * format = & rpf - > format ;
struct v4l2_rect crop ;
/*
* Source size and crop offsets .
*
* The crop offsets correspond to the location of the crop
* rectangle top left corner in the plane buffer . Only two
* offsets are needed , as planes 2 and 3 always have identical
* strides .
*/
crop = * vsp1_rwpf_get_crop ( rpf , rpf - > entity . config ) ;
/*
* Partition Algorithm Control
*
* The partition algorithm can split this frame into multiple
* slices . We must scale our partition window based on the pipe
* configuration to match the destination partition window .
* To achieve this , we adjust our crop to provide a ' sub - crop '
* matching the expected partition window . Only ' left ' and
* ' width ' need to be adjusted .
*/
if ( pipe - > partitions > 1 ) {
crop . width = pipe - > partition - > rpf . width ;
crop . left + = pipe - > partition - > rpf . left ;
}
2018-08-03 07:37:29 -04:00
if ( pipe - > interlaced ) {
crop . height = round_down ( crop . height / 2 , fmtinfo - > vsub ) ;
crop . top = round_down ( crop . top / 2 , fmtinfo - > vsub ) ;
}
2018-05-18 16:42:02 -04:00
vsp1_rpf_write ( rpf , dlb , VI6_RPF_SRC_BSIZE ,
2018-05-18 16:42:01 -04:00
( crop . width < < VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT ) |
( crop . height < < VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT ) ) ;
2018-05-18 16:42:02 -04:00
vsp1_rpf_write ( rpf , dlb , VI6_RPF_SRC_ESIZE ,
2018-05-18 16:42:01 -04:00
( crop . width < < VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT ) |
( crop . height < < VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT ) ) ;
mem . addr [ 0 ] + = crop . top * format - > plane_fmt [ 0 ] . bytesperline
+ crop . left * fmtinfo - > bpp [ 0 ] / 8 ;
if ( format - > num_planes > 1 ) {
unsigned int offset ;
offset = crop . top * format - > plane_fmt [ 1 ] . bytesperline
+ crop . left / fmtinfo - > hsub
* fmtinfo - > bpp [ 1 ] / 8 ;
mem . addr [ 1 ] + = offset ;
mem . addr [ 2 ] + = offset ;
}
/*
* On Gen3 hardware the SPUVS bit has no effect on 3 - planar
* formats . Swap the U and V planes manually in that case .
*/
if ( vsp1 - > info - > gen = = 3 & & format - > num_planes = = 3 & &
fmtinfo - > swap_uv )
swap ( mem . addr [ 1 ] , mem . addr [ 2 ] ) ;
2018-08-03 07:37:29 -04:00
/*
* Interlaced pipelines will use the extended pre - cmd to process
2018-08-31 10:40:44 -04:00
* SRCM_ADDR_ { Y , C0 , C1 } .
2018-08-03 07:37:29 -04:00
*/
if ( pipe - > interlaced ) {
vsp1_rpf_configure_autofld ( rpf , dl ) ;
} else {
vsp1_rpf_write ( rpf , dlb , VI6_RPF_SRCM_ADDR_Y , mem . addr [ 0 ] ) ;
vsp1_rpf_write ( rpf , dlb , VI6_RPF_SRCM_ADDR_C0 , mem . addr [ 1 ] ) ;
vsp1_rpf_write ( rpf , dlb , VI6_RPF_SRCM_ADDR_C1 , mem . addr [ 2 ] ) ;
}
2018-05-18 16:42:01 -04:00
}
2017-08-04 12:32:44 -04:00
static void rpf_partition ( struct vsp1_entity * entity ,
struct vsp1_pipeline * pipe ,
struct vsp1_partition * partition ,
unsigned int partition_idx ,
struct vsp1_partition_window * window )
{
partition - > rpf = * window ;
}
2015-11-17 12:23:23 -02:00
static const struct vsp1_entity_operations rpf_entity_ops = {
2018-05-18 16:42:01 -04:00
. configure_stream = rpf_configure_stream ,
. configure_frame = rpf_configure_frame ,
. configure_partition = rpf_configure_partition ,
2017-08-04 12:32:44 -04:00
. partition = rpf_partition ,
2013-06-04 11:22:30 -03:00
} ;
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_rwpf * vsp1_rpf_create ( struct vsp1_device * vsp1 , unsigned int index )
{
struct vsp1_rwpf * rpf ;
2015-11-15 19:42:01 -02:00
char name [ 6 ] ;
2013-06-04 11:22:30 -03:00
int ret ;
rpf = devm_kzalloc ( vsp1 - > dev , sizeof ( * rpf ) , GFP_KERNEL ) ;
if ( rpf = = NULL )
return ERR_PTR ( - ENOMEM ) ;
rpf - > max_width = RPF_MAX_WIDTH ;
rpf - > max_height = RPF_MAX_HEIGHT ;
2015-11-17 12:23:23 -02:00
rpf - > entity . ops = & rpf_entity_ops ;
2013-06-04 11:22:30 -03:00
rpf - > entity . type = VSP1_ENTITY_RPF ;
rpf - > entity . index = index ;
2015-11-15 19:42:01 -02:00
sprintf ( name , " rpf.%u " , index ) ;
2016-02-15 22:10:26 -02:00
ret = vsp1_entity_init ( vsp1 , & rpf - > entity , name , 2 , & rpf_ops ,
MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER ) ;
2013-06-04 11:22:30 -03:00
if ( ret < 0 )
return ERR_PTR ( ret ) ;
2014-05-21 19:00:05 -03:00
/* Initialize the control handler. */
2016-05-26 05:14:22 -03:00
ret = vsp1_rwpf_init_ctrls ( rpf , 0 ) ;
2015-11-01 12:19:42 -02:00
if ( ret < 0 ) {
2014-05-21 19:00:05 -03:00
dev_err ( vsp1 - > dev , " rpf%u: failed to initialize controls \n " ,
index ) ;
goto error ;
}
2016-06-20 05:04:38 -03:00
v4l2_ctrl_handler_setup ( & rpf - > ctrls ) ;
2013-06-04 11:22:30 -03:00
return rpf ;
2014-05-28 12:49:13 -03:00
error :
vsp1_entity_destroy ( & rpf - > entity ) ;
2013-06-04 11:22:30 -03:00
return ERR_PTR ( ret ) ;
}