2019-04-03 10:49:08 +10:30
// SPDX-License-Identifier: GPL-2.0+
// Copyright 2018 IBM Corporation
# include <linux/clk.h>
# include <linux/reset.h>
# include <linux/regmap.h>
# include <drm/drm_crtc_helper.h>
# include <drm/drm_device.h>
# include <drm/drm_fb_cma_helper.h>
# include <drm/drm_fourcc.h>
# include <drm/drm_gem_cma_helper.h>
# include <drm/drm_gem_framebuffer_helper.h>
# include <drm/drm_panel.h>
# include <drm/drm_simple_kms_helper.h>
# include <drm/drm_vblank.h>
# include "aspeed_gfx.h"
static struct aspeed_gfx *
drm_pipe_to_aspeed_gfx ( struct drm_simple_display_pipe * pipe )
{
return container_of ( pipe , struct aspeed_gfx , pipe ) ;
}
static int aspeed_gfx_set_pixel_fmt ( struct aspeed_gfx * priv , u32 * bpp )
{
struct drm_crtc * crtc = & priv - > pipe . crtc ;
struct drm_device * drm = crtc - > dev ;
const u32 format = crtc - > primary - > state - > fb - > format - > format ;
u32 ctrl1 ;
ctrl1 = readl ( priv - > base + CRT_CTRL1 ) ;
ctrl1 & = ~ CRT_CTRL_COLOR_MASK ;
switch ( format ) {
case DRM_FORMAT_RGB565 :
dev_dbg ( drm - > dev , " Setting up RGB565 mode \n " ) ;
ctrl1 | = CRT_CTRL_COLOR_RGB565 ;
* bpp = 16 ;
break ;
case DRM_FORMAT_XRGB8888 :
dev_dbg ( drm - > dev , " Setting up XRGB8888 mode \n " ) ;
ctrl1 | = CRT_CTRL_COLOR_XRGB8888 ;
* bpp = 32 ;
break ;
default :
dev_err ( drm - > dev , " Unhandled pixel format %08x \n " , format ) ;
return - EINVAL ;
}
writel ( ctrl1 , priv - > base + CRT_CTRL1 ) ;
return 0 ;
}
static void aspeed_gfx_enable_controller ( struct aspeed_gfx * priv )
{
u32 ctrl1 = readl ( priv - > base + CRT_CTRL1 ) ;
u32 ctrl2 = readl ( priv - > base + CRT_CTRL2 ) ;
/* SCU2C: set DAC source for display output to Graphics CRT (GFX) */
regmap_update_bits ( priv - > scu , 0x2c , BIT ( 16 ) , BIT ( 16 ) ) ;
writel ( ctrl1 | CRT_CTRL_EN , priv - > base + CRT_CTRL1 ) ;
writel ( ctrl2 | CRT_CTRL_DAC_EN , priv - > base + CRT_CTRL2 ) ;
}
static void aspeed_gfx_disable_controller ( struct aspeed_gfx * priv )
{
u32 ctrl1 = readl ( priv - > base + CRT_CTRL1 ) ;
u32 ctrl2 = readl ( priv - > base + CRT_CTRL2 ) ;
writel ( ctrl1 & ~ CRT_CTRL_EN , priv - > base + CRT_CTRL1 ) ;
writel ( ctrl2 & ~ CRT_CTRL_DAC_EN , priv - > base + CRT_CTRL2 ) ;
regmap_update_bits ( priv - > scu , 0x2c , BIT ( 16 ) , 0 ) ;
}
static void aspeed_gfx_crtc_mode_set_nofb ( struct aspeed_gfx * priv )
{
struct drm_display_mode * m = & priv - > pipe . crtc . state - > adjusted_mode ;
u32 ctrl1 , d_offset , t_count , bpp ;
int err ;
err = aspeed_gfx_set_pixel_fmt ( priv , & bpp ) ;
if ( err )
return ;
#if 0
/* TODO: we have only been able to test with the 40MHz USB clock. The
* clock is fixed , so we cannot adjust it here . */
clk_set_rate ( priv - > pixel_clk , m - > crtc_clock * 1000 ) ;
# endif
ctrl1 = readl ( priv - > base + CRT_CTRL1 ) ;
ctrl1 & = ~ ( CRT_CTRL_INTERLACED |
CRT_CTRL_HSYNC_NEGATIVE |
CRT_CTRL_VSYNC_NEGATIVE ) ;
if ( m - > flags & DRM_MODE_FLAG_INTERLACE )
ctrl1 | = CRT_CTRL_INTERLACED ;
if ( ! ( m - > flags & DRM_MODE_FLAG_PHSYNC ) )
ctrl1 | = CRT_CTRL_HSYNC_NEGATIVE ;
if ( ! ( m - > flags & DRM_MODE_FLAG_PVSYNC ) )
ctrl1 | = CRT_CTRL_VSYNC_NEGATIVE ;
writel ( ctrl1 , priv - > base + CRT_CTRL1 ) ;
/* Horizontal timing */
writel ( CRT_H_TOTAL ( m - > htotal - 1 ) | CRT_H_DE ( m - > hdisplay - 1 ) ,
priv - > base + CRT_HORIZ0 ) ;
writel ( CRT_H_RS_START ( m - > hsync_start - 1 ) | CRT_H_RS_END ( m - > hsync_end ) ,
priv - > base + CRT_HORIZ1 ) ;
/* Vertical timing */
writel ( CRT_V_TOTAL ( m - > vtotal - 1 ) | CRT_V_DE ( m - > vdisplay - 1 ) ,
priv - > base + CRT_VERT0 ) ;
writel ( CRT_V_RS_START ( m - > vsync_start ) | CRT_V_RS_END ( m - > vsync_end ) ,
priv - > base + CRT_VERT1 ) ;
/*
* Display Offset : address difference between consecutive scan lines
* Terminal Count : memory size of one scan line
*/
d_offset = m - > hdisplay * bpp / 8 ;
t_count = ( m - > hdisplay * bpp + 127 ) / 128 ;
writel ( CRT_DISP_OFFSET ( d_offset ) | CRT_TERM_COUNT ( t_count ) ,
priv - > base + CRT_OFFSET ) ;
/*
* Threshold : FIFO thresholds of refill and stop ( 16 byte chunks
* per line , rounded up )
*/
writel ( G5_CRT_THROD_VAL , priv - > base + CRT_THROD ) ;
}
static void aspeed_gfx_pipe_enable ( struct drm_simple_display_pipe * pipe ,
struct drm_crtc_state * crtc_state ,
struct drm_plane_state * plane_state )
{
struct aspeed_gfx * priv = drm_pipe_to_aspeed_gfx ( pipe ) ;
struct drm_crtc * crtc = & pipe - > crtc ;
aspeed_gfx_crtc_mode_set_nofb ( priv ) ;
aspeed_gfx_enable_controller ( priv ) ;
drm_crtc_vblank_on ( crtc ) ;
}
static void aspeed_gfx_pipe_disable ( struct drm_simple_display_pipe * pipe )
{
struct aspeed_gfx * priv = drm_pipe_to_aspeed_gfx ( pipe ) ;
struct drm_crtc * crtc = & pipe - > crtc ;
drm_crtc_vblank_off ( crtc ) ;
aspeed_gfx_disable_controller ( priv ) ;
}
static void aspeed_gfx_pipe_update ( struct drm_simple_display_pipe * pipe ,
struct drm_plane_state * plane_state )
{
struct aspeed_gfx * priv = drm_pipe_to_aspeed_gfx ( pipe ) ;
struct drm_crtc * crtc = & pipe - > crtc ;
struct drm_framebuffer * fb = pipe - > plane . state - > fb ;
struct drm_pending_vblank_event * event ;
struct drm_gem_cma_object * gem ;
spin_lock_irq ( & crtc - > dev - > event_lock ) ;
event = crtc - > state - > event ;
if ( event ) {
crtc - > state - > event = NULL ;
if ( drm_crtc_vblank_get ( crtc ) = = 0 )
drm_crtc_arm_vblank_event ( crtc , event ) ;
else
drm_crtc_send_vblank_event ( crtc , event ) ;
}
spin_unlock_irq ( & crtc - > dev - > event_lock ) ;
if ( ! fb )
return ;
gem = drm_fb_cma_get_gem_obj ( fb , 0 ) ;
if ( ! gem )
return ;
writel ( gem - > paddr , priv - > base + CRT_ADDR ) ;
}
static int aspeed_gfx_enable_vblank ( struct drm_simple_display_pipe * pipe )
{
struct aspeed_gfx * priv = drm_pipe_to_aspeed_gfx ( pipe ) ;
u32 reg = readl ( priv - > base + CRT_CTRL1 ) ;
/* Clear pending VBLANK IRQ */
writel ( reg | CRT_CTRL_VERTICAL_INTR_STS , priv - > base + CRT_CTRL1 ) ;
reg | = CRT_CTRL_VERTICAL_INTR_EN ;
writel ( reg , priv - > base + CRT_CTRL1 ) ;
return 0 ;
}
static void aspeed_gfx_disable_vblank ( struct drm_simple_display_pipe * pipe )
{
struct aspeed_gfx * priv = drm_pipe_to_aspeed_gfx ( pipe ) ;
u32 reg = readl ( priv - > base + CRT_CTRL1 ) ;
reg & = ~ CRT_CTRL_VERTICAL_INTR_EN ;
writel ( reg , priv - > base + CRT_CTRL1 ) ;
/* Clear pending VBLANK IRQ */
writel ( reg | CRT_CTRL_VERTICAL_INTR_STS , priv - > base + CRT_CTRL1 ) ;
}
2019-08-13 12:03:55 +05:30
static const struct drm_simple_display_pipe_funcs aspeed_gfx_funcs = {
2019-04-03 10:49:08 +10:30
. enable = aspeed_gfx_pipe_enable ,
. disable = aspeed_gfx_pipe_disable ,
. update = aspeed_gfx_pipe_update ,
. prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb ,
. enable_vblank = aspeed_gfx_enable_vblank ,
. disable_vblank = aspeed_gfx_disable_vblank ,
} ;
static const uint32_t aspeed_gfx_formats [ ] = {
DRM_FORMAT_XRGB8888 ,
DRM_FORMAT_RGB565 ,
} ;
int aspeed_gfx_create_pipe ( struct drm_device * drm )
{
struct aspeed_gfx * priv = drm - > dev_private ;
return drm_simple_display_pipe_init ( drm , & priv - > pipe , & aspeed_gfx_funcs ,
aspeed_gfx_formats ,
ARRAY_SIZE ( aspeed_gfx_formats ) ,
NULL ,
& priv - > connector ) ;
}