2011-02-02 05:40:08 -03:00
/*
* Samsung TV Mixer driver
*
* Copyright ( c ) 2010 - 2011 Samsung Electronics Co . , Ltd .
*
* Tomasz Stanislawski , < t . stanislaws @ samsung . 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 Foundiation . either version 2 of the License ,
* or ( at your option ) any later version
*/
# include "mixer.h"
# include "regs-vp.h"
# include <media/videobuf2-dma-contig.h>
/* FORMAT DEFINITIONS */
static const struct mxr_format mxr_fmt_nv12 = {
. name = " NV12 " ,
. fourcc = V4L2_PIX_FMT_NV12 ,
. colorspace = V4L2_COLORSPACE_JPEG ,
. num_planes = 2 ,
. plane = {
{ . width = 1 , . height = 1 , . size = 1 } ,
{ . width = 2 , . height = 2 , . size = 2 } ,
} ,
. num_subframes = 1 ,
. cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR ,
} ;
static const struct mxr_format mxr_fmt_nv21 = {
. name = " NV21 " ,
. fourcc = V4L2_PIX_FMT_NV21 ,
. colorspace = V4L2_COLORSPACE_JPEG ,
. num_planes = 2 ,
. plane = {
{ . width = 1 , . height = 1 , . size = 1 } ,
{ . width = 2 , . height = 2 , . size = 2 } ,
} ,
. num_subframes = 1 ,
. cookie = VP_MODE_NV21 | VP_MODE_MEM_LINEAR ,
} ;
static const struct mxr_format mxr_fmt_nv12m = {
. name = " NV12 (mplane) " ,
. fourcc = V4L2_PIX_FMT_NV12M ,
. colorspace = V4L2_COLORSPACE_JPEG ,
. num_planes = 2 ,
. plane = {
{ . width = 1 , . height = 1 , . size = 1 } ,
{ . width = 2 , . height = 2 , . size = 2 } ,
} ,
. num_subframes = 2 ,
. plane2subframe = { 0 , 1 } ,
. cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR ,
} ;
static const struct mxr_format mxr_fmt_nv12mt = {
. name = " NV12 tiled (mplane) " ,
. fourcc = V4L2_PIX_FMT_NV12MT ,
. colorspace = V4L2_COLORSPACE_JPEG ,
. num_planes = 2 ,
. plane = {
{ . width = 128 , . height = 32 , . size = 4096 } ,
{ . width = 128 , . height = 32 , . size = 2048 } ,
} ,
. num_subframes = 2 ,
. plane2subframe = { 0 , 1 } ,
. cookie = VP_MODE_NV12 | VP_MODE_MEM_TILED ,
} ;
static const struct mxr_format * mxr_video_format [ ] = {
& mxr_fmt_nv12 ,
& mxr_fmt_nv21 ,
& mxr_fmt_nv12m ,
& mxr_fmt_nv12mt ,
} ;
/* AUXILIARY CALLBACKS */
static void mxr_vp_layer_release ( struct mxr_layer * layer )
{
mxr_base_layer_unregister ( layer ) ;
mxr_base_layer_release ( layer ) ;
}
static void mxr_vp_buffer_set ( struct mxr_layer * layer ,
struct mxr_buffer * buf )
{
dma_addr_t luma_addr [ 2 ] = { 0 , 0 } ;
dma_addr_t chroma_addr [ 2 ] = { 0 , 0 } ;
if ( buf = = NULL ) {
mxr_reg_vp_buffer ( layer - > mdev , luma_addr , chroma_addr ) ;
return ;
}
2011-08-29 03:20:56 -03:00
luma_addr [ 0 ] = vb2_dma_contig_plane_dma_addr ( & buf - > vb , 0 ) ;
2011-02-02 05:40:08 -03:00
if ( layer - > fmt - > num_subframes = = 2 ) {
2011-08-29 03:20:56 -03:00
chroma_addr [ 0 ] = vb2_dma_contig_plane_dma_addr ( & buf - > vb , 1 ) ;
2011-02-02 05:40:08 -03:00
} else {
/* FIXME: mxr_get_plane_size compute integer division,
* which is slow and should not be performed in interrupt */
chroma_addr [ 0 ] = luma_addr [ 0 ] + mxr_get_plane_size (
& layer - > fmt - > plane [ 0 ] , layer - > geo . src . full_width ,
layer - > geo . src . full_height ) ;
}
if ( layer - > fmt - > cookie & VP_MODE_MEM_TILED ) {
luma_addr [ 1 ] = luma_addr [ 0 ] + 0x40 ;
chroma_addr [ 1 ] = chroma_addr [ 0 ] + 0x40 ;
} else {
luma_addr [ 1 ] = luma_addr [ 0 ] + layer - > geo . src . full_width ;
chroma_addr [ 1 ] = chroma_addr [ 0 ] ;
}
mxr_reg_vp_buffer ( layer - > mdev , luma_addr , chroma_addr ) ;
}
static void mxr_vp_stream_set ( struct mxr_layer * layer , int en )
{
mxr_reg_vp_layer_stream ( layer - > mdev , en ) ;
}
static void mxr_vp_format_set ( struct mxr_layer * layer )
{
mxr_reg_vp_format ( layer - > mdev , layer - > fmt , & layer - > geo ) ;
}
2011-08-25 07:14:26 -03:00
static inline unsigned int do_center ( unsigned int center ,
unsigned int size , unsigned int upper , unsigned int flags )
2011-02-02 05:40:08 -03:00
{
2011-08-25 07:14:26 -03:00
unsigned int lower ;
if ( flags & MXR_NO_OFFSET )
return 0 ;
lower = center - min ( center , size / 2 ) ;
return min ( lower , upper - size ) ;
}
2011-02-02 05:40:08 -03:00
2011-08-25 07:14:26 -03:00
static void mxr_vp_fix_geometry ( struct mxr_layer * layer ,
enum mxr_geometry_stage stage , unsigned long flags )
{
struct mxr_geometry * geo = & layer - > geo ;
struct mxr_crop * src = & geo - > src ;
struct mxr_crop * dst = & geo - > dst ;
unsigned long x_center , y_center ;
switch ( stage ) {
case MXR_GEOMETRY_SINK : /* nothing to be fixed here */
case MXR_GEOMETRY_COMPOSE :
/* remember center of the area */
x_center = dst - > x_offset + dst - > width / 2 ;
y_center = dst - > y_offset + dst - > height / 2 ;
/* ensure that compose is reachable using 16x scaling */
dst - > width = clamp ( dst - > width , 8U , 16 * src - > full_width ) ;
dst - > height = clamp ( dst - > height , 1U , 16 * src - > full_height ) ;
/* setup offsets */
dst - > x_offset = do_center ( x_center , dst - > width ,
dst - > full_width , flags ) ;
dst - > y_offset = do_center ( y_center , dst - > height ,
dst - > full_height , flags ) ;
flags = 0 ; /* remove possible MXR_NO_OFFSET flag */
/* fall through */
case MXR_GEOMETRY_CROP :
/* remember center of the area */
x_center = src - > x_offset + src - > width / 2 ;
y_center = src - > y_offset + src - > height / 2 ;
/* ensure scaling is between 0.25x .. 16x */
2011-11-25 05:56:18 -03:00
src - > width = clamp ( src - > width , round_up ( dst - > width / 16 , 4 ) ,
dst - > width * 4 ) ;
src - > height = clamp ( src - > height , round_up ( dst - > height / 16 , 4 ) ,
dst - > height * 4 ) ;
2011-08-25 07:14:26 -03:00
/* hardware limits */
src - > width = clamp ( src - > width , 32U , 2047U ) ;
src - > height = clamp ( src - > height , 4U , 2047U ) ;
/* setup offsets */
src - > x_offset = do_center ( x_center , src - > width ,
src - > full_width , flags ) ;
src - > y_offset = do_center ( y_center , src - > height ,
src - > full_height , flags ) ;
/* setting scaling ratio */
geo - > x_ratio = ( src - > width < < 16 ) / dst - > width ;
geo - > y_ratio = ( src - > height < < 16 ) / dst - > height ;
/* fall through */
case MXR_GEOMETRY_SOURCE :
src - > full_width = clamp ( src - > full_width ,
ALIGN ( src - > width + src - > x_offset , 8 ) , 8192U ) ;
src - > full_height = clamp ( src - > full_height ,
src - > height + src - > y_offset , 8192U ) ;
} ;
2011-02-02 05:40:08 -03:00
}
/* PUBLIC API */
struct mxr_layer * mxr_vp_layer_create ( struct mxr_device * mdev , int idx )
{
struct mxr_layer * layer ;
int ret ;
struct mxr_layer_ops ops = {
. release = mxr_vp_layer_release ,
. buffer_set = mxr_vp_buffer_set ,
. stream_set = mxr_vp_stream_set ,
. format_set = mxr_vp_format_set ,
. fix_geometry = mxr_vp_fix_geometry ,
} ;
char name [ 32 ] ;
sprintf ( name , " video%d " , idx ) ;
layer = mxr_base_layer_create ( mdev , idx , name , & ops ) ;
if ( layer = = NULL ) {
mxr_err ( mdev , " failed to initialize layer(%d) base \n " , idx ) ;
goto fail ;
}
layer - > fmt_array = mxr_video_format ;
layer - > fmt_array_size = ARRAY_SIZE ( mxr_video_format ) ;
ret = mxr_base_layer_register ( layer ) ;
if ( ret )
goto fail_layer ;
return layer ;
fail_layer :
mxr_base_layer_release ( layer ) ;
fail :
return NULL ;
}