2012-08-15 13:59:49 +01:00
/*
* Copyright ( C ) 2012 Russell King
* Rewritten from the dovefb driver , and Armada510 manuals .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/clk.h>
2014-04-22 11:10:15 +01:00
# include <linux/component.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
2012-08-15 13:59:49 +01:00
# include <drm/drmP.h>
# include <drm/drm_crtc_helper.h>
2014-10-29 10:03:57 +01:00
# include <drm/drm_plane_helper.h>
2012-08-15 13:59:49 +01:00
# include "armada_crtc.h"
# include "armada_drm.h"
# include "armada_fb.h"
# include "armada_gem.h"
# include "armada_hw.h"
2016-05-17 13:51:08 +01:00
# include "armada_trace.h"
2012-08-15 13:59:49 +01:00
struct armada_frame_work {
2015-08-06 16:37:18 +01:00
struct armada_plane_work work ;
2012-08-15 13:59:49 +01:00
struct drm_pending_vblank_event * event ;
struct armada_regs regs [ 4 ] ;
struct drm_framebuffer * old_fb ;
} ;
enum csc_mode {
CSC_AUTO = 0 ,
CSC_YUV_CCIR601 = 1 ,
CSC_YUV_CCIR709 = 2 ,
CSC_RGB_COMPUTER = 1 ,
CSC_RGB_STUDIO = 2 ,
} ;
2015-07-15 18:11:24 +01:00
static const uint32_t armada_primary_formats [ ] = {
DRM_FORMAT_UYVY ,
DRM_FORMAT_YUYV ,
DRM_FORMAT_VYUY ,
DRM_FORMAT_YVYU ,
DRM_FORMAT_ARGB8888 ,
DRM_FORMAT_ABGR8888 ,
DRM_FORMAT_XRGB8888 ,
DRM_FORMAT_XBGR8888 ,
DRM_FORMAT_RGB888 ,
DRM_FORMAT_BGR888 ,
DRM_FORMAT_ARGB1555 ,
DRM_FORMAT_ABGR1555 ,
DRM_FORMAT_RGB565 ,
DRM_FORMAT_BGR565 ,
} ;
2012-08-15 13:59:49 +01:00
/*
* A note about interlacing . Let ' s consider HDMI 1920 x1080i .
* The timing parameters we have from X are :
* Hact HsyA HsyI Htot Vact VsyA VsyI Vtot
* 1920 2448 2492 2640 1080 1084 1094 1125
* Which get translated to :
* Hact HsyA HsyI Htot Vact VsyA VsyI Vtot
* 1920 2448 2492 2640 540 542 547 562
*
* This is how it is defined by CEA - 861 - D - line and pixel numbers are
* referenced to the rising edge of VSYNC and HSYNC . Total clocks per
* line : 2640. The odd frame , the first active line is at line 21 , and
* the even frame , the first active line is 584.
*
* LN : 560 561 562 563 567 568 569
* DE : ~ ~ ~ | ____________________________ //__________________________
* HSYNC : ____ | ~ | _____ | ~ | _____ | ~ | _____ | ~ | _ //__|~|_____|~|_____|~|_____
* VSYNC : _________________________ | ~ ~ ~ ~ ~ ~ //~~~~~~~~~~~~~~~|__________
* 22 blanking lines . VSYNC at 1320 ( referenced to the HSYNC rising edge ) .
*
* LN : 1123 1124 1125 1 5 6 7
* DE : ~ ~ ~ | ____________________________ //__________________________
* HSYNC : ____ | ~ | _____ | ~ | _____ | ~ | _____ | ~ | _ //__|~|_____|~|_____|~|_____
* VSYNC : ____________________ | ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ //~~~~~~~~~~|_______________
* 23 blanking lines
*
* The Armada LCD Controller line and pixel numbers are , like X timings ,
* referenced to the top left of the active frame .
*
* So , translating these to our LCD controller :
* Odd frame , 563 total lines , VSYNC at line 543 - 548 , pixel 1128.
* Even frame , 562 total lines , VSYNC at line 542 - 547 , pixel 2448.
* Note : Vsync front porch remains constant !
*
* if ( odd_frame ) {
* vtotal = mode - > crtc_vtotal + 1 ;
* vbackporch = mode - > crtc_vsync_start - mode - > crtc_vdisplay + 1 ;
* vhorizpos = mode - > crtc_hsync_start - mode - > crtc_htotal / 2
* } else {
* vtotal = mode - > crtc_vtotal ;
* vbackporch = mode - > crtc_vsync_start - mode - > crtc_vdisplay ;
* vhorizpos = mode - > crtc_hsync_start ;
* }
* vfrontporch = mode - > crtc_vtotal - mode - > crtc_vsync_end ;
*
* So , we need to reprogram these registers on each vsync event :
* LCD_SPU_V_PORCH , LCD_SPU_ADV_REG , LCD_SPUT_V_H_TOTAL
*
* Note : we do not use the frame done interrupts because these appear
* to happen too early , and lead to jitter on the display ( presumably
* they occur at the end of the last active line , before the vsync back
* porch , which we ' re reprogramming . )
*/
void
armada_drm_crtc_update_regs ( struct armada_crtc * dcrtc , struct armada_regs * regs )
{
while ( regs - > offset ! = ~ 0 ) {
void __iomem * reg = dcrtc - > base + regs - > offset ;
uint32_t val ;
val = regs - > mask ;
if ( val ! = 0 )
val & = readl_relaxed ( reg ) ;
writel_relaxed ( val | regs - > val , reg ) ;
+ + regs ;
}
}
# define dpms_blanked(dpms) ((dpms) != DRM_MODE_DPMS_ON)
static void armada_drm_crtc_update ( struct armada_crtc * dcrtc )
{
uint32_t dumb_ctrl ;
dumb_ctrl = dcrtc - > cfg_dumb_ctrl ;
if ( ! dpms_blanked ( dcrtc - > dpms ) )
dumb_ctrl | = CFG_DUMB_ENA ;
/*
* When the dumb interface isn ' t in DUMB24_RGB888_0 mode , it might
* be using SPI or GPIO . If we set this to DUMB_BLANK , we will
* force LCD_D [ 23 : 0 ] to output blank color , overriding the GPIO or
* SPI usage . So leave it as - is unless in DUMB24_RGB888_0 mode .
*/
if ( dpms_blanked ( dcrtc - > dpms ) & &
( dumb_ctrl & DUMB_MASK ) = = DUMB24_RGB888_0 ) {
dumb_ctrl & = ~ DUMB_MASK ;
dumb_ctrl | = DUMB_BLANK ;
}
/*
* The documentation doesn ' t indicate what the normal state of
* the sync signals are . Sebastian Hesselbart kindly probed
* these signals on his board to determine their state .
*
* The non - inverted state of the sync signals is active high .
* Setting these bits makes the appropriate signal active low .
*/
if ( dcrtc - > crtc . mode . flags & DRM_MODE_FLAG_NCSYNC )
dumb_ctrl | = CFG_INV_CSYNC ;
if ( dcrtc - > crtc . mode . flags & DRM_MODE_FLAG_NHSYNC )
dumb_ctrl | = CFG_INV_HSYNC ;
if ( dcrtc - > crtc . mode . flags & DRM_MODE_FLAG_NVSYNC )
dumb_ctrl | = CFG_INV_VSYNC ;
if ( dcrtc - > dumb_ctrl ! = dumb_ctrl ) {
dcrtc - > dumb_ctrl = dumb_ctrl ;
writel_relaxed ( dumb_ctrl , dcrtc - > base + LCD_SPU_DUMB_CTRL ) ;
}
}
2016-08-16 22:09:11 +01:00
void armada_drm_plane_calc_addrs ( u32 * addrs , struct drm_framebuffer * fb ,
int x , int y )
{
u32 addr = drm_fb_obj ( fb ) - > dev_addr ;
2016-12-14 23:30:22 +02:00
int num_planes = fb - > format - > num_planes ;
2016-08-16 22:09:11 +01:00
int i ;
if ( num_planes > 3 )
num_planes = 3 ;
for ( i = 0 ; i < num_planes ; i + + )
addrs [ i ] = addr + fb - > offsets [ i ] + y * fb - > pitches [ i ] +
2016-12-14 23:30:57 +02:00
x * fb - > format - > cpp [ i ] ;
2016-08-16 22:09:11 +01:00
for ( ; i < 3 ; i + + )
addrs [ i ] = 0 ;
}
2012-08-15 13:59:49 +01:00
static unsigned armada_drm_crtc_calc_fb ( struct drm_framebuffer * fb ,
int x , int y , struct armada_regs * regs , bool interlaced )
{
unsigned pitch = fb - > pitches [ 0 ] ;
2016-08-16 22:09:11 +01:00
u32 addrs [ 3 ] , addr_odd , addr_even ;
2012-08-15 13:59:49 +01:00
unsigned i = 0 ;
DRM_DEBUG_DRIVER ( " pitch %u x %d y %d bpp %d \n " ,
2016-12-14 23:32:20 +02:00
pitch , x , y , fb - > format - > cpp [ 0 ] * 8 ) ;
2012-08-15 13:59:49 +01:00
2016-08-16 22:09:11 +01:00
armada_drm_plane_calc_addrs ( addrs , fb , x , y ) ;
addr_odd = addr_even = addrs [ 0 ] ;
2012-08-15 13:59:49 +01:00
if ( interlaced ) {
addr_even + = pitch ;
pitch * = 2 ;
}
/* write offset, base, and pitch */
armada_reg_queue_set ( regs , i , addr_odd , LCD_CFG_GRA_START_ADDR0 ) ;
armada_reg_queue_set ( regs , i , addr_even , LCD_CFG_GRA_START_ADDR1 ) ;
armada_reg_queue_mod ( regs , i , pitch , 0xffff , LCD_CFG_GRA_PITCH ) ;
return i ;
}
2015-08-06 16:37:18 +01:00
static void armada_drm_plane_work_run ( struct armada_crtc * dcrtc ,
2016-07-25 15:16:11 +01:00
struct drm_plane * plane )
2015-08-06 16:37:18 +01:00
{
2016-07-25 15:16:11 +01:00
struct armada_plane * dplane = drm_to_armada_plane ( plane ) ;
struct armada_plane_work * work = xchg ( & dplane - > work , NULL ) ;
2015-08-06 16:37:18 +01:00
/* Handle any pending frame work. */
if ( work ) {
2016-07-25 15:16:11 +01:00
work - > fn ( dcrtc , dplane , work ) ;
2016-06-06 11:41:40 -03:00
drm_crtc_vblank_put ( & dcrtc - > crtc ) ;
2015-08-06 16:37:18 +01:00
}
2015-08-07 13:34:26 +01:00
2016-07-25 15:16:11 +01:00
wake_up ( & dplane - > frame_wait ) ;
2015-08-06 16:37:18 +01:00
}
int armada_drm_plane_work_queue ( struct armada_crtc * dcrtc ,
struct armada_plane * plane , struct armada_plane_work * work )
{
int ret ;
2016-06-06 11:41:40 -03:00
ret = drm_crtc_vblank_get ( & dcrtc - > crtc ) ;
2015-08-06 16:37:18 +01:00
if ( ret ) {
DRM_ERROR ( " failed to acquire vblank counter \n " ) ;
return ret ;
}
ret = cmpxchg ( & plane - > work , NULL , work ) ? - EBUSY : 0 ;
if ( ret )
2016-06-06 11:41:40 -03:00
drm_crtc_vblank_put ( & dcrtc - > crtc ) ;
2015-08-06 16:37:18 +01:00
return ret ;
}
int armada_drm_plane_work_wait ( struct armada_plane * plane , long timeout )
{
return wait_event_timeout ( plane - > frame_wait , ! plane - > work , timeout ) ;
}
2015-08-07 09:33:05 +01:00
struct armada_plane_work * armada_drm_plane_work_cancel (
struct armada_crtc * dcrtc , struct armada_plane * plane )
2015-06-29 17:52:16 +01:00
{
2015-08-07 09:33:05 +01:00
struct armada_plane_work * work = xchg ( & plane - > work , NULL ) ;
2015-06-29 17:52:16 +01:00
2015-08-07 09:33:05 +01:00
if ( work )
2016-06-06 11:41:40 -03:00
drm_crtc_vblank_put ( & dcrtc - > crtc ) ;
2015-06-29 17:52:16 +01:00
2015-08-07 09:33:05 +01:00
return work ;
2015-06-29 17:52:16 +01:00
}
2012-08-15 13:59:49 +01:00
static int armada_drm_crtc_queue_frame_work ( struct armada_crtc * dcrtc ,
struct armada_frame_work * work )
{
2015-08-06 16:37:18 +01:00
struct armada_plane * plane = drm_to_armada_plane ( dcrtc - > crtc . primary ) ;
2012-08-15 13:59:49 +01:00
2015-08-06 16:37:18 +01:00
return armada_drm_plane_work_queue ( dcrtc , plane , & work - > work ) ;
2012-08-15 13:59:49 +01:00
}
2015-07-15 18:09:38 +01:00
static void armada_drm_crtc_complete_frame_work ( struct armada_crtc * dcrtc ,
2015-08-06 16:37:18 +01:00
struct armada_plane * plane , struct armada_plane_work * work )
2012-08-15 13:59:49 +01:00
{
2015-08-06 16:37:18 +01:00
struct armada_frame_work * fwork = container_of ( work , struct armada_frame_work , work ) ;
2012-08-15 13:59:49 +01:00
struct drm_device * dev = dcrtc - > crtc . dev ;
2015-07-15 18:09:38 +01:00
unsigned long flags ;
2012-08-15 13:59:49 +01:00
2015-07-15 18:09:38 +01:00
spin_lock_irqsave ( & dcrtc - > irq_lock , flags ) ;
2015-08-06 16:37:18 +01:00
armada_drm_crtc_update_regs ( dcrtc , fwork - > regs ) ;
2015-07-15 18:09:38 +01:00
spin_unlock_irqrestore ( & dcrtc - > irq_lock , flags ) ;
2012-08-15 13:59:49 +01:00
2015-08-06 16:37:18 +01:00
if ( fwork - > event ) {
2015-07-15 18:09:38 +01:00
spin_lock_irqsave ( & dev - > event_lock , flags ) ;
2016-06-06 11:41:33 -03:00
drm_crtc_send_vblank_event ( & dcrtc - > crtc , fwork - > event ) ;
2015-07-15 18:09:38 +01:00
spin_unlock_irqrestore ( & dev - > event_lock , flags ) ;
}
2012-08-15 13:59:49 +01:00
/* Finally, queue the process-half of the cleanup. */
2015-08-06 16:37:18 +01:00
__armada_drm_queue_unref_work ( dcrtc - > crtc . dev , fwork - > old_fb ) ;
kfree ( fwork ) ;
2012-08-15 13:59:49 +01:00
}
static void armada_drm_crtc_finish_fb ( struct armada_crtc * dcrtc ,
struct drm_framebuffer * fb , bool force )
{
struct armada_frame_work * work ;
if ( ! fb )
return ;
if ( force ) {
/* Display is disabled, so just drop the old fb */
drm_framebuffer_unreference ( fb ) ;
return ;
}
work = kmalloc ( sizeof ( * work ) , GFP_KERNEL ) ;
if ( work ) {
int i = 0 ;
2015-08-06 16:37:18 +01:00
work - > work . fn = armada_drm_crtc_complete_frame_work ;
2012-08-15 13:59:49 +01:00
work - > event = NULL ;
work - > old_fb = fb ;
armada_reg_queue_end ( work - > regs , i ) ;
if ( armada_drm_crtc_queue_frame_work ( dcrtc , work ) = = 0 )
return ;
kfree ( work ) ;
}
/*
* Oops - just drop the reference immediately and hope for
* the best . The worst that will happen is the buffer gets
* reused before it has finished being displayed .
*/
drm_framebuffer_unreference ( fb ) ;
}
static void armada_drm_vblank_off ( struct armada_crtc * dcrtc )
{
/*
* Tell the DRM core that vblank IRQs aren ' t going to happen for
* a while . This cleans up any pending vblank events for us .
*/
2014-10-11 23:57:04 +01:00
drm_crtc_vblank_off ( & dcrtc - > crtc ) ;
2016-07-25 15:16:11 +01:00
armada_drm_plane_work_run ( dcrtc , dcrtc - > crtc . primary ) ;
2012-08-15 13:59:49 +01:00
}
void armada_drm_crtc_gamma_set ( struct drm_crtc * crtc , u16 r , u16 g , u16 b ,
int idx )
{
}
void armada_drm_crtc_gamma_get ( struct drm_crtc * crtc , u16 * r , u16 * g , u16 * b ,
int idx )
{
}
/* The mode_config.mutex will be held for this call */
static void armada_drm_crtc_dpms ( struct drm_crtc * crtc , int dpms )
{
struct armada_crtc * dcrtc = drm_to_armada_crtc ( crtc ) ;
2016-10-04 22:19:57 +01:00
if ( dpms_blanked ( dcrtc - > dpms ) ! = dpms_blanked ( dpms ) ) {
2012-08-15 13:59:49 +01:00
if ( dpms_blanked ( dpms ) )
armada_drm_vblank_off ( dcrtc ) ;
2016-10-04 22:19:57 +01:00
else if ( ! IS_ERR ( dcrtc - > clk ) )
WARN_ON ( clk_prepare_enable ( dcrtc - > clk ) ) ;
dcrtc - > dpms = dpms ;
armada_drm_crtc_update ( dcrtc ) ;
if ( ! dpms_blanked ( dpms ) )
2014-10-11 23:57:04 +01:00
drm_crtc_vblank_on ( & dcrtc - > crtc ) ;
2016-10-04 22:19:57 +01:00
else if ( ! IS_ERR ( dcrtc - > clk ) )
clk_disable_unprepare ( dcrtc - > clk ) ;
} else if ( dcrtc - > dpms ! = dpms ) {
dcrtc - > dpms = dpms ;
2012-08-15 13:59:49 +01:00
}
}
/*
* Prepare for a mode set . Turn off overlay to ensure that we don ' t end
* up with the overlay size being bigger than the active screen size .
* We rely upon X refreshing this state after the mode set has completed .
*
* The mode_config . mutex will be held for this call
*/
static void armada_drm_crtc_prepare ( struct drm_crtc * crtc )
{
struct armada_crtc * dcrtc = drm_to_armada_crtc ( crtc ) ;
struct drm_plane * plane ;
/*
* If we have an overlay plane associated with this CRTC , disable
* it before the modeset to avoid its coordinates being outside
2015-06-29 17:52:42 +01:00
* the new mode parameters .
2012-08-15 13:59:49 +01:00
*/
plane = dcrtc - > plane ;
2015-06-29 17:52:42 +01:00
if ( plane )
drm_plane_force_disable ( plane ) ;
2012-08-15 13:59:49 +01:00
}
/* The mode_config.mutex will be held for this call */
static void armada_drm_crtc_commit ( struct drm_crtc * crtc )
{
struct armada_crtc * dcrtc = drm_to_armada_crtc ( crtc ) ;
if ( dcrtc - > dpms ! = DRM_MODE_DPMS_ON ) {
dcrtc - > dpms = DRM_MODE_DPMS_ON ;
armada_drm_crtc_update ( dcrtc ) ;
}
}
/* The mode_config.mutex will be held for this call */
static bool armada_drm_crtc_mode_fixup ( struct drm_crtc * crtc ,
const struct drm_display_mode * mode , struct drm_display_mode * adj )
{
struct armada_crtc * dcrtc = drm_to_armada_crtc ( crtc ) ;
int ret ;
/* We can't do interlaced modes if we don't have the SPU_ADV_REG */
2014-04-22 15:24:03 +01:00
if ( ! dcrtc - > variant - > has_spu_adv_reg & &
2012-08-15 13:59:49 +01:00
adj - > flags & DRM_MODE_FLAG_INTERLACE )
return false ;
/* Check whether the display mode is possible */
2014-04-22 15:24:03 +01:00
ret = dcrtc - > variant - > compute_clock ( dcrtc , adj , NULL ) ;
2012-08-15 13:59:49 +01:00
if ( ret )
return false ;
return true ;
}
2014-04-26 15:19:38 +01:00
static void armada_drm_crtc_irq ( struct armada_crtc * dcrtc , u32 stat )
2012-08-15 13:59:49 +01:00
{
void __iomem * base = dcrtc - > base ;
2015-08-07 09:33:05 +01:00
struct drm_plane * ovl_plane ;
2012-08-15 13:59:49 +01:00
if ( stat & DMA_FF_UNDERFLOW )
DRM_ERROR ( " video underflow on crtc %u \n " , dcrtc - > num ) ;
if ( stat & GRA_FF_UNDERFLOW )
DRM_ERROR ( " graphics underflow on crtc %u \n " , dcrtc - > num ) ;
if ( stat & VSYNC_IRQ )
2016-07-04 21:04:48 -03:00
drm_crtc_handle_vblank ( & dcrtc - > crtc ) ;
2012-08-15 13:59:49 +01:00
spin_lock ( & dcrtc - > irq_lock ) ;
2015-08-07 09:33:05 +01:00
ovl_plane = dcrtc - > plane ;
2016-07-25 15:16:11 +01:00
if ( ovl_plane )
armada_drm_plane_work_run ( dcrtc , ovl_plane ) ;
2012-08-15 13:59:49 +01:00
if ( stat & GRA_FRAME_IRQ & & dcrtc - > interlaced ) {
int i = stat & GRA_FRAME_IRQ0 ? 0 : 1 ;
uint32_t val ;
writel_relaxed ( dcrtc - > v [ i ] . spu_v_porch , base + LCD_SPU_V_PORCH ) ;
writel_relaxed ( dcrtc - > v [ i ] . spu_v_h_total ,
base + LCD_SPUT_V_H_TOTAL ) ;
val = readl_relaxed ( base + LCD_SPU_ADV_REG ) ;
val & = ~ ( ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN ) ;
val | = dcrtc - > v [ i ] . spu_adv_reg ;
2013-05-19 10:55:17 +01:00
writel_relaxed ( val , base + LCD_SPU_ADV_REG ) ;
2012-08-15 13:59:49 +01:00
}
2013-05-19 10:55:17 +01:00
if ( stat & DUMB_FRAMEDONE & & dcrtc - > cursor_update ) {
writel_relaxed ( dcrtc - > cursor_hw_pos ,
base + LCD_SPU_HWC_OVSA_HPXL_VLN ) ;
writel_relaxed ( dcrtc - > cursor_hw_sz ,
base + LCD_SPU_HWC_HPXL_VLN ) ;
armada_updatel ( CFG_HWC_ENA ,
CFG_HWC_ENA | CFG_HWC_1BITMOD | CFG_HWC_1BITENA ,
base + LCD_SPU_DMA_CTRL0 ) ;
dcrtc - > cursor_update = false ;
armada_drm_crtc_disable_irq ( dcrtc , DUMB_FRAMEDONE_ENA ) ;
}
2012-08-15 13:59:49 +01:00
spin_unlock ( & dcrtc - > irq_lock ) ;
2016-07-25 15:16:11 +01:00
if ( stat & GRA_FRAME_IRQ )
armada_drm_plane_work_run ( dcrtc , dcrtc - > crtc . primary ) ;
2012-08-15 13:59:49 +01:00
}
2014-04-26 15:19:38 +01:00
static irqreturn_t armada_drm_irq ( int irq , void * arg )
{
struct armada_crtc * dcrtc = arg ;
u32 v , stat = readl_relaxed ( dcrtc - > base + LCD_SPU_IRQ_ISR ) ;
/*
* This is rediculous - rather than writing bits to clear , we
* have to set the actual status register value . This is racy .
*/
writel_relaxed ( 0 , dcrtc - > base + LCD_SPU_IRQ_ISR ) ;
2016-05-17 13:51:08 +01:00
trace_armada_drm_irq ( & dcrtc - > crtc , stat ) ;
2014-04-26 15:19:38 +01:00
/* Mask out those interrupts we haven't enabled */
v = stat & dcrtc - > irq_ena ;
if ( v & ( VSYNC_IRQ | GRA_FRAME_IRQ | DUMB_FRAMEDONE ) ) {
armada_drm_crtc_irq ( dcrtc , stat ) ;
return IRQ_HANDLED ;
}
return IRQ_NONE ;
}
2012-08-15 13:59:49 +01:00
/* These are locked by dev->vbl_lock */
void armada_drm_crtc_disable_irq ( struct armada_crtc * dcrtc , u32 mask )
{
if ( dcrtc - > irq_ena & mask ) {
dcrtc - > irq_ena & = ~ mask ;
writel ( dcrtc - > irq_ena , dcrtc - > base + LCD_SPU_IRQ_ENA ) ;
}
}
void armada_drm_crtc_enable_irq ( struct armada_crtc * dcrtc , u32 mask )
{
if ( ( dcrtc - > irq_ena & mask ) ! = mask ) {
dcrtc - > irq_ena | = mask ;
writel ( dcrtc - > irq_ena , dcrtc - > base + LCD_SPU_IRQ_ENA ) ;
if ( readl_relaxed ( dcrtc - > base + LCD_SPU_IRQ_ISR ) & mask )
writel ( 0 , dcrtc - > base + LCD_SPU_IRQ_ISR ) ;
}
}
static uint32_t armada_drm_crtc_calculate_csc ( struct armada_crtc * dcrtc )
{
struct drm_display_mode * adj = & dcrtc - > crtc . mode ;
uint32_t val = 0 ;
if ( dcrtc - > csc_yuv_mode = = CSC_YUV_CCIR709 )
val | = CFG_CSC_YUV_CCIR709 ;
if ( dcrtc - > csc_rgb_mode = = CSC_RGB_STUDIO )
val | = CFG_CSC_RGB_STUDIO ;
/*
* In auto mode , set the colorimetry , based upon the HDMI spec .
* 1280 x720p , 1920 x1080p and 1920 x1080i use ITU709 , others use
* ITU601 . It may be more appropriate to set this depending on
* the source - but what if the graphic frame is YUV and the
* video frame is RGB ?
*/
if ( ( adj - > hdisplay = = 1280 & & adj - > vdisplay = = 720 & &
! ( adj - > flags & DRM_MODE_FLAG_INTERLACE ) ) | |
( adj - > hdisplay = = 1920 & & adj - > vdisplay = = 1080 ) ) {
if ( dcrtc - > csc_yuv_mode = = CSC_AUTO )
val | = CFG_CSC_YUV_CCIR709 ;
}
/*
* We assume we ' re connected to a TV - like device , so the YUV - > RGB
* conversion should produce a limited range . We should set this
* depending on the connectors attached to this CRTC , and what
* kind of device they report being connected .
*/
if ( dcrtc - > csc_rgb_mode = = CSC_AUTO )
val | = CFG_CSC_RGB_STUDIO ;
return val ;
}
2016-08-16 22:09:09 +01:00
static void armada_drm_primary_set ( struct drm_crtc * crtc ,
struct drm_plane * plane , int x , int y )
{
struct armada_plane_state * state = & drm_to_armada_plane ( plane ) - > state ;
struct armada_crtc * dcrtc = drm_to_armada_crtc ( crtc ) ;
2016-08-16 22:09:10 +01:00
struct armada_regs regs [ 8 ] ;
2016-08-16 22:09:09 +01:00
bool interlaced = dcrtc - > interlaced ;
unsigned i ;
2016-08-16 22:09:10 +01:00
u32 ctrl0 ;
2016-08-16 22:09:09 +01:00
i = armada_drm_crtc_calc_fb ( plane - > fb , x , y , regs , interlaced ) ;
2016-08-16 22:09:10 +01:00
armada_reg_queue_set ( regs , i , state - > dst_yx , LCD_SPU_GRA_OVSA_HPXL_VLN ) ;
2016-08-16 22:09:09 +01:00
armada_reg_queue_set ( regs , i , state - > src_hw , LCD_SPU_GRA_HPXL_VLN ) ;
armada_reg_queue_set ( regs , i , state - > dst_hw , LCD_SPU_GZM_HPXL_VLN ) ;
ctrl0 = state - > ctrl0 ;
if ( interlaced )
ctrl0 | = CFG_GRA_FTOGGLE ;
armada_reg_queue_mod ( regs , i , ctrl0 , CFG_GRAFORMAT |
CFG_GRA_MOD ( CFG_SWAPRB | CFG_SWAPUV |
CFG_SWAPYU | CFG_YUV2RGB ) |
CFG_PALETTE_ENA | CFG_GRA_FTOGGLE ,
LCD_SPU_DMA_CTRL0 ) ;
armada_reg_queue_end ( regs , i ) ;
armada_drm_crtc_update_regs ( dcrtc , regs ) ;
}
2012-08-15 13:59:49 +01:00
/* The mode_config.mutex will be held for this call */
static int armada_drm_crtc_mode_set ( struct drm_crtc * crtc ,
struct drm_display_mode * mode , struct drm_display_mode * adj ,
int x , int y , struct drm_framebuffer * old_fb )
{
struct armada_crtc * dcrtc = drm_to_armada_crtc ( crtc ) ;
struct armada_regs regs [ 17 ] ;
uint32_t lm , rm , tm , bm , val , sclk ;
unsigned long flags ;
unsigned i ;
bool interlaced ;
2014-04-01 15:22:40 -07:00
drm_framebuffer_reference ( crtc - > primary - > fb ) ;
2012-08-15 13:59:49 +01:00
interlaced = ! ! ( adj - > flags & DRM_MODE_FLAG_INTERLACE ) ;
2016-08-16 22:09:08 +01:00
val = CFG_GRA_ENA | CFG_GRA_HSMOOTH ;
val | = CFG_GRA_FMT ( drm_fb_to_armada_fb ( dcrtc - > crtc . primary - > fb ) - > fmt ) ;
val | = CFG_GRA_MOD ( drm_fb_to_armada_fb ( dcrtc - > crtc . primary - > fb ) - > mod ) ;
if ( drm_fb_to_armada_fb ( dcrtc - > crtc . primary - > fb ) - > fmt > CFG_420 )
val | = CFG_PALETTE_ENA ;
drm_to_armada_plane ( crtc - > primary ) - > state . ctrl0 = val ;
drm_to_armada_plane ( crtc - > primary ) - > state . src_hw =
drm_to_armada_plane ( crtc - > primary ) - > state . dst_hw =
2016-08-16 22:09:09 +01:00
adj - > crtc_vdisplay < < 16 | adj - > crtc_hdisplay ;
2016-08-16 22:09:08 +01:00
drm_to_armada_plane ( crtc - > primary ) - > state . dst_yx = 0 ;
2012-08-15 13:59:49 +01:00
2016-08-16 22:09:09 +01:00
i = 0 ;
2012-08-15 13:59:49 +01:00
rm = adj - > crtc_hsync_start - adj - > crtc_hdisplay ;
lm = adj - > crtc_htotal - adj - > crtc_hsync_end ;
bm = adj - > crtc_vsync_start - adj - > crtc_vdisplay ;
tm = adj - > crtc_vtotal - adj - > crtc_vsync_end ;
DRM_DEBUG_DRIVER ( " H: %d %d %d %d lm %d rm %d \n " ,
adj - > crtc_hdisplay ,
adj - > crtc_hsync_start ,
adj - > crtc_hsync_end ,
adj - > crtc_htotal , lm , rm ) ;
DRM_DEBUG_DRIVER ( " V: %d %d %d %d tm %d bm %d \n " ,
adj - > crtc_vdisplay ,
adj - > crtc_vsync_start ,
adj - > crtc_vsync_end ,
adj - > crtc_vtotal , tm , bm ) ;
/* Wait for pending flips to complete */
2015-08-06 16:37:18 +01:00
armada_drm_plane_work_wait ( drm_to_armada_plane ( dcrtc - > crtc . primary ) ,
MAX_SCHEDULE_TIMEOUT ) ;
2012-08-15 13:59:49 +01:00
2014-10-11 23:57:04 +01:00
drm_crtc_vblank_off ( crtc ) ;
2012-08-15 13:59:49 +01:00
val = dcrtc - > dumb_ctrl & ~ CFG_DUMB_ENA ;
if ( val ! = dcrtc - > dumb_ctrl ) {
dcrtc - > dumb_ctrl = val ;
writel_relaxed ( val , dcrtc - > base + LCD_SPU_DUMB_CTRL ) ;
}
2015-06-29 18:01:38 +01:00
/*
* If we are blanked , we would have disabled the clock . Re - enable
* it so that compute_clock ( ) does the right thing .
*/
if ( ! IS_ERR ( dcrtc - > clk ) & & dpms_blanked ( dcrtc - > dpms ) )
WARN_ON ( clk_prepare_enable ( dcrtc - > clk ) ) ;
2012-08-15 13:59:49 +01:00
/* Now compute the divider for real */
2014-04-22 15:24:03 +01:00
dcrtc - > variant - > compute_clock ( dcrtc , adj , & sclk ) ;
2012-08-15 13:59:49 +01:00
/* Ensure graphic fifo is enabled */
armada_reg_queue_mod ( regs , i , 0 , CFG_PDWN64x66 , LCD_SPU_SRAM_PARA1 ) ;
armada_reg_queue_set ( regs , i , sclk , LCD_CFG_SCLK_DIV ) ;
if ( interlaced ^ dcrtc - > interlaced ) {
if ( adj - > flags & DRM_MODE_FLAG_INTERLACE )
2016-06-06 11:41:40 -03:00
drm_crtc_vblank_get ( & dcrtc - > crtc ) ;
2012-08-15 13:59:49 +01:00
else
2016-06-06 11:41:40 -03:00
drm_crtc_vblank_put ( & dcrtc - > crtc ) ;
2012-08-15 13:59:49 +01:00
dcrtc - > interlaced = interlaced ;
}
spin_lock_irqsave ( & dcrtc - > irq_lock , flags ) ;
/* Even interlaced/progressive frame */
dcrtc - > v [ 1 ] . spu_v_h_total = adj - > crtc_vtotal < < 16 |
adj - > crtc_htotal ;
dcrtc - > v [ 1 ] . spu_v_porch = tm < < 16 | bm ;
val = adj - > crtc_hsync_start ;
2013-05-19 10:55:17 +01:00
dcrtc - > v [ 1 ] . spu_adv_reg = val < < 20 | val | ADV_VSYNCOFFEN |
2014-04-22 15:24:03 +01:00
dcrtc - > variant - > spu_adv_reg ;
2012-08-15 13:59:49 +01:00
if ( interlaced ) {
/* Odd interlaced frame */
dcrtc - > v [ 0 ] . spu_v_h_total = dcrtc - > v [ 1 ] . spu_v_h_total +
( 1 < < 16 ) ;
dcrtc - > v [ 0 ] . spu_v_porch = dcrtc - > v [ 1 ] . spu_v_porch + 1 ;
val = adj - > crtc_hsync_start - adj - > crtc_htotal / 2 ;
2013-05-19 10:55:17 +01:00
dcrtc - > v [ 0 ] . spu_adv_reg = val < < 20 | val | ADV_VSYNCOFFEN |
2014-04-22 15:24:03 +01:00
dcrtc - > variant - > spu_adv_reg ;
2012-08-15 13:59:49 +01:00
} else {
dcrtc - > v [ 0 ] = dcrtc - > v [ 1 ] ;
}
val = adj - > crtc_vdisplay < < 16 | adj - > crtc_hdisplay ;
armada_reg_queue_set ( regs , i , val , LCD_SPU_V_H_ACTIVE ) ;
armada_reg_queue_set ( regs , i , ( lm < < 16 ) | rm , LCD_SPU_H_PORCH ) ;
armada_reg_queue_set ( regs , i , dcrtc - > v [ 0 ] . spu_v_porch , LCD_SPU_V_PORCH ) ;
armada_reg_queue_set ( regs , i , dcrtc - > v [ 0 ] . spu_v_h_total ,
LCD_SPUT_V_H_TOTAL ) ;
2014-04-22 15:24:03 +01:00
if ( dcrtc - > variant - > has_spu_adv_reg ) {
2012-08-15 13:59:49 +01:00
armada_reg_queue_mod ( regs , i , dcrtc - > v [ 0 ] . spu_adv_reg ,
ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF |
ADV_VSYNCOFFEN , LCD_SPU_ADV_REG ) ;
2013-05-19 10:55:17 +01:00
}
2012-08-15 13:59:49 +01:00
val = adj - > flags & DRM_MODE_FLAG_NVSYNC ? CFG_VSYNC_INV : 0 ;
armada_reg_queue_mod ( regs , i , val , CFG_VSYNC_INV , LCD_SPU_DMA_CTRL1 ) ;
val = dcrtc - > spu_iopad_ctrl | armada_drm_crtc_calculate_csc ( dcrtc ) ;
armada_reg_queue_set ( regs , i , val , LCD_SPU_IOPAD_CONTROL ) ;
armada_reg_queue_end ( regs , i ) ;
armada_drm_crtc_update_regs ( dcrtc , regs ) ;
2016-08-16 22:09:09 +01:00
armada_drm_primary_set ( crtc , crtc - > primary , x , y ) ;
2012-08-15 13:59:49 +01:00
spin_unlock_irqrestore ( & dcrtc - > irq_lock , flags ) ;
armada_drm_crtc_update ( dcrtc ) ;
2014-10-11 23:57:04 +01:00
drm_crtc_vblank_on ( crtc ) ;
2012-08-15 13:59:49 +01:00
armada_drm_crtc_finish_fb ( dcrtc , old_fb , dpms_blanked ( dcrtc - > dpms ) ) ;
return 0 ;
}
/* The mode_config.mutex will be held for this call */
static int armada_drm_crtc_mode_set_base ( struct drm_crtc * crtc , int x , int y ,
struct drm_framebuffer * old_fb )
{
struct armada_crtc * dcrtc = drm_to_armada_crtc ( crtc ) ;
struct armada_regs regs [ 4 ] ;
unsigned i ;
2014-04-01 15:22:40 -07:00
i = armada_drm_crtc_calc_fb ( crtc - > primary - > fb , crtc - > x , crtc - > y , regs ,
2012-08-15 13:59:49 +01:00
dcrtc - > interlaced ) ;
armada_reg_queue_end ( regs , i ) ;
/* Wait for pending flips to complete */
2015-08-06 16:37:18 +01:00
armada_drm_plane_work_wait ( drm_to_armada_plane ( dcrtc - > crtc . primary ) ,
MAX_SCHEDULE_TIMEOUT ) ;
2012-08-15 13:59:49 +01:00
/* Take a reference to the new fb as we're using it */
2014-04-01 15:22:40 -07:00
drm_framebuffer_reference ( crtc - > primary - > fb ) ;
2012-08-15 13:59:49 +01:00
/* Update the base in the CRTC */
armada_drm_crtc_update_regs ( dcrtc , regs ) ;
/* Drop our previously held reference */
armada_drm_crtc_finish_fb ( dcrtc , old_fb , dpms_blanked ( dcrtc - > dpms ) ) ;
return 0 ;
}
2015-07-15 18:11:25 +01:00
void armada_drm_crtc_plane_disable ( struct armada_crtc * dcrtc ,
struct drm_plane * plane )
{
2015-07-15 18:11:25 +01:00
u32 sram_para1 , dma_ctrl0_mask ;
2015-07-15 18:11:25 +01:00
/*
* Drop our reference on any framebuffer attached to this plane .
* We don ' t need to NULL this out as drm_plane_force_disable ( ) ,
* and __setplane_internal ( ) will do so for an overlay plane , and
* __drm_helper_disable_unused_functions ( ) will do so for the
* primary plane .
*/
if ( plane - > fb )
drm_framebuffer_unreference ( plane - > fb ) ;
/* Power down the Y/U/V FIFOs */
sram_para1 = CFG_PDWN16x66 | CFG_PDWN32x66 ;
/* Power down most RAMs and FIFOs if this is the primary plane */
2015-07-15 18:11:25 +01:00
if ( plane - > type = = DRM_PLANE_TYPE_PRIMARY ) {
2015-07-15 18:11:25 +01:00
sram_para1 | = CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
CFG_PDWN32x32 | CFG_PDWN64x66 ;
2015-07-15 18:11:25 +01:00
dma_ctrl0_mask = CFG_GRA_ENA ;
} else {
dma_ctrl0_mask = CFG_DMA_ENA ;
}
spin_lock_irq ( & dcrtc - > irq_lock ) ;
armada_updatel ( 0 , dma_ctrl0_mask , dcrtc - > base + LCD_SPU_DMA_CTRL0 ) ;
spin_unlock_irq ( & dcrtc - > irq_lock ) ;
2015-07-15 18:11:25 +01:00
armada_updatel ( sram_para1 , 0 , dcrtc - > base + LCD_SPU_SRAM_PARA1 ) ;
}
2012-08-15 13:59:49 +01:00
/* The mode_config.mutex will be held for this call */
static void armada_drm_crtc_disable ( struct drm_crtc * crtc )
{
struct armada_crtc * dcrtc = drm_to_armada_crtc ( crtc ) ;
armada_drm_crtc_dpms ( crtc , DRM_MODE_DPMS_OFF ) ;
2015-07-15 18:11:25 +01:00
armada_drm_crtc_plane_disable ( dcrtc , crtc - > primary ) ;
2012-08-15 13:59:49 +01:00
}
static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = {
. dpms = armada_drm_crtc_dpms ,
. prepare = armada_drm_crtc_prepare ,
. commit = armada_drm_crtc_commit ,
. mode_fixup = armada_drm_crtc_mode_fixup ,
. mode_set = armada_drm_crtc_mode_set ,
. mode_set_base = armada_drm_crtc_mode_set_base ,
. disable = armada_drm_crtc_disable ,
} ;
2013-05-19 10:55:17 +01:00
static void armada_load_cursor_argb ( void __iomem * base , uint32_t * pix ,
unsigned stride , unsigned width , unsigned height )
{
uint32_t addr ;
unsigned y ;
addr = SRAM_HWC32_RAM1 ;
for ( y = 0 ; y < height ; y + + ) {
uint32_t * p = & pix [ y * stride ] ;
unsigned x ;
for ( x = 0 ; x < width ; x + + , p + + ) {
uint32_t val = * p ;
val = ( val & 0xff00ff00 ) |
( val & 0x000000ff ) < < 16 |
( val & 0x00ff0000 ) > > 16 ;
writel_relaxed ( val ,
base + LCD_SPU_SRAM_WRDAT ) ;
writel_relaxed ( addr | SRAM_WRITE ,
base + LCD_SPU_SRAM_CTRL ) ;
2014-04-07 12:00:17 +01:00
readl_relaxed ( base + LCD_SPU_HWC_OVSA_HPXL_VLN ) ;
2013-05-19 10:55:17 +01:00
addr + = 1 ;
if ( ( addr & 0x00ff ) = = 0 )
addr + = 0xf00 ;
if ( ( addr & 0x30ff ) = = 0 )
addr = SRAM_HWC32_RAM2 ;
}
}
}
static void armada_drm_crtc_cursor_tran ( void __iomem * base )
{
unsigned addr ;
for ( addr = 0 ; addr < 256 ; addr + + ) {
/* write the default value */
writel_relaxed ( 0x55555555 , base + LCD_SPU_SRAM_WRDAT ) ;
writel_relaxed ( addr | SRAM_WRITE | SRAM_HWC32_TRAN ,
base + LCD_SPU_SRAM_CTRL ) ;
}
}
static int armada_drm_crtc_cursor_update ( struct armada_crtc * dcrtc , bool reload )
{
uint32_t xoff , xscr , w = dcrtc - > cursor_w , s ;
uint32_t yoff , yscr , h = dcrtc - > cursor_h ;
uint32_t para1 ;
/*
* Calculate the visible width and height of the cursor ,
* screen position , and the position in the cursor bitmap .
*/
if ( dcrtc - > cursor_x < 0 ) {
xoff = - dcrtc - > cursor_x ;
xscr = 0 ;
w - = min ( xoff , w ) ;
} else if ( dcrtc - > cursor_x + w > dcrtc - > crtc . mode . hdisplay ) {
xoff = 0 ;
xscr = dcrtc - > cursor_x ;
w = max_t ( int , dcrtc - > crtc . mode . hdisplay - dcrtc - > cursor_x , 0 ) ;
} else {
xoff = 0 ;
xscr = dcrtc - > cursor_x ;
}
if ( dcrtc - > cursor_y < 0 ) {
yoff = - dcrtc - > cursor_y ;
yscr = 0 ;
h - = min ( yoff , h ) ;
} else if ( dcrtc - > cursor_y + h > dcrtc - > crtc . mode . vdisplay ) {
yoff = 0 ;
yscr = dcrtc - > cursor_y ;
h = max_t ( int , dcrtc - > crtc . mode . vdisplay - dcrtc - > cursor_y , 0 ) ;
} else {
yoff = 0 ;
yscr = dcrtc - > cursor_y ;
}
/* On interlaced modes, the vertical cursor size must be halved */
s = dcrtc - > cursor_w ;
if ( dcrtc - > interlaced ) {
s * = 2 ;
yscr / = 2 ;
h / = 2 ;
}
if ( ! dcrtc - > cursor_obj | | ! h | | ! w ) {
spin_lock_irq ( & dcrtc - > irq_lock ) ;
armada_drm_crtc_disable_irq ( dcrtc , DUMB_FRAMEDONE_ENA ) ;
dcrtc - > cursor_update = false ;
armada_updatel ( 0 , CFG_HWC_ENA , dcrtc - > base + LCD_SPU_DMA_CTRL0 ) ;
spin_unlock_irq ( & dcrtc - > irq_lock ) ;
return 0 ;
}
para1 = readl_relaxed ( dcrtc - > base + LCD_SPU_SRAM_PARA1 ) ;
armada_updatel ( CFG_CSB_256x32 , CFG_CSB_256x32 | CFG_PDWN256x32 ,
dcrtc - > base + LCD_SPU_SRAM_PARA1 ) ;
/*
* Initialize the transparency if the SRAM was powered down .
* We must also reload the cursor data as well .
*/
if ( ! ( para1 & CFG_CSB_256x32 ) ) {
armada_drm_crtc_cursor_tran ( dcrtc - > base ) ;
reload = true ;
}
if ( dcrtc - > cursor_hw_sz ! = ( h < < 16 | w ) ) {
spin_lock_irq ( & dcrtc - > irq_lock ) ;
armada_drm_crtc_disable_irq ( dcrtc , DUMB_FRAMEDONE_ENA ) ;
dcrtc - > cursor_update = false ;
armada_updatel ( 0 , CFG_HWC_ENA , dcrtc - > base + LCD_SPU_DMA_CTRL0 ) ;
spin_unlock_irq ( & dcrtc - > irq_lock ) ;
reload = true ;
}
if ( reload ) {
struct armada_gem_object * obj = dcrtc - > cursor_obj ;
uint32_t * pix ;
/* Set the top-left corner of the cursor image */
pix = obj - > addr ;
pix + = yoff * s + xoff ;
armada_load_cursor_argb ( dcrtc - > base , pix , s , w , h ) ;
}
/* Reload the cursor position, size and enable in the IRQ handler */
spin_lock_irq ( & dcrtc - > irq_lock ) ;
dcrtc - > cursor_hw_pos = yscr < < 16 | xscr ;
dcrtc - > cursor_hw_sz = h < < 16 | w ;
dcrtc - > cursor_update = true ;
armada_drm_crtc_enable_irq ( dcrtc , DUMB_FRAMEDONE_ENA ) ;
spin_unlock_irq ( & dcrtc - > irq_lock ) ;
return 0 ;
}
static void cursor_update ( void * data )
{
armada_drm_crtc_cursor_update ( data , true ) ;
}
static int armada_drm_crtc_cursor_set ( struct drm_crtc * crtc ,
struct drm_file * file , uint32_t handle , uint32_t w , uint32_t h )
{
struct armada_crtc * dcrtc = drm_to_armada_crtc ( crtc ) ;
struct armada_gem_object * obj = NULL ;
int ret ;
/* If no cursor support, replicate drm's return value */
2014-04-22 15:24:03 +01:00
if ( ! dcrtc - > variant - > has_spu_adv_reg )
2013-05-19 10:55:17 +01:00
return - ENXIO ;
if ( handle & & w > 0 & & h > 0 ) {
/* maximum size is 64x32 or 32x64 */
if ( w > 64 | | h > 64 | | ( w > 32 & & h > 32 ) )
return - ENOMEM ;
2016-05-09 11:04:54 +01:00
obj = armada_gem_object_lookup ( file , handle ) ;
2013-05-19 10:55:17 +01:00
if ( ! obj )
return - ENOENT ;
/* Must be a kernel-mapped object */
if ( ! obj - > addr ) {
drm_gem_object_unreference_unlocked ( & obj - > obj ) ;
return - EINVAL ;
}
if ( obj - > obj . size < w * h * 4 ) {
DRM_ERROR ( " buffer is too small \n " ) ;
drm_gem_object_unreference_unlocked ( & obj - > obj ) ;
return - ENOMEM ;
}
}
if ( dcrtc - > cursor_obj ) {
dcrtc - > cursor_obj - > update = NULL ;
dcrtc - > cursor_obj - > update_data = NULL ;
2015-11-23 10:32:45 +01:00
drm_gem_object_unreference_unlocked ( & dcrtc - > cursor_obj - > obj ) ;
2013-05-19 10:55:17 +01:00
}
dcrtc - > cursor_obj = obj ;
dcrtc - > cursor_w = w ;
dcrtc - > cursor_h = h ;
ret = armada_drm_crtc_cursor_update ( dcrtc , true ) ;
if ( obj ) {
obj - > update_data = dcrtc ;
obj - > update = cursor_update ;
}
return ret ;
}
static int armada_drm_crtc_cursor_move ( struct drm_crtc * crtc , int x , int y )
{
struct armada_crtc * dcrtc = drm_to_armada_crtc ( crtc ) ;
int ret ;
/* If no cursor support, replicate drm's return value */
2014-04-22 15:24:03 +01:00
if ( ! dcrtc - > variant - > has_spu_adv_reg )
2013-05-19 10:55:17 +01:00
return - EFAULT ;
dcrtc - > cursor_x = x ;
dcrtc - > cursor_y = y ;
ret = armada_drm_crtc_cursor_update ( dcrtc , false ) ;
return ret ;
}
2012-08-15 13:59:49 +01:00
static void armada_drm_crtc_destroy ( struct drm_crtc * crtc )
{
struct armada_crtc * dcrtc = drm_to_armada_crtc ( crtc ) ;
struct armada_private * priv = crtc - > dev - > dev_private ;
2013-05-19 10:55:17 +01:00
if ( dcrtc - > cursor_obj )
2015-11-23 10:32:34 +01:00
drm_gem_object_unreference_unlocked ( & dcrtc - > cursor_obj - > obj ) ;
2013-05-19 10:55:17 +01:00
2012-08-15 13:59:49 +01:00
priv - > dcrtc [ dcrtc - > num ] = NULL ;
drm_crtc_cleanup ( & dcrtc - > crtc ) ;
if ( ! IS_ERR ( dcrtc - > clk ) )
clk_disable_unprepare ( dcrtc - > clk ) ;
2014-04-26 15:19:38 +01:00
writel_relaxed ( 0 , dcrtc - > base + LCD_SPU_IRQ_ENA ) ;
2014-06-15 11:21:23 +01:00
of_node_put ( dcrtc - > crtc . port ) ;
2012-08-15 13:59:49 +01:00
kfree ( dcrtc ) ;
}
/*
* The mode_config lock is held here , to prevent races between this
* and a mode_set .
*/
static int armada_drm_crtc_page_flip ( struct drm_crtc * crtc ,
2013-10-22 09:38:18 +01:00
struct drm_framebuffer * fb , struct drm_pending_vblank_event * event , uint32_t page_flip_flags )
2012-08-15 13:59:49 +01:00
{
struct armada_crtc * dcrtc = drm_to_armada_crtc ( crtc ) ;
struct armada_frame_work * work ;
unsigned i ;
int ret ;
/* We don't support changing the pixel format */
2016-11-18 21:53:10 +02:00
if ( fb - > format ! = crtc - > primary - > fb - > format )
2012-08-15 13:59:49 +01:00
return - EINVAL ;
work = kmalloc ( sizeof ( * work ) , GFP_KERNEL ) ;
if ( ! work )
return - ENOMEM ;
2015-08-06 16:37:18 +01:00
work - > work . fn = armada_drm_crtc_complete_frame_work ;
2012-08-15 13:59:49 +01:00
work - > event = event ;
2014-04-01 15:22:40 -07:00
work - > old_fb = dcrtc - > crtc . primary - > fb ;
2012-08-15 13:59:49 +01:00
i = armada_drm_crtc_calc_fb ( fb , crtc - > x , crtc - > y , work - > regs ,
dcrtc - > interlaced ) ;
armada_reg_queue_end ( work - > regs , i ) ;
/*
2014-10-11 23:53:35 +01:00
* Ensure that we hold a reference on the new framebuffer .
* This has to match the behaviour in mode_set .
2012-08-15 13:59:49 +01:00
*/
2014-10-11 23:53:35 +01:00
drm_framebuffer_reference ( fb ) ;
2012-08-15 13:59:49 +01:00
ret = armada_drm_crtc_queue_frame_work ( dcrtc , work ) ;
if ( ret ) {
2014-10-11 23:53:35 +01:00
/* Undo our reference above */
drm_framebuffer_unreference ( fb ) ;
2012-08-15 13:59:49 +01:00
kfree ( work ) ;
return ret ;
}
/*
* Don ' t take a reference on the new framebuffer ;
* drm_mode_page_flip_ioctl ( ) has already grabbed a reference and
* will _not_ drop that reference on successful return from this
* function . Simply mark this new framebuffer as the current one .
*/
2014-04-01 15:22:40 -07:00
dcrtc - > crtc . primary - > fb = fb ;
2012-08-15 13:59:49 +01:00
/*
* Finally , if the display is blanked , we won ' t receive an
* interrupt , so complete it now .
*/
2015-08-06 16:37:18 +01:00
if ( dpms_blanked ( dcrtc - > dpms ) )
2016-07-25 15:16:11 +01:00
armada_drm_plane_work_run ( dcrtc , dcrtc - > crtc . primary ) ;
2012-08-15 13:59:49 +01:00
return 0 ;
}
static int
armada_drm_crtc_set_property ( struct drm_crtc * crtc ,
struct drm_property * property , uint64_t val )
{
struct armada_private * priv = crtc - > dev - > dev_private ;
struct armada_crtc * dcrtc = drm_to_armada_crtc ( crtc ) ;
bool update_csc = false ;
if ( property = = priv - > csc_yuv_prop ) {
dcrtc - > csc_yuv_mode = val ;
update_csc = true ;
} else if ( property = = priv - > csc_rgb_prop ) {
dcrtc - > csc_rgb_mode = val ;
update_csc = true ;
}
if ( update_csc ) {
uint32_t val ;
val = dcrtc - > spu_iopad_ctrl |
armada_drm_crtc_calculate_csc ( dcrtc ) ;
writel_relaxed ( val , dcrtc - > base + LCD_SPU_IOPAD_CONTROL ) ;
}
return 0 ;
}
2015-12-15 12:20:59 +01:00
static const struct drm_crtc_funcs armada_crtc_funcs = {
2013-05-19 10:55:17 +01:00
. cursor_set = armada_drm_crtc_cursor_set ,
. cursor_move = armada_drm_crtc_cursor_move ,
2012-08-15 13:59:49 +01:00
. destroy = armada_drm_crtc_destroy ,
. set_config = drm_crtc_helper_set_config ,
. page_flip = armada_drm_crtc_page_flip ,
. set_property = armada_drm_crtc_set_property ,
} ;
2015-07-15 18:11:24 +01:00
static const struct drm_plane_funcs armada_primary_plane_funcs = {
. update_plane = drm_primary_helper_update ,
. disable_plane = drm_primary_helper_disable ,
. destroy = drm_primary_helper_destroy ,
} ;
2015-07-15 18:11:25 +01:00
int armada_drm_plane_init ( struct armada_plane * plane )
{
init_waitqueue_head ( & plane - > frame_wait ) ;
return 0 ;
}
2012-08-15 13:59:49 +01:00
static struct drm_prop_enum_list armada_drm_csc_yuv_enum_list [ ] = {
{ CSC_AUTO , " Auto " } ,
{ CSC_YUV_CCIR601 , " CCIR601 " } ,
{ CSC_YUV_CCIR709 , " CCIR709 " } ,
} ;
static struct drm_prop_enum_list armada_drm_csc_rgb_enum_list [ ] = {
{ CSC_AUTO , " Auto " } ,
{ CSC_RGB_COMPUTER , " Computer system " } ,
{ CSC_RGB_STUDIO , " Studio " } ,
} ;
static int armada_drm_crtc_create_properties ( struct drm_device * dev )
{
struct armada_private * priv = dev - > dev_private ;
if ( priv - > csc_yuv_prop )
return 0 ;
priv - > csc_yuv_prop = drm_property_create_enum ( dev , 0 ,
" CSC_YUV " , armada_drm_csc_yuv_enum_list ,
ARRAY_SIZE ( armada_drm_csc_yuv_enum_list ) ) ;
priv - > csc_rgb_prop = drm_property_create_enum ( dev , 0 ,
" CSC_RGB " , armada_drm_csc_rgb_enum_list ,
ARRAY_SIZE ( armada_drm_csc_rgb_enum_list ) ) ;
if ( ! priv - > csc_yuv_prop | | ! priv - > csc_rgb_prop )
return - ENOMEM ;
return 0 ;
}
2015-06-06 21:46:53 +01:00
static int armada_drm_crtc_create ( struct drm_device * drm , struct device * dev ,
2014-06-15 11:21:23 +01:00
struct resource * res , int irq , const struct armada_variant * variant ,
struct device_node * port )
2012-08-15 13:59:49 +01:00
{
2014-04-22 11:10:15 +01:00
struct armada_private * priv = drm - > dev_private ;
2012-08-15 13:59:49 +01:00
struct armada_crtc * dcrtc ;
2015-07-15 18:11:24 +01:00
struct armada_plane * primary ;
2012-08-15 13:59:49 +01:00
void __iomem * base ;
int ret ;
2014-04-22 11:10:15 +01:00
ret = armada_drm_crtc_create_properties ( drm ) ;
2012-08-15 13:59:49 +01:00
if ( ret )
return ret ;
2014-08-07 17:36:12 -07:00
base = devm_ioremap_resource ( dev , res ) ;
2014-06-11 14:00:05 +09:00
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
2012-08-15 13:59:49 +01:00
dcrtc = kzalloc ( sizeof ( * dcrtc ) , GFP_KERNEL ) ;
if ( ! dcrtc ) {
DRM_ERROR ( " failed to allocate Armada crtc \n " ) ;
return - ENOMEM ;
}
2014-04-22 11:10:15 +01:00
if ( dev ! = drm - > dev )
dev_set_drvdata ( dev , dcrtc ) ;
2014-04-22 15:24:03 +01:00
dcrtc - > variant = variant ;
2012-08-15 13:59:49 +01:00
dcrtc - > base = base ;
2014-04-22 11:10:15 +01:00
dcrtc - > num = drm - > mode_config . num_crtc ;
2012-08-15 13:59:49 +01:00
dcrtc - > clk = ERR_PTR ( - EINVAL ) ;
dcrtc - > csc_yuv_mode = CSC_AUTO ;
dcrtc - > csc_rgb_mode = CSC_AUTO ;
dcrtc - > cfg_dumb_ctrl = DUMB24_RGB888_0 ;
dcrtc - > spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24 ;
spin_lock_init ( & dcrtc - > irq_lock ) ;
dcrtc - > irq_ena = CLEAN_SPU_IRQ_ISR ;
/* Initialize some registers which we don't otherwise set */
writel_relaxed ( 0x00000001 , dcrtc - > base + LCD_CFG_SCLK_DIV ) ;
writel_relaxed ( 0x00000000 , dcrtc - > base + LCD_SPU_BLANKCOLOR ) ;
writel_relaxed ( dcrtc - > spu_iopad_ctrl ,
dcrtc - > base + LCD_SPU_IOPAD_CONTROL ) ;
writel_relaxed ( 0x00000000 , dcrtc - > base + LCD_SPU_SRAM_PARA0 ) ;
writel_relaxed ( CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
CFG_PDWN64x66 , dcrtc - > base + LCD_SPU_SRAM_PARA1 ) ;
writel_relaxed ( 0x2032ff81 , dcrtc - > base + LCD_SPU_DMA_CTRL1 ) ;
2014-04-26 15:19:38 +01:00
writel_relaxed ( dcrtc - > irq_ena , dcrtc - > base + LCD_SPU_IRQ_ENA ) ;
writel_relaxed ( 0 , dcrtc - > base + LCD_SPU_IRQ_ISR ) ;
ret = devm_request_irq ( dev , irq , armada_drm_irq , 0 , " armada_drm_crtc " ,
dcrtc ) ;
if ( ret < 0 ) {
kfree ( dcrtc ) ;
return ret ;
}
2012-08-15 13:59:49 +01:00
2014-04-22 15:24:03 +01:00
if ( dcrtc - > variant - > init ) {
2014-04-22 11:10:15 +01:00
ret = dcrtc - > variant - > init ( dcrtc , dev ) ;
2012-08-15 13:59:49 +01:00
if ( ret ) {
kfree ( dcrtc ) ;
return ret ;
}
}
/* Ensure AXI pipeline is enabled */
armada_updatel ( CFG_ARBFAST_ENA , 0 , dcrtc - > base + LCD_SPU_DMA_CTRL0 ) ;
priv - > dcrtc [ dcrtc - > num ] = dcrtc ;
2014-06-15 11:21:23 +01:00
dcrtc - > crtc . port = port ;
2015-07-15 18:11:24 +01:00
2015-07-15 18:11:24 +01:00
primary = kzalloc ( sizeof ( * primary ) , GFP_KERNEL ) ;
2015-07-15 18:11:24 +01:00
if ( ! primary )
return - ENOMEM ;
2015-07-15 18:11:25 +01:00
ret = armada_drm_plane_init ( primary ) ;
if ( ret ) {
kfree ( primary ) ;
return ret ;
}
2015-07-15 18:11:24 +01:00
ret = drm_universal_plane_init ( drm , & primary - > base , 0 ,
& armada_primary_plane_funcs ,
armada_primary_formats ,
ARRAY_SIZE ( armada_primary_formats ) ,
drm: Pass 'name' to drm_universal_plane_init()
Done with coccinelle for the most part. It choked on
msm/mdp/mdp5/mdp5_plane.c like so:
"BAD:!!!!! enum drm_plane_type type;"
No idea how to deal with that, so I just fixed that up
by hand.
Also it thinks '...' is part of the semantic patch, so I put an
'int DOTDOTDOT' placeholder in its place and got rid of it with
sed afterwards.
I didn't convert drm_plane_init() since passing the varargs through
would mean either cpp macros or va_list, and I figured we don't
care about these legacy functions enough to warrant the extra pain.
@@
typedef uint32_t;
identifier dev, plane, possible_crtcs, funcs, formats, format_count, type;
@@
int drm_universal_plane_init(struct drm_device *dev,
struct drm_plane *plane,
unsigned long possible_crtcs,
const struct drm_plane_funcs *funcs,
const uint32_t *formats,
unsigned int format_count,
enum drm_plane_type type
+ ,const char *name, int DOTDOTDOT
)
{ ... }
@@
identifier dev, plane, possible_crtcs, funcs, formats, format_count, type;
@@
int drm_universal_plane_init(struct drm_device *dev,
struct drm_plane *plane,
unsigned long possible_crtcs,
const struct drm_plane_funcs *funcs,
const uint32_t *formats,
unsigned int format_count,
enum drm_plane_type type
+ ,const char *name, int DOTDOTDOT
);
@@
expression E1, E2, E3, E4, E5, E6, E7;
@@
drm_universal_plane_init(E1, E2, E3, E4, E5, E6, E7
+ ,NULL
)
v2: Split crtc and plane changes apart
Pass NUL for no-name instead of ""
Leave drm_plane_init() alone
v3: Add ', or NULL...' to @name kernel doc (Jani)
Annotate the function with __printf() attribute (Jani)
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/1449670795-2853-1-git-send-email-ville.syrjala@linux.intel.com
2015-12-09 16:19:55 +02:00
DRM_PLANE_TYPE_PRIMARY , NULL ) ;
2015-07-15 18:11:24 +01:00
if ( ret ) {
kfree ( primary ) ;
return ret ;
}
ret = drm_crtc_init_with_planes ( drm , & dcrtc - > crtc , & primary - > base , NULL ,
drm: Pass 'name' to drm_crtc_init_with_planes()
Done with coccinelle for the most part. However, it thinks '...' is
part of the semantic patch, so I put an 'int DOTDOTDOT' placeholder
in its place and got rid of it with sed afterwards.
I didn't convert drm_crtc_init() since passing the varargs through
would mean either cpp macros or va_list, and I figured we don't
care about these legacy functions enough to warrant the extra pain.
@@
identifier dev, crtc, primary, cursor, funcs;
@@
int drm_crtc_init_with_planes(struct drm_device *dev,
struct drm_crtc *crtc,
struct drm_plane *primary, struct drm_plane *cursor,
const struct drm_crtc_funcs *funcs
+ ,const char *name, int DOTDOTDOT
)
{ ... }
@@
identifier dev, crtc, primary, cursor, funcs;
@@
int drm_crtc_init_with_planes(struct drm_device *dev,
struct drm_crtc *crtc,
struct drm_plane *primary, struct drm_plane *cursor,
const struct drm_crtc_funcs *funcs
+ ,const char *name, int DOTDOTDOT
);
@@
expression E1, E2, E3, E4, E5;
@@
drm_crtc_init_with_planes(E1, E2, E3, E4, E5
+ ,NULL
)
v2: Split crtc and plane changes apart
Pass NULL for no-name instead of ""
Leave drm_crtc_init() alone
v3: Add ', or NULL...' to @name kernel doc (Jani)
Annotate the function with __printf() attribute (Jani)
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/1449670771-2751-1-git-send-email-ville.syrjala@linux.intel.com
2015-12-09 16:19:31 +02:00
& armada_crtc_funcs , NULL ) ;
2015-07-15 18:11:24 +01:00
if ( ret )
goto err_crtc_init ;
2012-08-15 13:59:49 +01:00
drm_crtc_helper_add ( & dcrtc - > crtc , & armada_crtc_helper_funcs ) ;
drm_object_attach_property ( & dcrtc - > crtc . base , priv - > csc_yuv_prop ,
dcrtc - > csc_yuv_mode ) ;
drm_object_attach_property ( & dcrtc - > crtc . base , priv - > csc_rgb_prop ,
dcrtc - > csc_rgb_mode ) ;
2014-04-22 11:10:15 +01:00
return armada_overlay_plane_create ( drm , 1 < < dcrtc - > num ) ;
2015-07-15 18:11:24 +01:00
err_crtc_init :
2015-07-15 18:11:24 +01:00
primary - > base . funcs - > destroy ( & primary - > base ) ;
2015-07-15 18:11:24 +01:00
return ret ;
2014-04-22 11:10:15 +01:00
}
static int
armada_lcd_bind ( struct device * dev , struct device * master , void * data )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct drm_device * drm = data ;
struct resource * res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
int irq = platform_get_irq ( pdev , 0 ) ;
const struct armada_variant * variant ;
2014-06-15 11:21:23 +01:00
struct device_node * port = NULL ;
2014-04-22 11:10:15 +01:00
if ( irq < 0 )
return irq ;
if ( ! dev - > of_node ) {
const struct platform_device_id * id ;
id = platform_get_device_id ( pdev ) ;
if ( ! id )
return - ENXIO ;
variant = ( const struct armada_variant * ) id - > driver_data ;
} else {
const struct of_device_id * match ;
2014-06-15 11:21:23 +01:00
struct device_node * np , * parent = dev - > of_node ;
2014-04-22 11:10:15 +01:00
match = of_match_device ( dev - > driver - > of_match_table , dev ) ;
if ( ! match )
return - ENXIO ;
2014-06-15 11:21:23 +01:00
np = of_get_child_by_name ( parent , " ports " ) ;
if ( np )
parent = np ;
port = of_get_child_by_name ( parent , " port " ) ;
of_node_put ( np ) ;
if ( ! port ) {
dev_err ( dev , " no port node found in %s \n " ,
parent - > full_name ) ;
return - ENXIO ;
}
2014-04-22 11:10:15 +01:00
variant = match - > data ;
}
2014-06-15 11:21:23 +01:00
return armada_drm_crtc_create ( drm , dev , res , irq , variant , port ) ;
2014-04-22 11:10:15 +01:00
}
static void
armada_lcd_unbind ( struct device * dev , struct device * master , void * data )
{
struct armada_crtc * dcrtc = dev_get_drvdata ( dev ) ;
armada_drm_crtc_destroy ( & dcrtc - > crtc ) ;
2012-08-15 13:59:49 +01:00
}
2014-04-22 11:10:15 +01:00
static const struct component_ops armada_lcd_ops = {
. bind = armada_lcd_bind ,
. unbind = armada_lcd_unbind ,
} ;
static int armada_lcd_probe ( struct platform_device * pdev )
{
return component_add ( & pdev - > dev , & armada_lcd_ops ) ;
}
static int armada_lcd_remove ( struct platform_device * pdev )
{
component_del ( & pdev - > dev , & armada_lcd_ops ) ;
return 0 ;
2012-08-15 13:59:49 +01:00
}
2014-04-22 11:10:15 +01:00
static struct of_device_id armada_lcd_of_match [ ] = {
{
. compatible = " marvell,dove-lcd " ,
. data = & armada510_ops ,
} ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , armada_lcd_of_match ) ;
static const struct platform_device_id armada_lcd_platform_ids [ ] = {
{
. name = " armada-lcd " ,
. driver_data = ( unsigned long ) & armada510_ops ,
} , {
. name = " armada-510-lcd " ,
. driver_data = ( unsigned long ) & armada510_ops ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( platform , armada_lcd_platform_ids ) ;
struct platform_driver armada_lcd_platform_driver = {
. probe = armada_lcd_probe ,
. remove = armada_lcd_remove ,
. driver = {
. name = " armada-lcd " ,
. owner = THIS_MODULE ,
. of_match_table = armada_lcd_of_match ,
} ,
. id_table = armada_lcd_platform_ids ,
} ;