2018-01-22 10:25:22 +01:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright ( C ) 2017 Free Electrons
* Maxime Ripard < maxime . ripard @ free - electrons . com >
*/
# include <linux/clk.h>
# include <linux/component.h>
# include <linux/module.h>
2019-01-18 15:51:28 +01:00
# include <linux/of_device.h>
2018-01-22 10:25:22 +01:00
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
# include <linux/reset.h>
2019-07-16 08:42:06 +02:00
# include <drm/drm_device.h>
2022-08-02 02:04:02 +02:00
# include <drm/drm_fb_dma_helper.h>
2019-07-16 08:42:06 +02:00
# include <drm/drm_fourcc.h>
# include <drm/drm_framebuffer.h>
2022-08-02 02:04:03 +02:00
# include <drm/drm_gem_dma_helper.h>
2019-07-16 08:42:06 +02:00
# include <drm/drm_plane.h>
2018-01-22 10:25:22 +01:00
# include "sun4i_drv.h"
# include "sun4i_frontend.h"
static const u32 sun4i_frontend_vert_coef [ 32 ] = {
0x00004000 , 0x000140ff , 0x00033ffe , 0x00043ffd ,
0x00063efc , 0xff083dfc , 0x000a3bfb , 0xff0d39fb ,
0xff0f37fb , 0xff1136fa , 0xfe1433fb , 0xfe1631fb ,
0xfd192ffb , 0xfd1c2cfb , 0xfd1f29fb , 0xfc2127fc ,
0xfc2424fc , 0xfc2721fc , 0xfb291ffd , 0xfb2c1cfd ,
0xfb2f19fd , 0xfb3116fe , 0xfb3314fe , 0xfa3611ff ,
0xfb370fff , 0xfb390dff , 0xfb3b0a00 , 0xfc3d08ff ,
0xfc3e0600 , 0xfd3f0400 , 0xfe3f0300 , 0xff400100 ,
} ;
static const u32 sun4i_frontend_horz_coef [ 64 ] = {
0x40000000 , 0x00000000 , 0x40fe0000 , 0x0000ff03 ,
0x3ffd0000 , 0x0000ff05 , 0x3ffc0000 , 0x0000ff06 ,
0x3efb0000 , 0x0000ff08 , 0x3dfb0000 , 0x0000ff09 ,
0x3bfa0000 , 0x0000fe0d , 0x39fa0000 , 0x0000fe0f ,
0x38fa0000 , 0x0000fe10 , 0x36fa0000 , 0x0000fe12 ,
0x33fa0000 , 0x0000fd16 , 0x31fa0000 , 0x0000fd18 ,
0x2ffa0000 , 0x0000fd1a , 0x2cfa0000 , 0x0000fc1e ,
0x29fa0000 , 0x0000fc21 , 0x27fb0000 , 0x0000fb23 ,
0x24fb0000 , 0x0000fb26 , 0x21fb0000 , 0x0000fb29 ,
0x1ffc0000 , 0x0000fa2b , 0x1cfc0000 , 0x0000fa2e ,
0x19fd0000 , 0x0000fa30 , 0x16fd0000 , 0x0000fa33 ,
0x14fd0000 , 0x0000fa35 , 0x11fe0000 , 0x0000fa37 ,
0x0ffe0000 , 0x0000fa39 , 0x0dfe0000 , 0x0000fa3b ,
0x0afe0000 , 0x0000fa3e , 0x08ff0000 , 0x0000fb3e ,
0x06ff0000 , 0x0000fb40 , 0x05ff0000 , 0x0000fc40 ,
0x03ff0000 , 0x0000fd41 , 0x01ff0000 , 0x0000fe42 ,
} ;
2019-01-18 15:51:16 +01:00
/*
* These coefficients are taken from the A33 BSP from Allwinner .
*
* The first three values of each row are coded as 13 - bit signed fixed - point
* numbers , with 10 bits for the fractional part . The fourth value is a
* constant coded as a 14 - bit signed fixed - point number with 4 bits for the
* fractional part .
*
* The values in table order give the following colorspace translation :
* G = 1.164 * Y - 0.391 * U - 0.813 * V + 135
* R = 1.164 * Y + 1.596 * V - 222
* B = 1.164 * Y + 2.018 * U + 276
*
* This seems to be a conversion from Y [ 16 : 235 ] UV [ 16 : 240 ] to RGB [ 0 : 255 ] ,
* following the BT601 spec .
*/
const u32 sunxi_bt601_yuv2rgb_coef [ 12 ] = {
0x000004a7 , 0x00001e6f , 0x00001cbf , 0x00000877 ,
0x000004a7 , 0x00000000 , 0x00000662 , 0x00003211 ,
0x000004a7 , 0x00000812 , 0x00000000 , 0x00002eb1 ,
} ;
EXPORT_SYMBOL ( sunxi_bt601_yuv2rgb_coef ) ;
2018-01-22 10:25:22 +01:00
static void sun4i_frontend_scaler_init ( struct sun4i_frontend * frontend )
{
int i ;
2019-01-18 15:51:28 +01:00
if ( frontend - > data - > has_coef_access_ctrl )
regmap_write_bits ( frontend - > regs , SUN4I_FRONTEND_FRM_CTRL_REG ,
SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL ,
SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL ) ;
2019-01-18 15:51:27 +01:00
2018-01-22 10:25:22 +01:00
for ( i = 0 ; i < 32 ; i + + ) {
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH0_HORZCOEF0_REG ( i ) ,
sun4i_frontend_horz_coef [ 2 * i ] ) ;
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH1_HORZCOEF0_REG ( i ) ,
sun4i_frontend_horz_coef [ 2 * i ] ) ;
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH0_HORZCOEF1_REG ( i ) ,
sun4i_frontend_horz_coef [ 2 * i + 1 ] ) ;
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH1_HORZCOEF1_REG ( i ) ,
sun4i_frontend_horz_coef [ 2 * i + 1 ] ) ;
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH0_VERTCOEF_REG ( i ) ,
sun4i_frontend_vert_coef [ i ] ) ;
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH1_VERTCOEF_REG ( i ) ,
sun4i_frontend_vert_coef [ i ] ) ;
}
2019-01-18 15:51:30 +01:00
if ( frontend - > data - > has_coef_rdy )
regmap_write_bits ( frontend - > regs ,
SUN4I_FRONTEND_FRM_CTRL_REG ,
SUN4I_FRONTEND_FRM_CTRL_COEF_RDY ,
SUN4I_FRONTEND_FRM_CTRL_COEF_RDY ) ;
2018-01-22 10:25:22 +01:00
}
int sun4i_frontend_init ( struct sun4i_frontend * frontend )
{
return pm_runtime_get_sync ( frontend - > dev ) ;
}
EXPORT_SYMBOL ( sun4i_frontend_init ) ;
void sun4i_frontend_exit ( struct sun4i_frontend * frontend )
{
pm_runtime_put ( frontend - > dev ) ;
}
EXPORT_SYMBOL ( sun4i_frontend_exit ) ;
2019-01-18 15:51:20 +01:00
static bool sun4i_frontend_format_chroma_requires_swap ( uint32_t fmt )
{
switch ( fmt ) {
case DRM_FORMAT_YVU411 :
case DRM_FORMAT_YVU420 :
case DRM_FORMAT_YVU422 :
case DRM_FORMAT_YVU444 :
return true ;
default :
return false ;
}
}
2019-01-18 15:51:24 +01:00
static bool sun4i_frontend_format_supports_tiling ( uint32_t fmt )
{
switch ( fmt ) {
case DRM_FORMAT_NV12 :
case DRM_FORMAT_NV16 :
case DRM_FORMAT_NV21 :
case DRM_FORMAT_NV61 :
case DRM_FORMAT_YUV411 :
case DRM_FORMAT_YUV420 :
case DRM_FORMAT_YUV422 :
case DRM_FORMAT_YVU420 :
case DRM_FORMAT_YVU422 :
case DRM_FORMAT_YVU411 :
return true ;
default :
return false ;
}
}
2018-01-22 10:25:22 +01:00
void sun4i_frontend_update_buffer ( struct sun4i_frontend * frontend ,
struct drm_plane * plane )
{
struct drm_plane_state * state = plane - > state ;
struct drm_framebuffer * fb = state - > fb ;
2019-01-18 15:51:23 +01:00
unsigned int strides [ 3 ] = { } ;
2022-08-02 02:04:04 +02:00
dma_addr_t dma_addr ;
2019-01-18 15:51:20 +01:00
bool swap ;
2018-01-22 10:25:22 +01:00
2019-01-18 15:51:23 +01:00
if ( fb - > modifier = = DRM_FORMAT_MOD_ALLWINNER_TILED ) {
unsigned int width = state - > src_w > > 16 ;
unsigned int offset ;
strides [ 0 ] = SUN4I_FRONTEND_LINESTRD_TILED ( fb - > pitches [ 0 ] ) ;
/*
* The X1 offset is the offset to the bottom - right point in the
* end tile , which is the final pixel ( at offset width - 1 )
* within the end tile ( with a 32 - byte mask ) .
*/
offset = ( width - 1 ) & ( 32 - 1 ) ;
regmap_write ( frontend - > regs , SUN4I_FRONTEND_TB_OFF0_REG ,
SUN4I_FRONTEND_TB_OFF_X1 ( offset ) ) ;
if ( fb - > format - > num_planes > 1 ) {
strides [ 1 ] =
SUN4I_FRONTEND_LINESTRD_TILED ( fb - > pitches [ 1 ] ) ;
regmap_write ( frontend - > regs , SUN4I_FRONTEND_TB_OFF1_REG ,
SUN4I_FRONTEND_TB_OFF_X1 ( offset ) ) ;
}
if ( fb - > format - > num_planes > 2 ) {
strides [ 2 ] =
SUN4I_FRONTEND_LINESTRD_TILED ( fb - > pitches [ 2 ] ) ;
regmap_write ( frontend - > regs , SUN4I_FRONTEND_TB_OFF2_REG ,
SUN4I_FRONTEND_TB_OFF_X1 ( offset ) ) ;
}
} else {
strides [ 0 ] = fb - > pitches [ 0 ] ;
if ( fb - > format - > num_planes > 1 )
strides [ 1 ] = fb - > pitches [ 1 ] ;
if ( fb - > format - > num_planes > 2 )
strides [ 2 ] = fb - > pitches [ 2 ] ;
}
2018-01-22 10:25:22 +01:00
/* Set the line width */
DRM_DEBUG_DRIVER ( " Frontend stride: %d bytes \n " , fb - > pitches [ 0 ] ) ;
regmap_write ( frontend - > regs , SUN4I_FRONTEND_LINESTRD0_REG ,
2019-01-18 15:51:23 +01:00
strides [ 0 ] ) ;
2018-01-22 10:25:22 +01:00
2019-01-18 15:51:19 +01:00
if ( fb - > format - > num_planes > 1 )
regmap_write ( frontend - > regs , SUN4I_FRONTEND_LINESTRD1_REG ,
2019-01-18 15:51:23 +01:00
strides [ 1 ] ) ;
2019-01-18 15:51:19 +01:00
2019-01-18 15:51:20 +01:00
if ( fb - > format - > num_planes > 2 )
regmap_write ( frontend - > regs , SUN4I_FRONTEND_LINESTRD2_REG ,
2019-01-18 15:51:23 +01:00
strides [ 2 ] ) ;
2019-01-18 15:51:20 +01:00
/* Some planar formats require chroma channel swapping by hand. */
swap = sun4i_frontend_format_chroma_requires_swap ( fb - > format - > format ) ;
2018-01-22 10:25:22 +01:00
/* Set the physical address of the buffer in memory */
2022-08-02 02:04:04 +02:00
dma_addr = drm_fb_dma_get_gem_addr ( fb , state , 0 ) ;
DRM_DEBUG_DRIVER ( " Setting buffer #0 address to %pad \n " , & dma_addr ) ;
regmap_write ( frontend - > regs , SUN4I_FRONTEND_BUF_ADDR0_REG , dma_addr ) ;
2019-01-18 15:51:19 +01:00
if ( fb - > format - > num_planes > 1 ) {
2022-08-02 02:04:04 +02:00
dma_addr = drm_fb_dma_get_gem_addr ( fb , state , swap ? 2 : 1 ) ;
DRM_DEBUG_DRIVER ( " Setting buffer #1 address to %pad \n " ,
& dma_addr ) ;
2019-01-18 15:51:19 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_BUF_ADDR1_REG ,
2022-08-02 02:04:04 +02:00
dma_addr ) ;
2019-01-18 15:51:19 +01:00
}
2019-01-18 15:51:20 +01:00
if ( fb - > format - > num_planes > 2 ) {
2022-08-02 02:04:04 +02:00
dma_addr = drm_fb_dma_get_gem_addr ( fb , state , swap ? 1 : 2 ) ;
DRM_DEBUG_DRIVER ( " Setting buffer #2 address to %pad \n " ,
& dma_addr ) ;
2019-01-18 15:51:20 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_BUF_ADDR2_REG ,
2022-08-02 02:04:04 +02:00
dma_addr ) ;
2019-01-18 15:51:20 +01:00
}
2018-01-22 10:25:22 +01:00
}
EXPORT_SYMBOL ( sun4i_frontend_update_buffer ) ;
2019-01-18 15:51:14 +01:00
static int
sun4i_frontend_drm_format_to_input_fmt ( const struct drm_format_info * format ,
u32 * val )
2018-01-22 10:25:22 +01:00
{
2019-01-18 15:51:15 +01:00
if ( ! format - > is_yuv )
2018-11-23 10:24:50 +01:00
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_RGB ;
2019-01-18 15:51:20 +01:00
else if ( drm_format_info_is_yuv_sampling_411 ( format ) )
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV411 ;
2019-01-18 15:51:19 +01:00
else if ( drm_format_info_is_yuv_sampling_420 ( format ) )
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV420 ;
2019-01-18 15:51:18 +01:00
else if ( drm_format_info_is_yuv_sampling_422 ( format ) )
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV422 ;
2019-01-18 15:51:20 +01:00
else if ( drm_format_info_is_yuv_sampling_444 ( format ) )
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV444 ;
2019-01-18 15:51:15 +01:00
else
2018-01-22 10:25:22 +01:00
return - EINVAL ;
2019-01-18 15:51:15 +01:00
return 0 ;
2018-01-22 10:25:22 +01:00
}
2019-01-18 15:51:14 +01:00
static int
sun4i_frontend_drm_format_to_input_mode ( const struct drm_format_info * format ,
2019-01-18 15:51:22 +01:00
uint64_t modifier , u32 * val )
2018-11-23 10:24:49 +01:00
{
2019-01-18 15:51:22 +01:00
bool tiled = ( modifier = = DRM_FORMAT_MOD_ALLWINNER_TILED ) ;
2019-01-18 15:51:19 +01:00
switch ( format - > num_planes ) {
case 1 :
2018-11-23 10:24:50 +01:00
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PACKED ;
2019-01-18 15:51:19 +01:00
return 0 ;
2018-11-23 10:24:51 +01:00
2019-01-18 15:51:19 +01:00
case 2 :
2019-01-18 15:51:22 +01:00
* val = tiled ? SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_SEMIPLANAR
: SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_SEMIPLANAR ;
2019-01-18 15:51:19 +01:00
return 0 ;
2019-01-18 15:51:20 +01:00
case 3 :
2019-01-18 15:51:22 +01:00
* val = tiled ? SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_PLANAR
: SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PLANAR ;
2019-01-18 15:51:20 +01:00
return 0 ;
2019-01-18 15:51:19 +01:00
default :
return - EINVAL ;
}
2018-11-23 10:24:49 +01:00
}
2019-01-18 15:51:14 +01:00
static int
sun4i_frontend_drm_format_to_input_sequence ( const struct drm_format_info * format ,
u32 * val )
2018-11-23 10:24:49 +01:00
{
2019-01-18 15:51:20 +01:00
/* Planar formats have an explicit input sequence. */
if ( drm_format_info_is_yuv_planar ( format ) ) {
* val = 0 ;
return 0 ;
}
2019-01-18 15:51:14 +01:00
switch ( format - > format ) {
2018-11-23 10:24:53 +01:00
case DRM_FORMAT_BGRX8888 :
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_BGRX ;
return 0 ;
2019-01-18 15:51:19 +01:00
case DRM_FORMAT_NV12 :
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UV ;
return 0 ;
case DRM_FORMAT_NV16 :
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UV ;
return 0 ;
case DRM_FORMAT_NV21 :
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VU ;
return 0 ;
case DRM_FORMAT_NV61 :
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VU ;
return 0 ;
2019-01-18 15:51:18 +01:00
case DRM_FORMAT_UYVY :
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UYVY ;
return 0 ;
case DRM_FORMAT_VYUY :
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VYUY ;
return 0 ;
2018-11-23 10:24:49 +01:00
case DRM_FORMAT_XRGB8888 :
2018-11-23 10:24:50 +01:00
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_XRGB ;
2018-11-23 10:24:49 +01:00
return 0 ;
2019-01-18 15:51:18 +01:00
case DRM_FORMAT_YUYV :
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YUYV ;
return 0 ;
case DRM_FORMAT_YVYU :
* val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YVYU ;
return 0 ;
2018-11-23 10:24:49 +01:00
default :
return - EINVAL ;
}
}
2018-01-22 10:25:22 +01:00
static int sun4i_frontend_drm_format_to_output_fmt ( uint32_t fmt , u32 * val )
{
switch ( fmt ) {
2018-11-23 10:24:54 +01:00
case DRM_FORMAT_BGRX8888 :
* val = SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_BGRX8888 ;
return 0 ;
2018-01-22 10:25:22 +01:00
case DRM_FORMAT_XRGB8888 :
2018-11-23 10:24:50 +01:00
* val = SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_XRGB8888 ;
2018-01-22 10:25:22 +01:00
return 0 ;
default :
return - EINVAL ;
}
}
2018-11-23 10:24:37 +01:00
static const uint32_t sun4i_frontend_formats [ ] = {
2018-11-23 10:24:53 +01:00
DRM_FORMAT_BGRX8888 ,
2019-01-18 15:51:19 +01:00
DRM_FORMAT_NV12 ,
DRM_FORMAT_NV16 ,
DRM_FORMAT_NV21 ,
DRM_FORMAT_NV61 ,
2019-01-18 15:51:18 +01:00
DRM_FORMAT_UYVY ,
DRM_FORMAT_VYUY ,
2018-11-23 10:24:37 +01:00
DRM_FORMAT_XRGB8888 ,
2019-01-18 15:51:20 +01:00
DRM_FORMAT_YUV411 ,
DRM_FORMAT_YUV420 ,
DRM_FORMAT_YUV422 ,
DRM_FORMAT_YUV444 ,
2019-01-18 15:51:18 +01:00
DRM_FORMAT_YUYV ,
2019-01-18 15:51:20 +01:00
DRM_FORMAT_YVU411 ,
DRM_FORMAT_YVU420 ,
DRM_FORMAT_YVU422 ,
DRM_FORMAT_YVU444 ,
2019-01-18 15:51:18 +01:00
DRM_FORMAT_YVYU ,
2018-11-23 10:24:37 +01:00
} ;
2018-11-23 10:25:04 +01:00
bool sun4i_frontend_format_is_supported ( uint32_t fmt , uint64_t modifier )
2018-11-23 10:24:37 +01:00
{
unsigned int i ;
2019-01-18 15:51:24 +01:00
if ( modifier = = DRM_FORMAT_MOD_ALLWINNER_TILED )
return sun4i_frontend_format_supports_tiling ( fmt ) ;
else if ( modifier ! = DRM_FORMAT_MOD_LINEAR )
2018-11-23 10:25:04 +01:00
return false ;
2018-11-23 10:24:37 +01:00
for ( i = 0 ; i < ARRAY_SIZE ( sun4i_frontend_formats ) ; i + + )
if ( sun4i_frontend_formats [ i ] = = fmt )
return true ;
return false ;
}
EXPORT_SYMBOL ( sun4i_frontend_format_is_supported ) ;
2018-01-22 10:25:22 +01:00
int sun4i_frontend_update_formats ( struct sun4i_frontend * frontend ,
struct drm_plane * plane , uint32_t out_fmt )
{
struct drm_plane_state * state = plane - > state ;
struct drm_framebuffer * fb = state - > fb ;
2019-01-18 15:51:14 +01:00
const struct drm_format_info * format = fb - > format ;
2019-01-18 15:51:22 +01:00
uint64_t modifier = fb - > modifier ;
2020-10-15 11:36:41 +02:00
unsigned int ch1_phase_idx ;
2018-01-22 10:25:22 +01:00
u32 out_fmt_val ;
2018-11-23 10:24:49 +01:00
u32 in_fmt_val , in_mod_val , in_ps_val ;
2019-01-18 15:51:17 +01:00
unsigned int i ;
u32 bypass ;
2018-01-22 10:25:22 +01:00
int ret ;
2018-11-23 10:24:49 +01:00
ret = sun4i_frontend_drm_format_to_input_fmt ( format , & in_fmt_val ) ;
2018-01-22 10:25:22 +01:00
if ( ret ) {
DRM_DEBUG_DRIVER ( " Invalid input format \n " ) ;
return ret ;
}
2019-01-18 15:51:22 +01:00
ret = sun4i_frontend_drm_format_to_input_mode ( format , modifier ,
& in_mod_val ) ;
2018-11-23 10:24:49 +01:00
if ( ret ) {
DRM_DEBUG_DRIVER ( " Invalid input mode \n " ) ;
return ret ;
}
ret = sun4i_frontend_drm_format_to_input_sequence ( format , & in_ps_val ) ;
if ( ret ) {
DRM_DEBUG_DRIVER ( " Invalid pixel sequence \n " ) ;
return ret ;
}
2018-01-22 10:25:22 +01:00
ret = sun4i_frontend_drm_format_to_output_fmt ( out_fmt , & out_fmt_val ) ;
if ( ret ) {
DRM_DEBUG_DRIVER ( " Invalid output format \n " ) ;
return ret ;
}
/*
* I have no idea what this does exactly , but it seems to be
* related to the scaler FIR filter phase parameters .
*/
2020-10-15 11:36:41 +02:00
ch1_phase_idx = ( format - > num_planes > 1 ) ? 1 : 0 ;
2019-01-18 15:51:31 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH0_HORZPHASE_REG ,
2020-10-15 11:36:40 +02:00
frontend - > data - > ch_phase [ 0 ] ) ;
2019-01-18 15:51:31 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH1_HORZPHASE_REG ,
2020-10-15 11:36:41 +02:00
frontend - > data - > ch_phase [ ch1_phase_idx ] ) ;
2019-01-18 15:51:31 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH0_VERTPHASE0_REG ,
2020-10-15 11:36:40 +02:00
frontend - > data - > ch_phase [ 0 ] ) ;
2019-01-18 15:51:31 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH1_VERTPHASE0_REG ,
2020-10-15 11:36:41 +02:00
frontend - > data - > ch_phase [ ch1_phase_idx ] ) ;
2019-01-18 15:51:31 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH0_VERTPHASE1_REG ,
2020-10-15 11:36:40 +02:00
frontend - > data - > ch_phase [ 0 ] ) ;
2019-01-18 15:51:31 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH1_VERTPHASE1_REG ,
2020-10-15 11:36:41 +02:00
frontend - > data - > ch_phase [ ch1_phase_idx ] ) ;
2018-01-22 10:25:22 +01:00
2019-01-18 15:51:17 +01:00
/*
* Checking the input format is sufficient since we currently only
* support RGB output formats to the backend . If YUV output formats
* ever get supported , an YUV input and output would require bypassing
* the CSC engine too .
*/
if ( format - > is_yuv ) {
/* Setup the CSC engine for YUV to RGB conversion. */
bypass = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( sunxi_bt601_yuv2rgb_coef ) ; i + + )
regmap_write ( frontend - > regs ,
SUN4I_FRONTEND_CSC_COEF_REG ( i ) ,
sunxi_bt601_yuv2rgb_coef [ i ] ) ;
} else {
bypass = SUN4I_FRONTEND_BYPASS_CSC_EN ;
}
2018-11-23 10:24:48 +01:00
regmap_update_bits ( frontend - > regs , SUN4I_FRONTEND_BYPASS_REG ,
2019-01-18 15:51:17 +01:00
SUN4I_FRONTEND_BYPASS_CSC_EN , bypass ) ;
2018-11-23 10:24:48 +01:00
2018-01-22 10:25:22 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_INPUT_FMT_REG ,
2018-11-23 10:24:50 +01:00
in_mod_val | in_fmt_val | in_ps_val ) ;
2018-01-22 10:25:22 +01:00
/*
* TODO : It look like the A31 and A80 at least will need the
* bit 7 ( ALPHA_EN ) enabled when using a format with alpha ( so
* ARGB8888 ) .
*/
regmap_write ( frontend - > regs , SUN4I_FRONTEND_OUTPUT_FMT_REG ,
2018-11-23 10:24:50 +01:00
out_fmt_val ) ;
2018-01-22 10:25:22 +01:00
return 0 ;
}
EXPORT_SYMBOL ( sun4i_frontend_update_formats ) ;
void sun4i_frontend_update_coord ( struct sun4i_frontend * frontend ,
struct drm_plane * plane )
{
struct drm_plane_state * state = plane - > state ;
2018-11-23 10:24:57 +01:00
struct drm_framebuffer * fb = state - > fb ;
uint32_t luma_width , luma_height ;
uint32_t chroma_width , chroma_height ;
2018-01-22 10:25:22 +01:00
/* Set height and width */
DRM_DEBUG_DRIVER ( " Frontend size W: %u H: %u \n " ,
state - > crtc_w , state - > crtc_h ) ;
2018-11-23 10:24:57 +01:00
luma_width = state - > src_w > > 16 ;
luma_height = state - > src_h > > 16 ;
chroma_width = DIV_ROUND_UP ( luma_width , fb - > format - > hsub ) ;
chroma_height = DIV_ROUND_UP ( luma_height , fb - > format - > vsub ) ;
2018-01-22 10:25:22 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH0_INSIZE_REG ,
2018-11-23 10:24:57 +01:00
SUN4I_FRONTEND_INSIZE ( luma_height , luma_width ) ) ;
2018-01-22 10:25:22 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH1_INSIZE_REG ,
2018-11-23 10:24:57 +01:00
SUN4I_FRONTEND_INSIZE ( chroma_height , chroma_width ) ) ;
2018-01-22 10:25:22 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH0_OUTSIZE_REG ,
SUN4I_FRONTEND_OUTSIZE ( state - > crtc_h , state - > crtc_w ) ) ;
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH1_OUTSIZE_REG ,
SUN4I_FRONTEND_OUTSIZE ( state - > crtc_h , state - > crtc_w ) ) ;
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH0_HORZFACT_REG ,
2018-11-23 10:24:57 +01:00
( luma_width < < 16 ) / state - > crtc_w ) ;
2018-01-22 10:25:22 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH1_HORZFACT_REG ,
2018-11-23 10:24:57 +01:00
( chroma_width < < 16 ) / state - > crtc_w ) ;
2018-01-22 10:25:22 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH0_VERTFACT_REG ,
2018-11-23 10:24:57 +01:00
( luma_height < < 16 ) / state - > crtc_h ) ;
2018-01-22 10:25:22 +01:00
regmap_write ( frontend - > regs , SUN4I_FRONTEND_CH1_VERTFACT_REG ,
2018-11-23 10:24:57 +01:00
( chroma_height < < 16 ) / state - > crtc_h ) ;
2018-01-22 10:25:22 +01:00
regmap_write_bits ( frontend - > regs , SUN4I_FRONTEND_FRM_CTRL_REG ,
SUN4I_FRONTEND_FRM_CTRL_REG_RDY ,
SUN4I_FRONTEND_FRM_CTRL_REG_RDY ) ;
}
EXPORT_SYMBOL ( sun4i_frontend_update_coord ) ;
int sun4i_frontend_enable ( struct sun4i_frontend * frontend )
{
regmap_write_bits ( frontend - > regs , SUN4I_FRONTEND_FRM_CTRL_REG ,
SUN4I_FRONTEND_FRM_CTRL_FRM_START ,
SUN4I_FRONTEND_FRM_CTRL_FRM_START ) ;
return 0 ;
}
EXPORT_SYMBOL ( sun4i_frontend_enable ) ;
2020-08-04 23:53:37 +02:00
static const struct regmap_config sun4i_frontend_regmap_config = {
2018-01-22 10:25:22 +01:00
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = 4 ,
. max_register = 0x0a14 ,
} ;
static int sun4i_frontend_bind ( struct device * dev , struct device * master ,
void * data )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct sun4i_frontend * frontend ;
struct drm_device * drm = data ;
struct sun4i_drv * drv = drm - > dev_private ;
void __iomem * regs ;
frontend = devm_kzalloc ( dev , sizeof ( * frontend ) , GFP_KERNEL ) ;
if ( ! frontend )
return - ENOMEM ;
dev_set_drvdata ( dev , frontend ) ;
frontend - > dev = dev ;
frontend - > node = dev - > of_node ;
2019-01-18 15:51:28 +01:00
frontend - > data = of_device_get_match_data ( dev ) ;
if ( ! frontend - > data )
return - ENODEV ;
2021-08-31 21:57:39 +08:00
regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
2018-01-22 10:25:22 +01:00
if ( IS_ERR ( regs ) )
return PTR_ERR ( regs ) ;
frontend - > regs = devm_regmap_init_mmio ( dev , regs ,
& sun4i_frontend_regmap_config ) ;
if ( IS_ERR ( frontend - > regs ) ) {
dev_err ( dev , " Couldn't create the frontend regmap \n " ) ;
return PTR_ERR ( frontend - > regs ) ;
}
frontend - > reset = devm_reset_control_get ( dev , NULL ) ;
if ( IS_ERR ( frontend - > reset ) ) {
dev_err ( dev , " Couldn't get our reset line \n " ) ;
return PTR_ERR ( frontend - > reset ) ;
}
frontend - > bus_clk = devm_clk_get ( dev , " ahb " ) ;
if ( IS_ERR ( frontend - > bus_clk ) ) {
dev_err ( dev , " Couldn't get our bus clock \n " ) ;
return PTR_ERR ( frontend - > bus_clk ) ;
}
frontend - > mod_clk = devm_clk_get ( dev , " mod " ) ;
if ( IS_ERR ( frontend - > mod_clk ) ) {
dev_err ( dev , " Couldn't get our mod clock \n " ) ;
return PTR_ERR ( frontend - > mod_clk ) ;
}
frontend - > ram_clk = devm_clk_get ( dev , " ram " ) ;
if ( IS_ERR ( frontend - > ram_clk ) ) {
dev_err ( dev , " Couldn't get our ram clock \n " ) ;
return PTR_ERR ( frontend - > ram_clk ) ;
}
list_add_tail ( & frontend - > list , & drv - > frontend_list ) ;
pm_runtime_enable ( dev ) ;
return 0 ;
}
static void sun4i_frontend_unbind ( struct device * dev , struct device * master ,
void * data )
{
struct sun4i_frontend * frontend = dev_get_drvdata ( dev ) ;
list_del ( & frontend - > list ) ;
pm_runtime_force_suspend ( dev ) ;
}
static const struct component_ops sun4i_frontend_ops = {
. bind = sun4i_frontend_bind ,
. unbind = sun4i_frontend_unbind ,
} ;
static int sun4i_frontend_probe ( struct platform_device * pdev )
{
return component_add ( & pdev - > dev , & sun4i_frontend_ops ) ;
}
static int sun4i_frontend_remove ( struct platform_device * pdev )
{
component_del ( & pdev - > dev , & sun4i_frontend_ops ) ;
return 0 ;
}
static int sun4i_frontend_runtime_resume ( struct device * dev )
{
struct sun4i_frontend * frontend = dev_get_drvdata ( dev ) ;
int ret ;
clk_set_rate ( frontend - > mod_clk , 300000000 ) ;
clk_prepare_enable ( frontend - > bus_clk ) ;
clk_prepare_enable ( frontend - > mod_clk ) ;
clk_prepare_enable ( frontend - > ram_clk ) ;
ret = reset_control_reset ( frontend - > reset ) ;
if ( ret ) {
dev_err ( dev , " Couldn't reset our device \n " ) ;
return ret ;
}
regmap_update_bits ( frontend - > regs , SUN4I_FRONTEND_EN_REG ,
SUN4I_FRONTEND_EN_EN ,
SUN4I_FRONTEND_EN_EN ) ;
sun4i_frontend_scaler_init ( frontend ) ;
return 0 ;
}
static int sun4i_frontend_runtime_suspend ( struct device * dev )
{
struct sun4i_frontend * frontend = dev_get_drvdata ( dev ) ;
clk_disable_unprepare ( frontend - > ram_clk ) ;
clk_disable_unprepare ( frontend - > mod_clk ) ;
clk_disable_unprepare ( frontend - > bus_clk ) ;
reset_control_assert ( frontend - > reset ) ;
return 0 ;
}
static const struct dev_pm_ops sun4i_frontend_pm_ops = {
. runtime_resume = sun4i_frontend_runtime_resume ,
. runtime_suspend = sun4i_frontend_runtime_suspend ,
} ;
2019-01-18 15:51:32 +01:00
static const struct sun4i_frontend_data sun4i_a10_frontend = {
2020-10-15 11:36:40 +02:00
. ch_phase = { 0x000 , 0xfc000 } ,
2019-01-18 15:51:32 +01:00
. has_coef_rdy = true ,
} ;
2019-01-18 15:51:28 +01:00
static const struct sun4i_frontend_data sun8i_a33_frontend = {
2020-10-15 11:36:42 +02:00
. ch_phase = { 0x400 , 0xfc400 } ,
2019-01-18 15:51:28 +01:00
. has_coef_access_ctrl = true ,
} ;
2018-01-22 10:25:22 +01:00
const struct of_device_id sun4i_frontend_of_table [ ] = {
2019-01-18 15:51:32 +01:00
{
. compatible = " allwinner,sun4i-a10-display-frontend " ,
. data = & sun4i_a10_frontend
} ,
2019-01-18 15:51:33 +01:00
{
. compatible = " allwinner,sun7i-a20-display-frontend " ,
. data = & sun4i_a10_frontend
} ,
2019-01-25 11:23:09 +08:00
{
. compatible = " allwinner,sun8i-a23-display-frontend " ,
. data = & sun8i_a33_frontend
} ,
2019-01-18 15:51:28 +01:00
{
. compatible = " allwinner,sun8i-a33-display-frontend " ,
. data = & sun8i_a33_frontend
} ,
2018-01-22 10:25:22 +01:00
{ }
} ;
EXPORT_SYMBOL ( sun4i_frontend_of_table ) ;
MODULE_DEVICE_TABLE ( of , sun4i_frontend_of_table ) ;
static struct platform_driver sun4i_frontend_driver = {
. probe = sun4i_frontend_probe ,
. remove = sun4i_frontend_remove ,
. driver = {
. name = " sun4i-frontend " ,
. of_match_table = sun4i_frontend_of_table ,
. pm = & sun4i_frontend_pm_ops ,
} ,
} ;
module_platform_driver ( sun4i_frontend_driver ) ;
MODULE_AUTHOR ( " Maxime Ripard <maxime.ripard@free-electrons.com> " ) ;
MODULE_DESCRIPTION ( " Allwinner A10 Display Engine Frontend Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;