2013-06-19 13:54:11 +02:00
/*
* rcar_du_kms . c - - R - Car Display Unit Mode Setting
*
* Copyright ( C ) 2013 Renesas 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 <drm/drmP.h>
# include <drm/drm_crtc.h>
# include <drm/drm_crtc_helper.h>
# include <drm/drm_fb_cma_helper.h>
# include <drm/drm_gem_cma_helper.h>
# include "rcar_du_crtc.h"
# include "rcar_du_drv.h"
2013-06-15 15:02:12 +02:00
# include "rcar_du_encoder.h"
2013-06-19 13:54:11 +02:00
# include "rcar_du_kms.h"
# include "rcar_du_regs.h"
/* -----------------------------------------------------------------------------
* Format helpers
*/
static const struct rcar_du_format_info rcar_du_format_infos [ ] = {
{
. fourcc = DRM_FORMAT_RGB565 ,
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_ARGB1555 ,
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_XRGB1555 ,
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_XRGB8888 ,
. bpp = 32 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP ,
. edf = PnDDCR4_EDF_RGB888 ,
} , {
. fourcc = DRM_FORMAT_ARGB8888 ,
. bpp = 32 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP ,
. edf = PnDDCR4_EDF_ARGB8888 ,
} , {
. fourcc = DRM_FORMAT_UYVY ,
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_YUYV ,
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_NV12 ,
. bpp = 12 ,
. planes = 2 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_NV21 ,
. bpp = 12 ,
. planes = 2 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} , {
/* In YUV 4:2:2, only NV16 is supported (NV61 isn't) */
. fourcc = DRM_FORMAT_NV16 ,
. bpp = 16 ,
. planes = 2 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} ,
} ;
const struct rcar_du_format_info * rcar_du_format_info ( u32 fourcc )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( rcar_du_format_infos ) ; + + i ) {
if ( rcar_du_format_infos [ i ] . fourcc = = fourcc )
return & rcar_du_format_infos [ i ] ;
}
return NULL ;
}
/* -----------------------------------------------------------------------------
* Frame buffer
*/
2013-07-04 20:05:51 +02:00
int rcar_du_dumb_create ( struct drm_file * file , struct drm_device * dev ,
struct drm_mode_create_dumb * args )
{
2013-06-14 20:52:52 +02:00
struct rcar_du_device * rcdu = dev - > dev_private ;
2013-07-04 20:05:51 +02:00
unsigned int min_pitch = DIV_ROUND_UP ( args - > width * args - > bpp , 8 ) ;
unsigned int align ;
2013-06-14 20:52:52 +02:00
/* The R8A7779 DU requires a 16 pixels pitch alignment as documented,
* but the R8A7790 DU seems to require a 128 bytes pitch alignment .
*/
if ( rcar_du_has ( rcdu , RCAR_DU_FEATURE_ALIGN_128B ) )
align = 128 ;
else
align = 16 * args - > bpp / 8 ;
2013-07-04 20:05:51 +02:00
args - > pitch = roundup ( max ( args - > pitch , min_pitch ) , align ) ;
return drm_gem_cma_dumb_create ( file , dev , args ) ;
}
2013-06-19 13:54:11 +02:00
static struct drm_framebuffer *
rcar_du_fb_create ( struct drm_device * dev , struct drm_file * file_priv ,
struct drm_mode_fb_cmd2 * mode_cmd )
{
2013-06-14 20:52:52 +02:00
struct rcar_du_device * rcdu = dev - > dev_private ;
2013-06-19 13:54:11 +02:00
const struct rcar_du_format_info * format ;
2013-07-04 20:05:51 +02:00
unsigned int align ;
2013-06-19 13:54:11 +02:00
format = rcar_du_format_info ( mode_cmd - > pixel_format ) ;
if ( format = = NULL ) {
dev_dbg ( dev - > dev , " unsupported pixel format %08x \n " ,
mode_cmd - > pixel_format ) ;
return ERR_PTR ( - EINVAL ) ;
}
2013-06-14 20:52:52 +02:00
if ( rcar_du_has ( rcdu , RCAR_DU_FEATURE_ALIGN_128B ) )
align = 128 ;
else
align = 16 * format - > bpp / 8 ;
2013-07-04 20:05:51 +02:00
if ( mode_cmd - > pitches [ 0 ] & ( align - 1 ) | |
mode_cmd - > pitches [ 0 ] > = 8192 ) {
2013-06-19 13:54:11 +02:00
dev_dbg ( dev - > dev , " invalid pitch value %u \n " ,
mode_cmd - > pitches [ 0 ] ) ;
return ERR_PTR ( - EINVAL ) ;
}
if ( format - > planes = = 2 ) {
if ( mode_cmd - > pitches [ 1 ] ! = mode_cmd - > pitches [ 0 ] ) {
dev_dbg ( dev - > dev ,
" luma and chroma pitches do not match \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
}
return drm_fb_cma_create ( dev , file_priv , mode_cmd ) ;
}
static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
. fb_create = rcar_du_fb_create ,
} ;
int rcar_du_modeset_init ( struct rcar_du_device * rcdu )
{
2013-06-17 00:29:25 +02:00
static const unsigned int mmio_offsets [ ] = {
DU0_REG_OFFSET , DU2_REG_OFFSET
} ;
2013-06-19 13:54:11 +02:00
struct drm_device * dev = rcdu - > ddev ;
struct drm_encoder * encoder ;
2013-06-17 00:29:25 +02:00
unsigned int num_groups ;
2013-06-19 13:54:11 +02:00
unsigned int i ;
int ret ;
drm_mode_config_init ( rcdu - > ddev ) ;
rcdu - > ddev - > mode_config . min_width = 0 ;
rcdu - > ddev - > mode_config . min_height = 0 ;
rcdu - > ddev - > mode_config . max_width = 4095 ;
rcdu - > ddev - > mode_config . max_height = 2047 ;
rcdu - > ddev - > mode_config . funcs = & rcar_du_mode_config_funcs ;
2013-06-17 00:29:25 +02:00
rcdu - > num_crtcs = rcdu - > info - > num_crtcs ;
/* Initialize the groups. */
num_groups = DIV_ROUND_UP ( rcdu - > num_crtcs , 2 ) ;
for ( i = 0 ; i < num_groups ; + + i ) {
struct rcar_du_group * rgrp = & rcdu - > groups [ i ] ;
2013-06-16 21:01:02 +02:00
2013-06-17 00:29:25 +02:00
rgrp - > dev = rcdu ;
rgrp - > mmio_offset = mmio_offsets [ i ] ;
rgrp - > index = i ;
2013-06-19 13:54:11 +02:00
2013-06-17 00:29:25 +02:00
ret = rcar_du_planes_init ( rgrp ) ;
2013-07-04 20:05:50 +02:00
if ( ret < 0 )
return ret ;
}
2013-06-19 13:54:11 +02:00
2013-06-17 00:29:25 +02:00
/* Create the CRTCs. */
for ( i = 0 ; i < rcdu - > num_crtcs ; + + i ) {
struct rcar_du_group * rgrp = & rcdu - > groups [ i / 2 ] ;
ret = rcar_du_crtc_create ( rgrp , i ) ;
if ( ret < 0 )
return ret ;
}
2013-06-19 13:54:11 +02:00
2013-06-17 00:29:25 +02:00
/* Initialize the encoders. */
2013-06-19 13:54:11 +02:00
for ( i = 0 ; i < rcdu - > pdata - > num_encoders ; + + i ) {
const struct rcar_du_encoder_data * pdata =
& rcdu - > pdata - > encoders [ i ] ;
2013-06-17 03:13:11 +02:00
const struct rcar_du_output_routing * route =
& rcdu - > info - > routes [ pdata - > output ] ;
2013-06-19 13:54:11 +02:00
2013-06-16 16:25:35 +02:00
if ( pdata - > type = = RCAR_DU_ENCODER_UNUSED )
2013-06-15 15:02:12 +02:00
continue ;
2013-06-17 03:13:11 +02:00
if ( pdata - > output > = RCAR_DU_OUTPUT_MAX | |
route - > possible_crtcs = = 0 ) {
2013-06-19 13:54:11 +02:00
dev_warn ( rcdu - > dev ,
" encoder %u references unexisting output %u, skipping \n " ,
i , pdata - > output ) ;
continue ;
}
2013-06-16 16:25:35 +02:00
rcar_du_encoder_init ( rcdu , pdata - > type , pdata - > output , pdata ) ;
2013-06-19 13:54:11 +02:00
}
2013-06-17 03:13:11 +02:00
/* Set the possible CRTCs and possible clones. There's always at least
* one way for all encoders to clone each other , set all bits in the
* possible clones field .
2013-06-19 13:54:11 +02:00
*/
list_for_each_entry ( encoder , & dev - > mode_config . encoder_list , head ) {
struct rcar_du_encoder * renc = to_rcar_encoder ( encoder ) ;
2013-06-17 03:13:11 +02:00
const struct rcar_du_output_routing * route =
& rcdu - > info - > routes [ renc - > output ] ;
2013-06-19 13:54:11 +02:00
2013-06-17 03:13:11 +02:00
encoder - > possible_crtcs = route - > possible_crtcs ;
encoder - > possible_clones = ( 1 < < rcdu - > pdata - > num_encoders ) - 1 ;
2013-06-19 13:54:11 +02:00
}
2013-06-17 00:29:25 +02:00
/* Now that the CRTCs have been initialized register the planes. */
for ( i = 0 ; i < num_groups ; + + i ) {
ret = rcar_du_planes_register ( & rcdu - > groups [ i ] ) ;
if ( ret < 0 )
return ret ;
}
2013-06-19 13:54:11 +02:00
drm_kms_helper_poll_init ( rcdu - > ddev ) ;
drm_helper_disable_unused_functions ( rcdu - > ddev ) ;
return 0 ;
}