2017-11-02 16:49:51 +00:00
// SPDX-License-Identifier: GPL-2.0
/*
* ( C ) COPYRIGHT 2016 ARM Limited . All rights reserved .
* Author : Brian Starkey < brian . starkey @ arm . com >
*
* ARM Mali DP Writeback connector implementation
*/
2019-08-04 11:41:32 +02:00
2017-11-02 16:49:51 +00:00
# include <drm/drm_atomic.h>
# include <drm/drm_atomic_helper.h>
# include <drm/drm_crtc.h>
# include <drm/drm_fb_cma_helper.h>
2019-08-04 11:41:32 +02:00
# include <drm/drm_fourcc.h>
2017-11-02 16:49:51 +00:00
# include <drm/drm_gem_cma_helper.h>
2019-08-04 11:41:32 +02:00
# include <drm/drm_probe_helper.h>
2017-11-02 16:49:51 +00:00
# include <drm/drm_writeback.h>
# include "malidp_drv.h"
# include "malidp_hw.h"
# include "malidp_mw.h"
# define to_mw_state(_state) (struct malidp_mw_connector_state *)(_state)
struct malidp_mw_connector_state {
struct drm_connector_state base ;
dma_addr_t addrs [ 2 ] ;
s32 pitches [ 2 ] ;
u8 format ;
u8 n_planes ;
2018-08-22 16:18:19 +01:00
bool rgb2yuv_initialized ;
const s16 * rgb2yuv_coeffs ;
2017-11-02 16:49:51 +00:00
} ;
static int malidp_mw_connector_get_modes ( struct drm_connector * connector )
{
struct drm_device * dev = connector - > dev ;
return drm_add_modes_noedid ( connector , dev - > mode_config . max_width ,
dev - > mode_config . max_height ) ;
}
static enum drm_mode_status
malidp_mw_connector_mode_valid ( struct drm_connector * connector ,
struct drm_display_mode * mode )
{
struct drm_device * dev = connector - > dev ;
struct drm_mode_config * mode_config = & dev - > mode_config ;
int w = mode - > hdisplay , h = mode - > vdisplay ;
if ( ( w < mode_config - > min_width ) | | ( w > mode_config - > max_width ) )
return MODE_BAD_HVALUE ;
if ( ( h < mode_config - > min_height ) | | ( h > mode_config - > max_height ) )
return MODE_BAD_VVALUE ;
return MODE_OK ;
}
2019-12-17 11:53:09 +00:00
static const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs = {
2017-11-02 16:49:51 +00:00
. get_modes = malidp_mw_connector_get_modes ,
. mode_valid = malidp_mw_connector_mode_valid ,
} ;
static void malidp_mw_connector_reset ( struct drm_connector * connector )
{
struct malidp_mw_connector_state * mw_state =
kzalloc ( sizeof ( * mw_state ) , GFP_KERNEL ) ;
if ( connector - > state )
__drm_atomic_helper_connector_destroy_state ( connector - > state ) ;
kfree ( connector - > state ) ;
__drm_atomic_helper_connector_reset ( connector , & mw_state - > base ) ;
}
static enum drm_connector_status
malidp_mw_connector_detect ( struct drm_connector * connector , bool force )
{
2018-07-13 16:10:59 +01:00
return connector_status_connected ;
2017-11-02 16:49:51 +00:00
}
static void malidp_mw_connector_destroy ( struct drm_connector * connector )
{
drm_connector_cleanup ( connector ) ;
}
static struct drm_connector_state *
malidp_mw_connector_duplicate_state ( struct drm_connector * connector )
{
2018-08-22 16:18:19 +01:00
struct malidp_mw_connector_state * mw_state , * mw_current_state ;
2017-11-02 16:49:51 +00:00
if ( WARN_ON ( ! connector - > state ) )
return NULL ;
mw_state = kzalloc ( sizeof ( * mw_state ) , GFP_KERNEL ) ;
if ( ! mw_state )
return NULL ;
2018-08-22 16:18:19 +01:00
mw_current_state = to_mw_state ( connector - > state ) ;
mw_state - > rgb2yuv_coeffs = mw_current_state - > rgb2yuv_coeffs ;
mw_state - > rgb2yuv_initialized = mw_current_state - > rgb2yuv_initialized ;
2017-11-02 16:49:51 +00:00
__drm_atomic_helper_connector_duplicate_state ( connector , & mw_state - > base ) ;
return & mw_state - > base ;
}
static const struct drm_connector_funcs malidp_mw_connector_funcs = {
. reset = malidp_mw_connector_reset ,
. detect = malidp_mw_connector_detect ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. destroy = malidp_mw_connector_destroy ,
. atomic_duplicate_state = malidp_mw_connector_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_connector_destroy_state ,
} ;
2018-08-22 16:18:19 +01:00
static const s16 rgb2yuv_coeffs_bt709_limited [ MALIDP_COLORADJ_NUM_COEFFS ] = {
47 , 157 , 16 ,
- 26 , - 87 , 112 ,
112 , - 102 , - 10 ,
16 , 128 , 128
} ;
2017-11-02 16:49:51 +00:00
static int
malidp_mw_encoder_atomic_check ( struct drm_encoder * encoder ,
struct drm_crtc_state * crtc_state ,
struct drm_connector_state * conn_state )
{
struct malidp_mw_connector_state * mw_state = to_mw_state ( conn_state ) ;
struct malidp_drm * malidp = encoder - > dev - > dev_private ;
struct drm_framebuffer * fb ;
int i , n_planes ;
2019-07-31 11:04:38 +00:00
if ( ! conn_state - > writeback_job )
2017-11-02 16:49:51 +00:00
return 0 ;
fb = conn_state - > writeback_job - > fb ;
if ( ( fb - > width ! = crtc_state - > mode . hdisplay ) | |
( fb - > height ! = crtc_state - > mode . vdisplay ) ) {
DRM_DEBUG_KMS ( " Invalid framebuffer size %ux%u \n " ,
fb - > width , fb - > height ) ;
return - EINVAL ;
}
2018-07-06 14:06:31 +01:00
if ( fb - > modifier ) {
DRM_DEBUG_KMS ( " Writeback framebuffer does not support modifiers \n " ) ;
return - EINVAL ;
}
2017-11-02 16:49:51 +00:00
mw_state - > format =
malidp_hw_get_format_id ( & malidp - > dev - > hw - > map , SE_MEMWRITE ,
2018-07-17 12:11:09 +01:00
fb - > format - > format , ! ! fb - > modifier ) ;
2017-11-02 16:49:51 +00:00
if ( mw_state - > format = = MALIDP_INVALID_FORMAT_ID ) {
2021-02-16 17:57:22 +02:00
DRM_DEBUG_KMS ( " Invalid pixel format %p4cc \n " ,
& fb - > format - > format ) ;
2017-11-02 16:49:51 +00:00
return - EINVAL ;
}
2019-05-16 12:31:47 +02:00
n_planes = fb - > format - > num_planes ;
2017-11-02 16:49:51 +00:00
for ( i = 0 ; i < n_planes ; i + + ) {
struct drm_gem_cma_object * obj = drm_fb_cma_get_gem_obj ( fb , i ) ;
/* memory write buffers are never rotated */
u8 alignment = malidp_hw_get_pitch_align ( malidp - > dev , 0 ) ;
if ( fb - > pitches [ i ] & ( alignment - 1 ) ) {
DRM_DEBUG_KMS ( " Invalid pitch %u for plane %d \n " ,
fb - > pitches [ i ] , i ) ;
return - EINVAL ;
}
mw_state - > pitches [ i ] = fb - > pitches [ i ] ;
mw_state - > addrs [ i ] = obj - > paddr + fb - > offsets [ i ] ;
}
mw_state - > n_planes = n_planes ;
2018-08-22 16:18:19 +01:00
if ( fb - > format - > is_yuv )
mw_state - > rgb2yuv_coeffs = rgb2yuv_coeffs_bt709_limited ;
2017-11-02 16:49:51 +00:00
return 0 ;
}
static const struct drm_encoder_helper_funcs malidp_mw_encoder_helper_funcs = {
. atomic_check = malidp_mw_encoder_atomic_check ,
} ;
static u32 * get_writeback_formats ( struct malidp_drm * malidp , int * n_formats )
{
const struct malidp_hw_regmap * map = & malidp - > dev - > hw - > map ;
u32 * formats ;
int n , i ;
formats = kcalloc ( map - > n_pixel_formats , sizeof ( * formats ) ,
GFP_KERNEL ) ;
if ( ! formats )
return NULL ;
for ( n = 0 , i = 0 ; i < map - > n_pixel_formats ; i + + ) {
if ( map - > pixel_formats [ i ] . layer & SE_MEMWRITE )
formats [ n + + ] = map - > pixel_formats [ i ] . format ;
}
* n_formats = n ;
return formats ;
}
int malidp_mw_connector_init ( struct drm_device * drm )
{
struct malidp_drm * malidp = drm - > dev_private ;
u32 * formats ;
int ret , n_formats ;
if ( ! malidp - > dev - > hw - > enable_memwrite )
return 0 ;
malidp - > mw_connector . encoder . possible_crtcs = 1 < < drm_crtc_index ( & malidp - > crtc ) ;
drm_connector_helper_add ( & malidp - > mw_connector . base ,
& malidp_mw_connector_helper_funcs ) ;
formats = get_writeback_formats ( malidp , & n_formats ) ;
if ( ! formats )
return - ENOMEM ;
ret = drm_writeback_connector_init ( drm , & malidp - > mw_connector ,
& malidp_mw_connector_funcs ,
& malidp_mw_encoder_helper_funcs ,
formats , n_formats ) ;
kfree ( formats ) ;
if ( ret )
return ret ;
return 0 ;
}
void malidp_mw_atomic_commit ( struct drm_device * drm ,
struct drm_atomic_state * old_state )
{
struct malidp_drm * malidp = drm - > dev_private ;
struct drm_writeback_connector * mw_conn = & malidp - > mw_connector ;
struct drm_connector_state * conn_state = mw_conn - > base . state ;
struct malidp_hw_device * hwdev = malidp - > dev ;
struct malidp_mw_connector_state * mw_state ;
if ( ! conn_state )
return ;
mw_state = to_mw_state ( conn_state ) ;
2019-07-31 11:04:38 +00:00
if ( conn_state - > writeback_job ) {
2017-11-02 16:49:51 +00:00
struct drm_framebuffer * fb = conn_state - > writeback_job - > fb ;
DRM_DEV_DEBUG_DRIVER ( drm - > dev ,
" Enable memwrite %ux%u:%d %pad fmt: %u \n " ,
fb - > width , fb - > height ,
mw_state - > pitches [ 0 ] ,
& mw_state - > addrs [ 0 ] ,
mw_state - > format ) ;
2019-02-21 12:17:32 +02:00
drm_writeback_queue_job ( mw_conn , conn_state ) ;
2017-11-02 16:49:51 +00:00
hwdev - > hw - > enable_memwrite ( hwdev , mw_state - > addrs ,
mw_state - > pitches , mw_state - > n_planes ,
2018-08-22 16:18:19 +01:00
fb - > width , fb - > height , mw_state - > format ,
! mw_state - > rgb2yuv_initialized ?
mw_state - > rgb2yuv_coeffs : NULL ) ;
mw_state - > rgb2yuv_initialized = ! ! mw_state - > rgb2yuv_coeffs ;
2017-11-02 16:49:51 +00:00
} else {
DRM_DEV_DEBUG_DRIVER ( drm - > dev , " Disable memwrite \n " ) ;
hwdev - > hw - > disable_memwrite ( hwdev ) ;
}
}