2013-01-08 15:04:28 -06:00
/*
* Copyright ( C ) 2012 Texas Instruments
* Author : Rob Clark < robdclark @ gmail . com >
*
* 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 .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2016-09-21 06:14:53 -07:00
# include <drm/drm_atomic.h>
2016-04-07 15:05:16 +03:00
# include <drm/drm_atomic_helper.h>
2016-09-21 06:14:53 -07:00
# include <drm/drm_crtc.h>
# include <drm/drm_flip_work.h>
# include <drm/drm_plane_helper.h>
2016-09-06 22:55:33 +03:00
# include <linux/workqueue.h>
2013-01-08 15:04:28 -06:00
# include "tilcdc_drv.h"
# include "tilcdc_regs.h"
2015-11-03 12:00:51 +02:00
# define TILCDC_VBLANK_SAFETY_THRESHOLD_US 1000
2013-01-08 15:04:28 -06:00
struct tilcdc_crtc {
struct drm_crtc base ;
2016-04-07 15:04:18 +03:00
struct drm_plane primary ;
2013-01-08 15:04:28 -06:00
const struct tilcdc_panel_info * info ;
struct drm_pending_vblank_event * event ;
2016-10-25 12:27:31 +03:00
struct mutex enable_lock ;
2016-06-22 16:27:54 +03:00
bool enabled ;
2016-10-25 12:27:31 +03:00
bool shutdown ;
2013-01-08 15:04:28 -06:00
wait_queue_head_t frame_done_wq ;
bool frame_done ;
2015-11-03 12:00:51 +02:00
spinlock_t irq_lock ;
2016-09-06 16:19:54 +03:00
unsigned int lcd_fck_rate ;
2015-11-03 12:00:51 +02:00
ktime_t last_vblank ;
2013-01-08 15:04:28 -06:00
2015-10-20 09:37:27 +03:00
struct drm_framebuffer * curr_fb ;
2015-11-03 12:00:51 +02:00
struct drm_framebuffer * next_fb ;
2013-01-08 15:04:28 -06:00
/* for deferred fb unref's: */
2013-08-07 13:41:20 -04:00
struct drm_flip_work unref_work ;
2015-02-10 14:13:23 +02:00
/* Only set if an external encoder is connected */
bool simulate_vesa_sync ;
2016-01-08 14:33:09 +02:00
int sync_lost_count ;
bool frame_intact ;
2016-04-06 14:02:38 +03:00
struct work_struct recover_work ;
2013-01-08 15:04:28 -06:00
} ;
# define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base)
2013-08-07 13:41:20 -04:00
static void unref_worker ( struct drm_flip_work * work , void * val )
2013-01-08 15:04:28 -06:00
{
2013-06-21 13:52:26 -05:00
struct tilcdc_crtc * tilcdc_crtc =
2013-08-07 13:41:20 -04:00
container_of ( work , struct tilcdc_crtc , unref_work ) ;
2013-01-08 15:04:28 -06:00
struct drm_device * dev = tilcdc_crtc - > base . dev ;
mutex_lock ( & dev - > mode_config . mutex ) ;
2013-08-07 13:41:20 -04:00
drm_framebuffer_unreference ( val ) ;
2013-01-08 15:04:28 -06:00
mutex_unlock ( & dev - > mode_config . mutex ) ;
}
2015-10-20 09:37:27 +03:00
static void set_scanout ( struct drm_crtc * crtc , struct drm_framebuffer * fb )
2013-01-08 15:04:28 -06:00
{
struct tilcdc_crtc * tilcdc_crtc = to_tilcdc_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
2016-10-28 13:52:41 +02:00
struct tilcdc_drm_private * priv = dev - > dev_private ;
2013-01-08 15:04:28 -06:00
struct drm_gem_cma_object * gem ;
2015-10-20 09:37:27 +03:00
dma_addr_t start , end ;
2016-08-26 15:10:14 +03:00
u64 dma_base_and_ceiling ;
2013-01-08 15:04:28 -06:00
gem = drm_fb_cma_get_gem_obj ( fb , 0 ) ;
2015-10-20 09:37:27 +03:00
start = gem - > paddr + fb - > offsets [ 0 ] +
crtc - > y * fb - > pitches [ 0 ] +
2016-10-18 01:41:14 +03:00
crtc - > x * drm_format_plane_cpp ( fb - > pixel_format , 0 ) ;
2013-01-08 15:04:28 -06:00
2015-10-20 09:37:27 +03:00
end = start + ( crtc - > mode . vdisplay * fb - > pitches [ 0 ] ) ;
2013-01-08 15:04:28 -06:00
2016-08-26 15:10:14 +03:00
/* Write LCDC_DMA_FB_BASE_ADDR_0_REG and LCDC_DMA_FB_CEILING_ADDR_0_REG
* with a single insruction , if available . This should make it more
* unlikely that LCDC would fetch the DMA addresses in the middle of
* an update .
*/
2016-10-28 13:52:41 +02:00
if ( priv - > rev = = 1 )
end - = 1 ;
dma_base_and_ceiling = ( u64 ) end < < 32 | start ;
2016-08-26 15:10:14 +03:00
tilcdc_write64 ( dev , LCDC_DMA_FB_BASE_ADDR_0_REG , dma_base_and_ceiling ) ;
2015-10-20 09:37:27 +03:00
if ( tilcdc_crtc - > curr_fb )
drm_flip_work_queue ( & tilcdc_crtc - > unref_work ,
tilcdc_crtc - > curr_fb ) ;
tilcdc_crtc - > curr_fb = fb ;
2013-01-08 15:04:28 -06:00
}
2016-06-21 16:00:44 +03:00
static void tilcdc_crtc_enable_irqs ( struct drm_device * dev )
{
struct tilcdc_drm_private * priv = dev - > dev_private ;
tilcdc_clear_irqstatus ( dev , 0xffffffff ) ;
if ( priv - > rev = = 1 ) {
tilcdc_set ( dev , LCDC_RASTER_CTRL_REG ,
LCDC_V1_UNDERFLOW_INT_ENA ) ;
2016-08-23 12:57:00 +00:00
tilcdc_set ( dev , LCDC_DMA_CTRL_REG ,
LCDC_V1_END_OF_FRAME_INT_ENA ) ;
2016-06-21 16:00:44 +03:00
} else {
tilcdc_write ( dev , LCDC_INT_ENABLE_SET_REG ,
LCDC_V2_UNDERFLOW_INT_ENA |
LCDC_V2_END_OF_FRAME0_INT_ENA |
LCDC_FRAME_DONE | LCDC_SYNC_LOST ) ;
}
}
static void tilcdc_crtc_disable_irqs ( struct drm_device * dev )
{
struct tilcdc_drm_private * priv = dev - > dev_private ;
/* disable irqs that we might have enabled: */
if ( priv - > rev = = 1 ) {
tilcdc_clear ( dev , LCDC_RASTER_CTRL_REG ,
LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA ) ;
tilcdc_clear ( dev , LCDC_DMA_CTRL_REG ,
LCDC_V1_END_OF_FRAME_INT_ENA ) ;
} else {
tilcdc_write ( dev , LCDC_INT_ENABLE_CLR_REG ,
LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA |
LCDC_V2_END_OF_FRAME0_INT_ENA |
LCDC_FRAME_DONE | LCDC_SYNC_LOST ) ;
}
}
2015-10-20 09:37:27 +03:00
static void reset ( struct drm_crtc * crtc )
2013-01-08 15:04:28 -06:00
{
struct drm_device * dev = crtc - > dev ;
struct tilcdc_drm_private * priv = dev - > dev_private ;
2015-10-20 09:37:27 +03:00
if ( priv - > rev ! = 2 )
return ;
tilcdc_set ( dev , LCDC_CLK_RESET_REG , LCDC_CLK_MAIN_RESET ) ;
usleep_range ( 250 , 1000 ) ;
tilcdc_clear ( dev , LCDC_CLK_RESET_REG , LCDC_CLK_MAIN_RESET ) ;
}
2016-06-22 16:27:54 +03:00
static void tilcdc_crtc_enable ( struct drm_crtc * crtc )
2015-10-20 09:37:27 +03:00
{
struct drm_device * dev = crtc - > dev ;
2016-06-22 16:27:54 +03:00
struct tilcdc_crtc * tilcdc_crtc = to_tilcdc_crtc ( crtc ) ;
2016-09-06 17:25:08 +03:00
WARN_ON ( ! drm_modeset_is_locked ( & crtc - > mutex ) ) ;
2016-10-25 12:27:31 +03:00
mutex_lock ( & tilcdc_crtc - > enable_lock ) ;
if ( tilcdc_crtc - > enabled | | tilcdc_crtc - > shutdown ) {
mutex_unlock ( & tilcdc_crtc - > enable_lock ) ;
2016-06-22 16:27:54 +03:00
return ;
2016-10-25 12:27:31 +03:00
}
2016-06-22 16:27:54 +03:00
pm_runtime_get_sync ( dev - > dev ) ;
2015-10-20 09:37:27 +03:00
reset ( crtc ) ;
2013-01-08 15:04:28 -06:00
2016-06-21 16:00:44 +03:00
tilcdc_crtc_enable_irqs ( dev ) ;
2015-10-20 09:37:27 +03:00
tilcdc_clear ( dev , LCDC_DMA_CTRL_REG , LCDC_DUAL_FRAME_BUFFER_ENABLE ) ;
2013-01-08 15:04:28 -06:00
tilcdc_set ( dev , LCDC_RASTER_CTRL_REG , LCDC_PALETTE_LOAD_MODE ( DATA_ONLY ) ) ;
tilcdc_set ( dev , LCDC_RASTER_CTRL_REG , LCDC_RASTER_ENABLE ) ;
2016-06-15 11:16:23 +03:00
drm_crtc_vblank_on ( crtc ) ;
2016-06-22 16:27:54 +03:00
tilcdc_crtc - > enabled = true ;
2016-10-25 12:27:31 +03:00
mutex_unlock ( & tilcdc_crtc - > enable_lock ) ;
2013-01-08 15:04:28 -06:00
}
2016-10-25 12:27:31 +03:00
static void tilcdc_crtc_off ( struct drm_crtc * crtc , bool shutdown )
2013-01-08 15:04:28 -06:00
{
2016-04-07 20:20:23 +03:00
struct tilcdc_crtc * tilcdc_crtc = to_tilcdc_crtc ( crtc ) ;
2013-01-08 15:04:28 -06:00
struct drm_device * dev = crtc - > dev ;
2016-04-07 20:20:23 +03:00
struct tilcdc_drm_private * priv = dev - > dev_private ;
2013-01-08 15:04:28 -06:00
2016-10-25 12:27:31 +03:00
mutex_lock ( & tilcdc_crtc - > enable_lock ) ;
if ( shutdown )
tilcdc_crtc - > shutdown = true ;
if ( ! tilcdc_crtc - > enabled ) {
mutex_unlock ( & tilcdc_crtc - > enable_lock ) ;
2016-06-22 16:27:54 +03:00
return ;
2016-10-25 12:27:31 +03:00
}
2016-04-07 20:20:23 +03:00
tilcdc_crtc - > frame_done = false ;
2013-01-08 15:04:28 -06:00
tilcdc_clear ( dev , LCDC_RASTER_CTRL_REG , LCDC_RASTER_ENABLE ) ;
2016-04-07 20:20:23 +03:00
/*
* if necessary wait for framedone irq which will still come
* before putting things to sleep . .
*/
if ( priv - > rev = = 2 ) {
int ret = wait_event_timeout ( tilcdc_crtc - > frame_done_wq ,
tilcdc_crtc - > frame_done ,
2016-06-16 16:19:17 +03:00
msecs_to_jiffies ( 500 ) ) ;
2016-04-07 20:20:23 +03:00
if ( ret = = 0 )
dev_err ( dev - > dev , " %s: timeout waiting for framedone \n " ,
__func__ ) ;
}
2016-06-15 11:16:23 +03:00
drm_crtc_vblank_off ( crtc ) ;
2016-06-21 16:00:44 +03:00
tilcdc_crtc_disable_irqs ( dev ) ;
2016-06-22 16:27:54 +03:00
pm_runtime_put_sync ( dev - > dev ) ;
if ( tilcdc_crtc - > next_fb ) {
drm_flip_work_queue ( & tilcdc_crtc - > unref_work ,
tilcdc_crtc - > next_fb ) ;
tilcdc_crtc - > next_fb = NULL ;
}
if ( tilcdc_crtc - > curr_fb ) {
drm_flip_work_queue ( & tilcdc_crtc - > unref_work ,
tilcdc_crtc - > curr_fb ) ;
tilcdc_crtc - > curr_fb = NULL ;
}
drm_flip_work_commit ( & tilcdc_crtc - > unref_work , priv - > wq ) ;
tilcdc_crtc - > last_vblank = ktime_set ( 0 , 0 ) ;
tilcdc_crtc - > enabled = false ;
2016-10-25 12:27:31 +03:00
mutex_unlock ( & tilcdc_crtc - > enable_lock ) ;
2016-06-22 16:27:54 +03:00
}
2016-10-18 23:23:27 +03:00
static void tilcdc_crtc_disable ( struct drm_crtc * crtc )
{
WARN_ON ( ! drm_modeset_is_locked ( & crtc - > mutex ) ) ;
2016-10-25 12:27:31 +03:00
tilcdc_crtc_off ( crtc , false ) ;
}
void tilcdc_crtc_shutdown ( struct drm_crtc * crtc )
{
tilcdc_crtc_off ( crtc , true ) ;
2016-10-18 23:23:27 +03:00
}
2016-06-22 16:27:54 +03:00
static bool tilcdc_crtc_is_on ( struct drm_crtc * crtc )
{
return crtc - > state & & crtc - > state - > enable & & crtc - > state - > active ;
2013-01-08 15:04:28 -06:00
}
2016-04-06 14:02:38 +03:00
static void tilcdc_crtc_recover_work ( struct work_struct * work )
{
struct tilcdc_crtc * tilcdc_crtc =
container_of ( work , struct tilcdc_crtc , recover_work ) ;
struct drm_crtc * crtc = & tilcdc_crtc - > base ;
dev_info ( crtc - > dev - > dev , " %s: Reset CRTC " , __func__ ) ;
drm_modeset_lock_crtc ( crtc , NULL ) ;
if ( ! tilcdc_crtc_is_on ( crtc ) )
goto out ;
tilcdc_crtc_disable ( crtc ) ;
tilcdc_crtc_enable ( crtc ) ;
out :
drm_modeset_unlock_crtc ( crtc ) ;
}
2013-01-08 15:04:28 -06:00
static void tilcdc_crtc_destroy ( struct drm_crtc * crtc )
{
struct tilcdc_crtc * tilcdc_crtc = to_tilcdc_crtc ( crtc ) ;
2016-09-06 22:55:33 +03:00
struct tilcdc_drm_private * priv = crtc - > dev - > dev_private ;
2013-01-08 15:04:28 -06:00
2016-09-07 11:46:40 +03:00
drm_modeset_lock_crtc ( crtc , NULL ) ;
2016-06-22 16:27:54 +03:00
tilcdc_crtc_disable ( crtc ) ;
2016-09-07 11:46:40 +03:00
drm_modeset_unlock_crtc ( crtc ) ;
2013-01-08 15:04:28 -06:00
2016-09-06 22:55:33 +03:00
flush_workqueue ( priv - > wq ) ;
2013-01-08 15:04:28 -06:00
2015-05-27 11:58:37 +03:00
of_node_put ( crtc - > port ) ;
2013-01-08 15:04:28 -06:00
drm_crtc_cleanup ( crtc ) ;
2013-08-07 13:41:20 -04:00
drm_flip_work_cleanup ( & tilcdc_crtc - > unref_work ) ;
2013-01-08 15:04:28 -06:00
}
2016-06-22 17:21:06 +03:00
int tilcdc_crtc_update_fb ( struct drm_crtc * crtc ,
2013-01-08 15:04:28 -06:00
struct drm_framebuffer * fb ,
2016-06-22 17:21:06 +03:00
struct drm_pending_vblank_event * event )
2013-01-08 15:04:28 -06:00
{
struct tilcdc_crtc * tilcdc_crtc = to_tilcdc_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
2015-10-20 09:37:27 +03:00
unsigned long flags ;
2014-02-07 17:37:07 +00:00
2016-09-06 17:25:08 +03:00
WARN_ON ( ! drm_modeset_is_locked ( & crtc - > mutex ) ) ;
2013-01-08 15:04:28 -06:00
if ( tilcdc_crtc - > event ) {
dev_err ( dev - > dev , " already pending page flip! \n " ) ;
return - EBUSY ;
}
2015-10-20 09:37:27 +03:00
drm_framebuffer_reference ( fb ) ;
2014-04-01 15:22:40 -07:00
crtc - > primary - > fb = fb ;
2015-10-19 12:30:03 +03:00
2015-11-03 12:00:51 +02:00
spin_lock_irqsave ( & tilcdc_crtc - > irq_lock , flags ) ;
2016-06-13 09:53:36 +03:00
if ( crtc - > hwmode . vrefresh & & ktime_to_ns ( tilcdc_crtc - > last_vblank ) ) {
ktime_t next_vblank ;
s64 tdiff ;
2015-10-20 09:37:27 +03:00
2016-06-13 09:53:36 +03:00
next_vblank = ktime_add_us ( tilcdc_crtc - > last_vblank ,
1000000 / crtc - > hwmode . vrefresh ) ;
2015-11-03 12:00:51 +02:00
2016-06-13 09:53:36 +03:00
tdiff = ktime_to_us ( ktime_sub ( next_vblank , ktime_get ( ) ) ) ;
if ( tdiff < TILCDC_VBLANK_SAFETY_THRESHOLD_US )
tilcdc_crtc - > next_fb = fb ;
}
if ( tilcdc_crtc - > next_fb ! = fb )
2015-11-03 12:00:51 +02:00
set_scanout ( crtc , fb ) ;
2015-10-20 09:37:27 +03:00
tilcdc_crtc - > event = event ;
2015-11-03 12:00:51 +02:00
spin_unlock_irqrestore ( & tilcdc_crtc - > irq_lock , flags ) ;
2013-01-08 15:04:28 -06:00
return 0 ;
}
static bool tilcdc_crtc_mode_fixup ( struct drm_crtc * crtc ,
const struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
2015-02-10 14:13:23 +02:00
struct tilcdc_crtc * tilcdc_crtc = to_tilcdc_crtc ( crtc ) ;
if ( ! tilcdc_crtc - > simulate_vesa_sync )
return true ;
/*
* tilcdc does not generate VESA - compliant sync but aligns
* VS on the second edge of HS instead of first edge .
* We use adjusted_mode , to fixup sync by aligning both rising
* edges and add HSKEW offset to fix the sync .
*/
adjusted_mode - > hskew = mode - > hsync_end - mode - > hsync_start ;
adjusted_mode - > flags | = DRM_MODE_FLAG_HSKEW ;
if ( mode - > flags & DRM_MODE_FLAG_NHSYNC ) {
adjusted_mode - > flags | = DRM_MODE_FLAG_PHSYNC ;
adjusted_mode - > flags & = ~ DRM_MODE_FLAG_NHSYNC ;
} else {
adjusted_mode - > flags | = DRM_MODE_FLAG_NHSYNC ;
adjusted_mode - > flags & = ~ DRM_MODE_FLAG_PHSYNC ;
}
2013-01-08 15:04:28 -06:00
return true ;
}
2016-09-29 18:43:57 +02:00
/*
* Calculate the percentage difference between the requested pixel clock rate
* and the effective rate resulting from calculating the clock divider value .
*/
static unsigned int tilcdc_pclk_diff ( unsigned long rate ,
unsigned long real_rate )
{
int r = rate / 100 , rr = real_rate / 100 ;
return ( unsigned int ) ( abs ( ( ( rr - r ) * 100 ) / r ) ) ;
}
2016-09-06 16:19:54 +03:00
static void tilcdc_crtc_set_clk ( struct drm_crtc * crtc )
{
struct drm_device * dev = crtc - > dev ;
struct tilcdc_drm_private * priv = dev - > dev_private ;
struct tilcdc_crtc * tilcdc_crtc = to_tilcdc_crtc ( crtc ) ;
2016-09-29 18:43:57 +02:00
unsigned long clk_rate , real_rate , req_rate ;
unsigned int clkdiv ;
2016-09-06 16:19:54 +03:00
int ret ;
2016-09-29 18:43:57 +02:00
clkdiv = 2 ; /* first try using a standard divider of 2 */
2016-09-06 16:19:54 +03:00
/* mode.clock is in KHz, set_rate wants parameter in Hz */
2016-09-29 18:43:57 +02:00
req_rate = crtc - > mode . clock * 1000 ;
ret = clk_set_rate ( priv - > clk , req_rate * clkdiv ) ;
clk_rate = clk_get_rate ( priv - > clk ) ;
2016-09-06 16:19:54 +03:00
if ( ret < 0 ) {
2016-09-29 18:43:57 +02:00
/*
* If we fail to set the clock rate ( some architectures don ' t
* use the common clock framework yet and may not implement
* all the clk API calls for every clock ) , try the next best
* thing : adjusting the clock divider , unless clk_get_rate ( )
* failed as well .
*/
if ( ! clk_rate ) {
/* Nothing more we can do. Just bail out. */
dev_err ( dev - > dev ,
" failed to set the pixel clock - unable to read current lcdc clock rate \n " ) ;
return ;
}
clkdiv = DIV_ROUND_CLOSEST ( clk_rate , req_rate ) ;
/*
* Emit a warning if the real clock rate resulting from the
* calculated divider differs much from the requested rate .
*
* 5 % is an arbitrary value - LCDs are usually quite tolerant
* about pixel clock rates .
*/
real_rate = clkdiv * req_rate ;
if ( tilcdc_pclk_diff ( clk_rate , real_rate ) > 5 ) {
dev_warn ( dev - > dev ,
" effective pixel clock rate (%luHz) differs from the calculated rate (%luHz) \n " ,
clk_rate , real_rate ) ;
}
2016-09-06 16:19:54 +03:00
}
2016-09-29 18:43:57 +02:00
tilcdc_crtc - > lcd_fck_rate = clk_rate ;
2016-09-06 16:19:54 +03:00
DBG ( " lcd_clk=%u, mode clock=%d, div=%u " ,
tilcdc_crtc - > lcd_fck_rate , crtc - > mode . clock , clkdiv ) ;
/* Configure the LCD clock divisor. */
tilcdc_write ( dev , LCDC_CTRL_REG , LCDC_CLK_DIVISOR ( clkdiv ) |
LCDC_RASTER_MODE ) ;
if ( priv - > rev = = 2 )
tilcdc_set ( dev , LCDC_CLK_ENABLE_REG ,
LCDC_V2_DMA_CLK_EN | LCDC_V2_LIDD_CLK_EN |
LCDC_V2_CORE_CLK_EN ) ;
}
2016-04-07 15:09:50 +03:00
static void tilcdc_crtc_mode_set_nofb ( struct drm_crtc * crtc )
{
struct tilcdc_crtc * tilcdc_crtc = to_tilcdc_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
struct tilcdc_drm_private * priv = dev - > dev_private ;
const struct tilcdc_panel_info * info = tilcdc_crtc - > info ;
uint32_t reg , hbp , hfp , hsw , vbp , vfp , vsw ;
struct drm_display_mode * mode = & crtc - > state - > adjusted_mode ;
struct drm_framebuffer * fb = crtc - > primary - > state - > fb ;
2016-09-06 17:25:08 +03:00
WARN_ON ( ! drm_modeset_is_locked ( & crtc - > mutex ) ) ;
2016-04-07 15:09:50 +03:00
if ( WARN_ON ( ! info ) )
return ;
if ( WARN_ON ( ! fb ) )
return ;
/* Configure the Burst Size and fifo threshold of DMA: */
reg = tilcdc_read ( dev , LCDC_DMA_CTRL_REG ) & ~ 0x00000770 ;
switch ( info - > dma_burst_sz ) {
case 1 :
reg | = LCDC_DMA_BURST_SIZE ( LCDC_DMA_BURST_1 ) ;
break ;
case 2 :
reg | = LCDC_DMA_BURST_SIZE ( LCDC_DMA_BURST_2 ) ;
break ;
case 4 :
reg | = LCDC_DMA_BURST_SIZE ( LCDC_DMA_BURST_4 ) ;
break ;
case 8 :
reg | = LCDC_DMA_BURST_SIZE ( LCDC_DMA_BURST_8 ) ;
break ;
case 16 :
reg | = LCDC_DMA_BURST_SIZE ( LCDC_DMA_BURST_16 ) ;
break ;
default :
dev_err ( dev - > dev , " invalid burst size \n " ) ;
return ;
}
reg | = ( info - > fifo_th < < 8 ) ;
tilcdc_write ( dev , LCDC_DMA_CTRL_REG , reg ) ;
/* Configure timings: */
hbp = mode - > htotal - mode - > hsync_end ;
hfp = mode - > hsync_start - mode - > hdisplay ;
hsw = mode - > hsync_end - mode - > hsync_start ;
vbp = mode - > vtotal - mode - > vsync_end ;
vfp = mode - > vsync_start - mode - > vdisplay ;
vsw = mode - > vsync_end - mode - > vsync_start ;
DBG ( " %dx%d, hbp=%u, hfp=%u, hsw=%u, vbp=%u, vfp=%u, vsw=%u " ,
mode - > hdisplay , mode - > vdisplay , hbp , hfp , hsw , vbp , vfp , vsw ) ;
/* Set AC Bias Period and Number of Transitions per Interrupt: */
reg = tilcdc_read ( dev , LCDC_RASTER_TIMING_2_REG ) & ~ 0x000fff00 ;
reg | = LCDC_AC_BIAS_FREQUENCY ( info - > ac_bias ) |
LCDC_AC_BIAS_TRANSITIONS_PER_INT ( info - > ac_bias_intrpt ) ;
/*
* subtract one from hfp , hbp , hsw because the hardware uses
* a value of 0 as 1
*/
if ( priv - > rev = = 2 ) {
/* clear bits we're going to set */
reg & = ~ 0x78000033 ;
reg | = ( ( hfp - 1 ) & 0x300 ) > > 8 ;
reg | = ( ( hbp - 1 ) & 0x300 ) > > 4 ;
reg | = ( ( hsw - 1 ) & 0x3c0 ) < < 21 ;
}
tilcdc_write ( dev , LCDC_RASTER_TIMING_2_REG , reg ) ;
reg = ( ( ( mode - > hdisplay > > 4 ) - 1 ) < < 4 ) |
( ( ( hbp - 1 ) & 0xff ) < < 24 ) |
( ( ( hfp - 1 ) & 0xff ) < < 16 ) |
( ( ( hsw - 1 ) & 0x3f ) < < 10 ) ;
if ( priv - > rev = = 2 )
reg | = ( ( ( mode - > hdisplay > > 4 ) - 1 ) & 0x40 ) > > 3 ;
tilcdc_write ( dev , LCDC_RASTER_TIMING_0_REG , reg ) ;
reg = ( ( mode - > vdisplay - 1 ) & 0x3ff ) |
( ( vbp & 0xff ) < < 24 ) |
( ( vfp & 0xff ) < < 16 ) |
( ( ( vsw - 1 ) & 0x3f ) < < 10 ) ;
tilcdc_write ( dev , LCDC_RASTER_TIMING_1_REG , reg ) ;
/*
* be sure to set Bit 10 for the V2 LCDC controller ,
* otherwise limited to 1024 pixels width , stopping
* 1920 x1080 being supported .
*/
if ( priv - > rev = = 2 ) {
if ( ( mode - > vdisplay - 1 ) & 0x400 ) {
tilcdc_set ( dev , LCDC_RASTER_TIMING_2_REG ,
LCDC_LPP_B10 ) ;
} else {
tilcdc_clear ( dev , LCDC_RASTER_TIMING_2_REG ,
LCDC_LPP_B10 ) ;
}
}
/* Configure display type: */
reg = tilcdc_read ( dev , LCDC_RASTER_CTRL_REG ) &
~ ( LCDC_TFT_MODE | LCDC_MONO_8BIT_MODE | LCDC_MONOCHROME_MODE |
LCDC_V2_TFT_24BPP_MODE | LCDC_V2_TFT_24BPP_UNPACK |
0x000ff000 /* Palette Loading Delay bits */ ) ;
reg | = LCDC_TFT_MODE ; /* no monochrome/passive support */
if ( info - > tft_alt_mode )
reg | = LCDC_TFT_ALT_ENABLE ;
if ( priv - > rev = = 2 ) {
2016-10-18 01:41:14 +03:00
switch ( fb - > pixel_format ) {
case DRM_FORMAT_BGR565 :
case DRM_FORMAT_RGB565 :
2016-04-07 15:09:50 +03:00
break ;
2016-10-18 01:41:14 +03:00
case DRM_FORMAT_XBGR8888 :
case DRM_FORMAT_XRGB8888 :
2016-04-07 15:09:50 +03:00
reg | = LCDC_V2_TFT_24BPP_UNPACK ;
/* fallthrough */
2016-10-18 01:41:14 +03:00
case DRM_FORMAT_BGR888 :
case DRM_FORMAT_RGB888 :
2016-04-07 15:09:50 +03:00
reg | = LCDC_V2_TFT_24BPP_MODE ;
break ;
default :
dev_err ( dev - > dev , " invalid pixel format \n " ) ;
return ;
}
}
reg | = info - > fdd < 12 ;
tilcdc_write ( dev , LCDC_RASTER_CTRL_REG , reg ) ;
if ( info - > invert_pxl_clk )
tilcdc_set ( dev , LCDC_RASTER_TIMING_2_REG , LCDC_INVERT_PIXEL_CLOCK ) ;
else
tilcdc_clear ( dev , LCDC_RASTER_TIMING_2_REG , LCDC_INVERT_PIXEL_CLOCK ) ;
if ( info - > sync_ctrl )
tilcdc_set ( dev , LCDC_RASTER_TIMING_2_REG , LCDC_SYNC_CTRL ) ;
else
tilcdc_clear ( dev , LCDC_RASTER_TIMING_2_REG , LCDC_SYNC_CTRL ) ;
if ( info - > sync_edge )
tilcdc_set ( dev , LCDC_RASTER_TIMING_2_REG , LCDC_SYNC_EDGE ) ;
else
tilcdc_clear ( dev , LCDC_RASTER_TIMING_2_REG , LCDC_SYNC_EDGE ) ;
if ( mode - > flags & DRM_MODE_FLAG_NHSYNC )
tilcdc_set ( dev , LCDC_RASTER_TIMING_2_REG , LCDC_INVERT_HSYNC ) ;
else
tilcdc_clear ( dev , LCDC_RASTER_TIMING_2_REG , LCDC_INVERT_HSYNC ) ;
if ( mode - > flags & DRM_MODE_FLAG_NVSYNC )
tilcdc_set ( dev , LCDC_RASTER_TIMING_2_REG , LCDC_INVERT_VSYNC ) ;
else
tilcdc_clear ( dev , LCDC_RASTER_TIMING_2_REG , LCDC_INVERT_VSYNC ) ;
if ( info - > raster_order )
tilcdc_set ( dev , LCDC_RASTER_CTRL_REG , LCDC_RASTER_ORDER ) ;
else
tilcdc_clear ( dev , LCDC_RASTER_CTRL_REG , LCDC_RASTER_ORDER ) ;
drm_framebuffer_reference ( fb ) ;
set_scanout ( crtc , fb ) ;
2016-09-06 16:19:54 +03:00
tilcdc_crtc_set_clk ( crtc ) ;
2016-04-07 15:09:50 +03:00
crtc - > hwmode = crtc - > state - > adjusted_mode ;
}
2016-04-07 15:10:23 +03:00
static int tilcdc_crtc_atomic_check ( struct drm_crtc * crtc ,
struct drm_crtc_state * state )
{
struct drm_display_mode * mode = & state - > mode ;
int ret ;
/* If we are not active we don't care */
if ( ! state - > active )
return 0 ;
if ( state - > state - > planes [ 0 ] . ptr ! = crtc - > primary | |
state - > state - > planes [ 0 ] . state = = NULL | |
state - > state - > planes [ 0 ] . state - > crtc ! = crtc ) {
dev_dbg ( crtc - > dev - > dev , " CRTC primary plane must be present " ) ;
return - EINVAL ;
}
ret = tilcdc_crtc_mode_valid ( crtc , mode ) ;
if ( ret ) {
dev_dbg ( crtc - > dev - > dev , " Mode \" %s \" not valid " , mode - > name ) ;
return - EINVAL ;
}
return 0 ;
}
2013-01-08 15:04:28 -06:00
static const struct drm_crtc_funcs tilcdc_crtc_funcs = {
2016-04-07 15:05:16 +03:00
. destroy = tilcdc_crtc_destroy ,
. set_config = drm_atomic_helper_set_config ,
. page_flip = drm_atomic_helper_page_flip ,
. reset = drm_atomic_helper_crtc_reset ,
. atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_crtc_destroy_state ,
2013-01-08 15:04:28 -06:00
} ;
static const struct drm_crtc_helper_funcs tilcdc_crtc_helper_funcs = {
. mode_fixup = tilcdc_crtc_mode_fixup ,
2016-04-07 15:05:16 +03:00
. enable = tilcdc_crtc_enable ,
. disable = tilcdc_crtc_disable ,
2016-04-07 15:10:23 +03:00
. atomic_check = tilcdc_crtc_atomic_check ,
2016-04-07 15:09:50 +03:00
. mode_set_nofb = tilcdc_crtc_mode_set_nofb ,
2013-01-08 15:04:28 -06:00
} ;
int tilcdc_crtc_max_width ( struct drm_crtc * crtc )
{
struct drm_device * dev = crtc - > dev ;
struct tilcdc_drm_private * priv = dev - > dev_private ;
int max_width = 0 ;
if ( priv - > rev = = 1 )
max_width = 1024 ;
else if ( priv - > rev = = 2 )
max_width = 2048 ;
return max_width ;
}
int tilcdc_crtc_mode_valid ( struct drm_crtc * crtc , struct drm_display_mode * mode )
{
struct tilcdc_drm_private * priv = crtc - > dev - > dev_private ;
unsigned int bandwidth ;
2013-06-21 13:52:25 -05:00
uint32_t hbp , hfp , hsw , vbp , vfp , vsw ;
2013-01-08 15:04:28 -06:00
2013-06-21 13:52:25 -05:00
/*
* check to see if the width is within the range that
* the LCD Controller physically supports
*/
2013-01-08 15:04:28 -06:00
if ( mode - > hdisplay > tilcdc_crtc_max_width ( crtc ) )
return MODE_VIRTUAL_X ;
/* width must be multiple of 16 */
if ( mode - > hdisplay & 0xf )
return MODE_VIRTUAL_X ;
if ( mode - > vdisplay > 2048 )
return MODE_VIRTUAL_Y ;
2013-06-21 13:52:25 -05:00
DBG ( " Processing mode %dx%d@%d with pixel clock %d " ,
mode - > hdisplay , mode - > vdisplay ,
drm_mode_vrefresh ( mode ) , mode - > clock ) ;
hbp = mode - > htotal - mode - > hsync_end ;
hfp = mode - > hsync_start - mode - > hdisplay ;
hsw = mode - > hsync_end - mode - > hsync_start ;
vbp = mode - > vtotal - mode - > vsync_end ;
vfp = mode - > vsync_start - mode - > vdisplay ;
vsw = mode - > vsync_end - mode - > vsync_start ;
if ( ( hbp - 1 ) & ~ 0x3ff ) {
DBG ( " Pruning mode: Horizontal Back Porch out of range " ) ;
return MODE_HBLANK_WIDE ;
}
if ( ( hfp - 1 ) & ~ 0x3ff ) {
DBG ( " Pruning mode: Horizontal Front Porch out of range " ) ;
return MODE_HBLANK_WIDE ;
}
if ( ( hsw - 1 ) & ~ 0x3ff ) {
DBG ( " Pruning mode: Horizontal Sync Width out of range " ) ;
return MODE_HSYNC_WIDE ;
}
if ( vbp & ~ 0xff ) {
DBG ( " Pruning mode: Vertical Back Porch out of range " ) ;
return MODE_VBLANK_WIDE ;
}
if ( vfp & ~ 0xff ) {
DBG ( " Pruning mode: Vertical Front Porch out of range " ) ;
return MODE_VBLANK_WIDE ;
}
if ( ( vsw - 1 ) & ~ 0x3f ) {
DBG ( " Pruning mode: Vertical Sync Width out of range " ) ;
return MODE_VSYNC_WIDE ;
}
2013-06-21 13:52:23 -05:00
/*
* some devices have a maximum allowed pixel clock
* configured from the DT
*/
if ( mode - > clock > priv - > max_pixelclock ) {
2013-06-21 13:52:26 -05:00
DBG ( " Pruning mode: pixel clock too high " ) ;
2013-06-21 13:52:23 -05:00
return MODE_CLOCK_HIGH ;
}
/*
* some devices further limit the max horizontal resolution
* configured from the DT
*/
if ( mode - > hdisplay > priv - > max_width )
return MODE_BAD_WIDTH ;
2013-01-08 15:04:28 -06:00
/* filter out modes that would require too much memory bandwidth: */
2013-06-21 13:52:23 -05:00
bandwidth = mode - > hdisplay * mode - > vdisplay *
drm_mode_vrefresh ( mode ) ;
if ( bandwidth > priv - > max_bandwidth ) {
2013-06-21 13:52:26 -05:00
DBG ( " Pruning mode: exceeds defined bandwidth limit " ) ;
2013-01-08 15:04:28 -06:00
return MODE_BAD ;
2013-06-21 13:52:23 -05:00
}
2013-01-08 15:04:28 -06:00
return MODE_OK ;
}
void tilcdc_crtc_set_panel_info ( struct drm_crtc * crtc ,
const struct tilcdc_panel_info * info )
{
struct tilcdc_crtc * tilcdc_crtc = to_tilcdc_crtc ( crtc ) ;
tilcdc_crtc - > info = info ;
}
2015-02-10 14:13:23 +02:00
void tilcdc_crtc_set_simulate_vesa_sync ( struct drm_crtc * crtc ,
bool simulate_vesa_sync )
{
struct tilcdc_crtc * tilcdc_crtc = to_tilcdc_crtc ( crtc ) ;
tilcdc_crtc - > simulate_vesa_sync = simulate_vesa_sync ;
}
2013-01-08 15:04:28 -06:00
void tilcdc_crtc_update_clk ( struct drm_crtc * crtc )
{
struct drm_device * dev = crtc - > dev ;
struct tilcdc_drm_private * priv = dev - > dev_private ;
2016-09-06 16:19:54 +03:00
struct tilcdc_crtc * tilcdc_crtc = to_tilcdc_crtc ( crtc ) ;
2013-01-08 15:04:28 -06:00
2016-09-06 16:19:54 +03:00
drm_modeset_lock_crtc ( crtc , NULL ) ;
if ( tilcdc_crtc - > lcd_fck_rate ! = clk_get_rate ( priv - > clk ) ) {
if ( tilcdc_crtc_is_on ( crtc ) ) {
pm_runtime_get_sync ( dev - > dev ) ;
tilcdc_crtc_disable ( crtc ) ;
2013-01-08 15:04:28 -06:00
2016-09-06 16:19:54 +03:00
tilcdc_crtc_set_clk ( crtc ) ;
2013-01-08 15:04:28 -06:00
2016-09-06 16:19:54 +03:00
tilcdc_crtc_enable ( crtc ) ;
pm_runtime_put_sync ( dev - > dev ) ;
}
2013-01-08 15:04:28 -06:00
}
2016-09-06 16:19:54 +03:00
drm_modeset_unlock_crtc ( crtc ) ;
2013-01-08 15:04:28 -06:00
}
2016-01-08 14:33:09 +02:00
# define SYNC_LOST_COUNT_LIMIT 50
2013-01-08 15:04:28 -06:00
irqreturn_t tilcdc_crtc_irq ( struct drm_crtc * crtc )
{
struct tilcdc_crtc * tilcdc_crtc = to_tilcdc_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
struct tilcdc_drm_private * priv = dev - > dev_private ;
2015-10-20 12:08:03 +03:00
uint32_t stat ;
2013-01-08 15:04:28 -06:00
2015-10-20 12:08:03 +03:00
stat = tilcdc_read_irqstatus ( dev ) ;
tilcdc_clear_irqstatus ( dev , stat ) ;
2015-10-20 09:37:27 +03:00
if ( stat & LCDC_END_OF_FRAME0 ) {
2013-01-08 15:04:28 -06:00
unsigned long flags ;
2015-11-03 12:00:51 +02:00
bool skip_event = false ;
ktime_t now ;
now = ktime_get ( ) ;
2013-01-08 15:04:28 -06:00
2015-10-20 09:37:27 +03:00
drm_flip_work_commit ( & tilcdc_crtc - > unref_work , priv - > wq ) ;
2013-01-08 15:04:28 -06:00
2015-11-03 12:00:51 +02:00
spin_lock_irqsave ( & tilcdc_crtc - > irq_lock , flags ) ;
tilcdc_crtc - > last_vblank = now ;
if ( tilcdc_crtc - > next_fb ) {
set_scanout ( crtc , tilcdc_crtc - > next_fb ) ;
tilcdc_crtc - > next_fb = NULL ;
skip_event = true ;
}
spin_unlock_irqrestore ( & tilcdc_crtc - > irq_lock , flags ) ;
2016-07-04 21:04:52 -03:00
drm_crtc_handle_vblank ( crtc ) ;
2013-01-08 15:04:28 -06:00
2015-11-03 12:00:51 +02:00
if ( ! skip_event ) {
struct drm_pending_vblank_event * event ;
2013-01-08 15:04:28 -06:00
2015-11-03 12:00:51 +02:00
spin_lock_irqsave ( & dev - > event_lock , flags ) ;
event = tilcdc_crtc - > event ;
2015-10-20 09:37:27 +03:00
tilcdc_crtc - > event = NULL ;
2015-11-03 12:00:51 +02:00
if ( event )
2016-04-14 10:48:22 -07:00
drm_crtc_send_vblank_event ( crtc , event ) ;
2015-10-20 09:37:27 +03:00
2015-11-03 12:00:51 +02:00
spin_unlock_irqrestore ( & dev - > event_lock , flags ) ;
}
2016-01-08 14:33:09 +02:00
if ( tilcdc_crtc - > frame_intact )
tilcdc_crtc - > sync_lost_count = 0 ;
else
tilcdc_crtc - > frame_intact = true ;
2013-01-08 15:04:28 -06:00
}
2016-04-07 20:36:48 +03:00
if ( stat & LCDC_FIFO_UNDERFLOW )
2016-10-28 13:52:42 +02:00
dev_err_ratelimited ( dev - > dev , " %s(0x%08x): FIFO underflow " ,
2016-04-07 20:36:48 +03:00
__func__ , stat ) ;
/* For revision 2 only */
2013-01-08 15:04:28 -06:00
if ( priv - > rev = = 2 ) {
if ( stat & LCDC_FRAME_DONE ) {
tilcdc_crtc - > frame_done = true ;
wake_up ( & tilcdc_crtc - > frame_done_wq ) ;
}
2016-06-17 11:54:06 +03:00
if ( stat & LCDC_SYNC_LOST ) {
dev_err_ratelimited ( dev - > dev , " %s(0x%08x): Sync lost " ,
__func__ , stat ) ;
tilcdc_crtc - > frame_intact = false ;
if ( tilcdc_crtc - > sync_lost_count + + >
SYNC_LOST_COUNT_LIMIT ) {
2016-04-06 14:02:38 +03:00
dev_err ( dev - > dev , " %s(0x%08x): Sync lost flood detected, recovering " , __func__ , stat ) ;
queue_work ( system_wq ,
& tilcdc_crtc - > recover_work ) ;
2016-06-17 11:54:06 +03:00
tilcdc_write ( dev , LCDC_INT_ENABLE_CLR_REG ,
LCDC_SYNC_LOST ) ;
2016-04-06 14:02:38 +03:00
tilcdc_crtc - > sync_lost_count = 0 ;
2016-06-17 11:54:06 +03:00
}
2016-01-08 14:33:09 +02:00
}
2015-12-18 13:07:52 +02:00
2016-04-07 20:36:48 +03:00
/* Indicate to LCDC that the interrupt service routine has
* completed , see 13.3 .6 .1 .6 in AM335x TRM .
*/
tilcdc_write ( dev , LCDC_END_OF_INT_IND_REG , 0 ) ;
}
2015-12-18 13:07:52 +02:00
2013-01-08 15:04:28 -06:00
return IRQ_HANDLED ;
}
struct drm_crtc * tilcdc_crtc_create ( struct drm_device * dev )
{
2015-05-27 11:58:37 +03:00
struct tilcdc_drm_private * priv = dev - > dev_private ;
2013-01-08 15:04:28 -06:00
struct tilcdc_crtc * tilcdc_crtc ;
struct drm_crtc * crtc ;
int ret ;
2016-02-23 12:44:27 +02:00
tilcdc_crtc = devm_kzalloc ( dev - > dev , sizeof ( * tilcdc_crtc ) , GFP_KERNEL ) ;
2013-01-08 15:04:28 -06:00
if ( ! tilcdc_crtc ) {
dev_err ( dev - > dev , " allocation failed \n " ) ;
return NULL ;
}
crtc = & tilcdc_crtc - > base ;
2016-04-07 15:04:18 +03:00
ret = tilcdc_plane_init ( dev , & tilcdc_crtc - > primary ) ;
if ( ret < 0 )
goto fail ;
2016-10-25 12:27:31 +03:00
mutex_init ( & tilcdc_crtc - > enable_lock ) ;
2013-01-08 15:04:28 -06:00
init_waitqueue_head ( & tilcdc_crtc - > frame_done_wq ) ;
2014-11-14 19:30:30 +01:00
drm_flip_work_init ( & tilcdc_crtc - > unref_work ,
2013-08-07 13:41:20 -04:00
" unref " , unref_worker ) ;
2013-01-08 15:04:28 -06:00
2015-11-03 12:00:51 +02:00
spin_lock_init ( & tilcdc_crtc - > irq_lock ) ;
2016-04-06 14:02:38 +03:00
INIT_WORK ( & tilcdc_crtc - > recover_work , tilcdc_crtc_recover_work ) ;
2015-11-03 12:00:51 +02:00
2016-04-07 15:04:18 +03:00
ret = drm_crtc_init_with_planes ( dev , crtc ,
& tilcdc_crtc - > primary ,
NULL ,
& tilcdc_crtc_funcs ,
" tilcdc crtc " ) ;
2013-01-08 15:04:28 -06:00
if ( ret < 0 )
goto fail ;
drm_crtc_helper_add ( crtc , & tilcdc_crtc_helper_funcs ) ;
2015-05-27 11:58:37 +03:00
if ( priv - > is_componentized ) {
struct device_node * ports =
of_get_child_by_name ( dev - > dev - > of_node , " ports " ) ;
if ( ports ) {
crtc - > port = of_get_child_by_name ( ports , " port " ) ;
of_node_put ( ports ) ;
} else {
crtc - > port =
of_get_child_by_name ( dev - > dev - > of_node , " port " ) ;
}
if ( ! crtc - > port ) { /* This should never happen */
dev_err ( dev - > dev , " Port node not found in %s \n " ,
dev - > dev - > of_node - > full_name ) ;
goto fail ;
}
}
2013-01-08 15:04:28 -06:00
return crtc ;
fail :
tilcdc_crtc_destroy ( crtc ) ;
return NULL ;
}