2014-08-22 18:36:26 +08:00
/*
* Copyright ( C ) Fuzhou Rockchip Electronics Co . Ltd
* Author : Mark Yao < mark . yao @ rock - chips . com >
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* 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 .
*/
# include <drm/drm.h>
# include <drm/drmP.h>
2015-11-30 18:22:42 +08:00
# include <drm/drm_atomic.h>
2014-08-22 18:36:26 +08:00
# include <drm/drm_crtc.h>
# include <drm/drm_crtc_helper.h>
# include <drm/drm_plane_helper.h>
# include <linux/kernel.h>
2015-05-01 20:02:30 -04:00
# include <linux/module.h>
2014-08-22 18:36:26 +08:00
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/pm_runtime.h>
# include <linux/component.h>
# include <linux/reset.h>
# include <linux/delay.h>
# include "rockchip_drm_drv.h"
# include "rockchip_drm_gem.h"
# include "rockchip_drm_fb.h"
# include "rockchip_drm_vop.h"
# define __REG_SET_RELAXED(x, off, mask, shift, v) \
vop_mask_write_relaxed ( x , off , ( mask ) < < shift , ( v ) < < shift )
# define __REG_SET_NORMAL(x, off, mask, shift, v) \
vop_mask_write ( x , off , ( mask ) < < shift , ( v ) < < shift )
# define REG_SET(x, base, reg, v, mode) \
__REG_SET_ # # mode ( x , base + reg . offset , reg . mask , reg . shift , v )
2016-01-12 18:05:18 +00:00
# define REG_SET_MASK(x, base, reg, mask, v, mode) \
__REG_SET_ # # mode ( x , base + reg . offset , mask , reg . shift , v )
2014-08-22 18:36:26 +08:00
# define VOP_WIN_SET(x, win, name, v) \
REG_SET ( x , win - > base , win - > phy - > name , v , RELAXED )
2015-06-26 17:14:46 +08:00
# define VOP_SCL_SET(x, win, name, v) \
REG_SET ( x , win - > base , win - > phy - > scl - > name , v , RELAXED )
2015-12-15 09:08:43 +08:00
# define VOP_SCL_SET_EXT(x, win, name, v) \
REG_SET ( x , win - > base , win - > phy - > scl - > ext - > name , v , RELAXED )
2014-08-22 18:36:26 +08:00
# define VOP_CTRL_SET(x, name, v) \
REG_SET ( x , 0 , ( x ) - > data - > ctrl - > name , v , NORMAL )
2015-12-15 08:36:55 +08:00
# define VOP_INTR_GET(vop, name) \
vop_read_reg ( vop , 0 , & vop - > data - > ctrl - > name )
2016-01-12 18:05:18 +00:00
# define VOP_INTR_SET(vop, name, mask, v) \
REG_SET_MASK ( vop , 0 , vop - > data - > intr - > name , mask , v , NORMAL )
2015-12-15 08:36:55 +08:00
# define VOP_INTR_SET_TYPE(vop, name, type, v) \
do { \
2016-01-12 18:05:18 +00:00
int i , reg = 0 , mask = 0 ; \
2015-12-15 08:36:55 +08:00
for ( i = 0 ; i < vop - > data - > intr - > nintrs ; i + + ) { \
2016-01-12 18:05:18 +00:00
if ( vop - > data - > intr - > intrs [ i ] & type ) { \
2015-12-15 08:36:55 +08:00
reg | = ( v ) < < i ; \
2016-01-12 18:05:18 +00:00
mask | = 1 < < i ; \
} \
2015-12-15 08:36:55 +08:00
} \
2016-01-12 18:05:18 +00:00
VOP_INTR_SET ( vop , name , mask , reg ) ; \
2015-12-15 08:36:55 +08:00
} while ( 0 )
# define VOP_INTR_GET_TYPE(vop, name, type) \
vop_get_intr_type ( vop , & vop - > data - > intr - > name , type )
2014-08-22 18:36:26 +08:00
# define VOP_WIN_GET(x, win, name) \
vop_read_reg ( x , win - > base , & win - > phy - > name )
# define VOP_WIN_GET_YRGBADDR(vop, win) \
vop_readl ( vop , win - > base + win - > phy - > yrgb_mst . offset )
# define to_vop(x) container_of(x, struct vop, crtc)
# define to_vop_win(x) container_of(x, struct vop_win, base)
2015-11-30 18:22:42 +08:00
# define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base)
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
struct vop_plane_state {
struct drm_plane_state base ;
int format ;
struct drm_rect src ;
struct drm_rect dest ;
2014-08-22 18:36:26 +08:00
dma_addr_t yrgb_mst ;
2015-11-30 18:22:42 +08:00
bool enable ;
2014-08-22 18:36:26 +08:00
} ;
struct vop_win {
struct drm_plane base ;
const struct vop_win_data * data ;
struct vop * vop ;
2015-11-30 18:22:42 +08:00
struct vop_plane_state state ;
2014-08-22 18:36:26 +08:00
} ;
struct vop {
struct drm_crtc crtc ;
struct device * dev ;
struct drm_device * drm_dev ;
2015-01-22 14:37:56 +08:00
bool is_enabled ;
2014-08-22 18:36:26 +08:00
/* mutex vsync_ work */
struct mutex vsync_mutex ;
bool vsync_work_pending ;
2015-02-04 13:10:31 +08:00
struct completion dsp_hold_completion ;
2015-11-30 18:22:42 +08:00
struct completion wait_update_complete ;
struct drm_pending_vblank_event * event ;
2014-08-22 18:36:26 +08:00
const struct vop_data * data ;
uint32_t * regsbak ;
void __iomem * regs ;
/* physical map length of vop register */
uint32_t len ;
/* one time only one process allowed to config the register */
spinlock_t reg_lock ;
/* lock vop irq reg */
spinlock_t irq_lock ;
unsigned int irq ;
/* vop AHP clk */
struct clk * hclk ;
/* vop dclk */
struct clk * dclk ;
/* vop share memory frequency */
struct clk * aclk ;
/* vop dclk reset */
struct reset_control * dclk_rst ;
struct vop_win win [ ] ;
} ;
static inline void vop_writel ( struct vop * vop , uint32_t offset , uint32_t v )
{
writel ( v , vop - > regs + offset ) ;
vop - > regsbak [ offset > > 2 ] = v ;
}
static inline uint32_t vop_readl ( struct vop * vop , uint32_t offset )
{
return readl ( vop - > regs + offset ) ;
}
static inline uint32_t vop_read_reg ( struct vop * vop , uint32_t base ,
const struct vop_reg * reg )
{
return ( vop_readl ( vop , base + reg - > offset ) > > reg - > shift ) & reg - > mask ;
}
static inline void vop_mask_write ( struct vop * vop , uint32_t offset ,
uint32_t mask , uint32_t v )
{
if ( mask ) {
uint32_t cached_val = vop - > regsbak [ offset > > 2 ] ;
cached_val = ( cached_val & ~ mask ) | v ;
writel ( cached_val , vop - > regs + offset ) ;
vop - > regsbak [ offset > > 2 ] = cached_val ;
}
}
static inline void vop_mask_write_relaxed ( struct vop * vop , uint32_t offset ,
uint32_t mask , uint32_t v )
{
if ( mask ) {
uint32_t cached_val = vop - > regsbak [ offset > > 2 ] ;
cached_val = ( cached_val & ~ mask ) | v ;
writel_relaxed ( cached_val , vop - > regs + offset ) ;
vop - > regsbak [ offset > > 2 ] = cached_val ;
}
}
2015-12-15 08:36:55 +08:00
static inline uint32_t vop_get_intr_type ( struct vop * vop ,
const struct vop_reg * reg , int type )
{
uint32_t i , ret = 0 ;
uint32_t regs = vop_read_reg ( vop , 0 , reg ) ;
for ( i = 0 ; i < vop - > data - > intr - > nintrs ; i + + ) {
if ( ( type & vop - > data - > intr - > intrs [ i ] ) & & ( regs & 1 < < i ) )
ret | = vop - > data - > intr - > intrs [ i ] ;
}
return ret ;
}
2015-12-14 18:14:36 +08:00
static inline void vop_cfg_done ( struct vop * vop )
{
VOP_CTRL_SET ( vop , cfg_done , 1 ) ;
}
2015-05-11 19:55:39 +09:00
static bool has_rb_swapped ( uint32_t format )
{
switch ( format ) {
case DRM_FORMAT_XBGR8888 :
case DRM_FORMAT_ABGR8888 :
case DRM_FORMAT_BGR888 :
case DRM_FORMAT_BGR565 :
return true ;
default :
return false ;
}
}
2014-08-22 18:36:26 +08:00
static enum vop_data_format vop_convert_format ( uint32_t format )
{
switch ( format ) {
case DRM_FORMAT_XRGB8888 :
case DRM_FORMAT_ARGB8888 :
2015-05-11 19:55:39 +09:00
case DRM_FORMAT_XBGR8888 :
case DRM_FORMAT_ABGR8888 :
2014-08-22 18:36:26 +08:00
return VOP_FMT_ARGB8888 ;
case DRM_FORMAT_RGB888 :
2015-05-11 19:55:39 +09:00
case DRM_FORMAT_BGR888 :
2014-08-22 18:36:26 +08:00
return VOP_FMT_RGB888 ;
case DRM_FORMAT_RGB565 :
2015-05-11 19:55:39 +09:00
case DRM_FORMAT_BGR565 :
2014-08-22 18:36:26 +08:00
return VOP_FMT_RGB565 ;
case DRM_FORMAT_NV12 :
return VOP_FMT_YUV420SP ;
case DRM_FORMAT_NV16 :
return VOP_FMT_YUV422SP ;
case DRM_FORMAT_NV24 :
return VOP_FMT_YUV444SP ;
default :
DRM_ERROR ( " unsupport format[%08x] \n " , format ) ;
return - EINVAL ;
}
}
2015-07-20 16:16:49 +08:00
static bool is_yuv_support ( uint32_t format )
{
switch ( format ) {
case DRM_FORMAT_NV12 :
case DRM_FORMAT_NV16 :
case DRM_FORMAT_NV24 :
return true ;
default :
return false ;
}
}
2014-08-22 18:36:26 +08:00
static bool is_alpha_support ( uint32_t format )
{
switch ( format ) {
case DRM_FORMAT_ARGB8888 :
2015-05-11 19:55:39 +09:00
case DRM_FORMAT_ABGR8888 :
2014-08-22 18:36:26 +08:00
return true ;
default :
return false ;
}
}
2015-06-26 17:14:46 +08:00
static uint16_t scl_vop_cal_scale ( enum scale_mode mode , uint32_t src ,
uint32_t dst , bool is_horizontal ,
int vsu_mode , int * vskiplines )
{
uint16_t val = 1 < < SCL_FT_DEFAULT_FIXPOINT_SHIFT ;
if ( is_horizontal ) {
if ( mode = = SCALE_UP )
val = GET_SCL_FT_BIC ( src , dst ) ;
else if ( mode = = SCALE_DOWN )
val = GET_SCL_FT_BILI_DN ( src , dst ) ;
} else {
if ( mode = = SCALE_UP ) {
if ( vsu_mode = = SCALE_UP_BIL )
val = GET_SCL_FT_BILI_UP ( src , dst ) ;
else
val = GET_SCL_FT_BIC ( src , dst ) ;
} else if ( mode = = SCALE_DOWN ) {
if ( vskiplines ) {
* vskiplines = scl_get_vskiplines ( src , dst ) ;
val = scl_get_bili_dn_vskip ( src , dst ,
* vskiplines ) ;
} else {
val = GET_SCL_FT_BILI_DN ( src , dst ) ;
}
}
}
return val ;
}
static void scl_vop_cal_scl_fac ( struct vop * vop , const struct vop_win_data * win ,
uint32_t src_w , uint32_t src_h , uint32_t dst_w ,
uint32_t dst_h , uint32_t pixel_format )
{
uint16_t yrgb_hor_scl_mode , yrgb_ver_scl_mode ;
uint16_t cbcr_hor_scl_mode = SCALE_NONE ;
uint16_t cbcr_ver_scl_mode = SCALE_NONE ;
int hsub = drm_format_horz_chroma_subsampling ( pixel_format ) ;
int vsub = drm_format_vert_chroma_subsampling ( pixel_format ) ;
bool is_yuv = is_yuv_support ( pixel_format ) ;
uint16_t cbcr_src_w = src_w / hsub ;
uint16_t cbcr_src_h = src_h / vsub ;
uint16_t vsu_mode ;
uint16_t lb_mode ;
uint32_t val ;
int vskiplines ;
if ( dst_w > 3840 ) {
DRM_ERROR ( " Maximum destination width (3840) exceeded \n " ) ;
return ;
}
2015-12-15 09:08:43 +08:00
if ( ! win - > phy - > scl - > ext ) {
VOP_SCL_SET ( vop , win , scale_yrgb_x ,
scl_cal_scale2 ( src_w , dst_w ) ) ;
VOP_SCL_SET ( vop , win , scale_yrgb_y ,
scl_cal_scale2 ( src_h , dst_h ) ) ;
if ( is_yuv ) {
VOP_SCL_SET ( vop , win , scale_cbcr_x ,
scl_cal_scale2 ( src_w , dst_w ) ) ;
VOP_SCL_SET ( vop , win , scale_cbcr_y ,
scl_cal_scale2 ( src_h , dst_h ) ) ;
}
return ;
}
2015-06-26 17:14:46 +08:00
yrgb_hor_scl_mode = scl_get_scl_mode ( src_w , dst_w ) ;
yrgb_ver_scl_mode = scl_get_scl_mode ( src_h , dst_h ) ;
if ( is_yuv ) {
cbcr_hor_scl_mode = scl_get_scl_mode ( cbcr_src_w , dst_w ) ;
cbcr_ver_scl_mode = scl_get_scl_mode ( cbcr_src_h , dst_h ) ;
if ( cbcr_hor_scl_mode = = SCALE_DOWN )
lb_mode = scl_vop_cal_lb_mode ( dst_w , true ) ;
else
lb_mode = scl_vop_cal_lb_mode ( cbcr_src_w , true ) ;
} else {
if ( yrgb_hor_scl_mode = = SCALE_DOWN )
lb_mode = scl_vop_cal_lb_mode ( dst_w , false ) ;
else
lb_mode = scl_vop_cal_lb_mode ( src_w , false ) ;
}
2015-12-15 09:08:43 +08:00
VOP_SCL_SET_EXT ( vop , win , lb_mode , lb_mode ) ;
2015-06-26 17:14:46 +08:00
if ( lb_mode = = LB_RGB_3840X2 ) {
if ( yrgb_ver_scl_mode ! = SCALE_NONE ) {
DRM_ERROR ( " ERROR : not allow yrgb ver scale \n " ) ;
return ;
}
if ( cbcr_ver_scl_mode ! = SCALE_NONE ) {
DRM_ERROR ( " ERROR : not allow cbcr ver scale \n " ) ;
return ;
}
vsu_mode = SCALE_UP_BIL ;
} else if ( lb_mode = = LB_RGB_2560X4 ) {
vsu_mode = SCALE_UP_BIL ;
} else {
vsu_mode = SCALE_UP_BIC ;
}
val = scl_vop_cal_scale ( yrgb_hor_scl_mode , src_w , dst_w ,
true , 0 , NULL ) ;
VOP_SCL_SET ( vop , win , scale_yrgb_x , val ) ;
val = scl_vop_cal_scale ( yrgb_ver_scl_mode , src_h , dst_h ,
false , vsu_mode , & vskiplines ) ;
VOP_SCL_SET ( vop , win , scale_yrgb_y , val ) ;
2015-12-15 09:08:43 +08:00
VOP_SCL_SET_EXT ( vop , win , vsd_yrgb_gt4 , vskiplines = = 4 ) ;
VOP_SCL_SET_EXT ( vop , win , vsd_yrgb_gt2 , vskiplines = = 2 ) ;
2015-06-26 17:14:46 +08:00
2015-12-15 09:08:43 +08:00
VOP_SCL_SET_EXT ( vop , win , yrgb_hor_scl_mode , yrgb_hor_scl_mode ) ;
VOP_SCL_SET_EXT ( vop , win , yrgb_ver_scl_mode , yrgb_ver_scl_mode ) ;
VOP_SCL_SET_EXT ( vop , win , yrgb_hsd_mode , SCALE_DOWN_BIL ) ;
VOP_SCL_SET_EXT ( vop , win , yrgb_vsd_mode , SCALE_DOWN_BIL ) ;
VOP_SCL_SET_EXT ( vop , win , yrgb_vsu_mode , vsu_mode ) ;
2015-06-26 17:14:46 +08:00
if ( is_yuv ) {
val = scl_vop_cal_scale ( cbcr_hor_scl_mode , cbcr_src_w ,
dst_w , true , 0 , NULL ) ;
VOP_SCL_SET ( vop , win , scale_cbcr_x , val ) ;
val = scl_vop_cal_scale ( cbcr_ver_scl_mode , cbcr_src_h ,
dst_h , false , vsu_mode , & vskiplines ) ;
VOP_SCL_SET ( vop , win , scale_cbcr_y , val ) ;
2015-12-15 09:08:43 +08:00
VOP_SCL_SET_EXT ( vop , win , vsd_cbcr_gt4 , vskiplines = = 4 ) ;
VOP_SCL_SET_EXT ( vop , win , vsd_cbcr_gt2 , vskiplines = = 2 ) ;
VOP_SCL_SET_EXT ( vop , win , cbcr_hor_scl_mode , cbcr_hor_scl_mode ) ;
VOP_SCL_SET_EXT ( vop , win , cbcr_ver_scl_mode , cbcr_ver_scl_mode ) ;
VOP_SCL_SET_EXT ( vop , win , cbcr_hsd_mode , SCALE_DOWN_BIL ) ;
VOP_SCL_SET_EXT ( vop , win , cbcr_vsd_mode , SCALE_DOWN_BIL ) ;
VOP_SCL_SET_EXT ( vop , win , cbcr_vsu_mode , vsu_mode ) ;
2015-06-26 17:14:46 +08:00
}
}
2015-02-04 13:10:31 +08:00
static void vop_dsp_hold_valid_irq_enable ( struct vop * vop )
{
unsigned long flags ;
if ( WARN_ON ( ! vop - > is_enabled ) )
return ;
spin_lock_irqsave ( & vop - > irq_lock , flags ) ;
2015-12-15 08:36:55 +08:00
VOP_INTR_SET_TYPE ( vop , enable , DSP_HOLD_VALID_INTR , 1 ) ;
2015-02-04 13:10:31 +08:00
spin_unlock_irqrestore ( & vop - > irq_lock , flags ) ;
}
static void vop_dsp_hold_valid_irq_disable ( struct vop * vop )
{
unsigned long flags ;
if ( WARN_ON ( ! vop - > is_enabled ) )
return ;
spin_lock_irqsave ( & vop - > irq_lock , flags ) ;
2015-12-15 08:36:55 +08:00
VOP_INTR_SET_TYPE ( vop , enable , DSP_HOLD_VALID_INTR , 0 ) ;
2015-02-04 13:10:31 +08:00
spin_unlock_irqrestore ( & vop - > irq_lock , flags ) ;
}
2015-11-30 18:22:42 +08:00
static void vop_enable ( struct drm_crtc * crtc )
2014-08-22 18:36:26 +08:00
{
struct vop * vop = to_vop ( crtc ) ;
int ret ;
2015-01-22 14:37:56 +08:00
if ( vop - > is_enabled )
return ;
2015-04-01 13:48:53 +08:00
ret = pm_runtime_get_sync ( vop - > dev ) ;
if ( ret < 0 ) {
dev_err ( vop - > dev , " failed to get pm runtime: %d \n " , ret ) ;
return ;
}
2014-08-22 18:36:26 +08:00
ret = clk_enable ( vop - > hclk ) ;
if ( ret < 0 ) {
dev_err ( vop - > dev , " failed to enable hclk - %d \n " , ret ) ;
return ;
}
ret = clk_enable ( vop - > dclk ) ;
if ( ret < 0 ) {
dev_err ( vop - > dev , " failed to enable dclk - %d \n " , ret ) ;
goto err_disable_hclk ;
}
ret = clk_enable ( vop - > aclk ) ;
if ( ret < 0 ) {
dev_err ( vop - > dev , " failed to enable aclk - %d \n " , ret ) ;
goto err_disable_dclk ;
}
/*
* Slave iommu shares power , irq and clock with vop . It was associated
* automatically with this master device via common driver code .
* Now that we have enabled the clock we attach it to the shared drm
* mapping .
*/
ret = rockchip_drm_dma_attach_device ( vop - > drm_dev , vop - > dev ) ;
if ( ret ) {
dev_err ( vop - > dev , " failed to attach dma mapping, %d \n " , ret ) ;
goto err_disable_aclk ;
}
2015-07-20 16:25:20 +08:00
memcpy ( vop - > regs , vop - > regsbak , vop - > len ) ;
2015-01-22 18:29:57 +08:00
/*
* At here , vop clock & iommu is enable , R / W vop regs would be safe .
*/
vop - > is_enabled = true ;
2014-08-22 18:36:26 +08:00
spin_lock ( & vop - > reg_lock ) ;
VOP_CTRL_SET ( vop , standby , 0 ) ;
spin_unlock ( & vop - > reg_lock ) ;
enable_irq ( vop - > irq ) ;
2015-11-23 15:21:08 +08:00
drm_crtc_vblank_on ( crtc ) ;
2014-08-22 18:36:26 +08:00
return ;
err_disable_aclk :
clk_disable ( vop - > aclk ) ;
err_disable_dclk :
clk_disable ( vop - > dclk ) ;
err_disable_hclk :
clk_disable ( vop - > hclk ) ;
}
2015-11-09 11:33:16 +08:00
static void vop_crtc_disable ( struct drm_crtc * crtc )
2014-08-22 18:36:26 +08:00
{
struct vop * vop = to_vop ( crtc ) ;
2016-03-22 16:08:04 +01:00
int i ;
2014-08-22 18:36:26 +08:00
2015-01-22 14:37:56 +08:00
if ( ! vop - > is_enabled )
return ;
2016-03-22 16:08:04 +01:00
/*
* We need to make sure that all windows are disabled before we
* disable that crtc . Otherwise we might try to scan from a destroyed
* buffer later .
*/
for ( i = 0 ; i < vop - > data - > win_size ; i + + ) {
struct vop_win * vop_win = & vop - > win [ i ] ;
const struct vop_win_data * win = vop_win - > data ;
spin_lock ( & vop - > reg_lock ) ;
VOP_WIN_SET ( vop , win , enable , 0 ) ;
spin_unlock ( & vop - > reg_lock ) ;
}
2015-11-23 15:21:08 +08:00
drm_crtc_vblank_off ( crtc ) ;
2014-08-22 18:36:26 +08:00
/*
2015-02-04 13:10:31 +08:00
* Vop standby will take effect at end of current frame ,
* if dsp hold valid irq happen , it means standby complete .
*
* we must wait standby complete when we want to disable aclk ,
* if not , memory bus maybe dead .
2014-08-22 18:36:26 +08:00
*/
2015-02-04 13:10:31 +08:00
reinit_completion ( & vop - > dsp_hold_completion ) ;
vop_dsp_hold_valid_irq_enable ( vop ) ;
2014-08-22 18:36:26 +08:00
spin_lock ( & vop - > reg_lock ) ;
VOP_CTRL_SET ( vop , standby , 1 ) ;
spin_unlock ( & vop - > reg_lock ) ;
2015-01-22 18:29:57 +08:00
2015-02-04 13:10:31 +08:00
wait_for_completion ( & vop - > dsp_hold_completion ) ;
vop_dsp_hold_valid_irq_disable ( vop ) ;
disable_irq ( vop - > irq ) ;
2015-01-22 18:29:57 +08:00
vop - > is_enabled = false ;
2015-02-04 13:10:31 +08:00
2014-08-22 18:36:26 +08:00
/*
2015-02-04 13:10:31 +08:00
* vop standby complete , so iommu detach is safe .
2014-08-22 18:36:26 +08:00
*/
rockchip_drm_dma_detach_device ( vop - > drm_dev , vop - > dev ) ;
2015-02-04 13:10:31 +08:00
clk_disable ( vop - > dclk ) ;
2014-08-22 18:36:26 +08:00
clk_disable ( vop - > aclk ) ;
clk_disable ( vop - > hclk ) ;
2015-04-01 13:48:53 +08:00
pm_runtime_put ( vop - > dev ) ;
2014-08-22 18:36:26 +08:00
}
2015-11-30 18:22:42 +08:00
static void vop_plane_destroy ( struct drm_plane * plane )
2014-08-22 18:36:26 +08:00
{
2015-11-30 18:22:42 +08:00
drm_plane_cleanup ( plane ) ;
2014-08-22 18:36:26 +08:00
}
2015-11-30 18:22:42 +08:00
static int vop_plane_atomic_check ( struct drm_plane * plane ,
struct drm_plane_state * state )
2014-08-22 18:36:26 +08:00
{
2015-11-30 18:22:42 +08:00
struct drm_crtc * crtc = state - > crtc ;
2016-03-04 11:04:03 +00:00
struct drm_crtc_state * crtc_state ;
2015-11-30 18:22:42 +08:00
struct drm_framebuffer * fb = state - > fb ;
2014-08-22 18:36:26 +08:00
struct vop_win * vop_win = to_vop_win ( plane ) ;
2015-11-30 18:22:42 +08:00
struct vop_plane_state * vop_plane_state = to_vop_plane_state ( state ) ;
2014-08-22 18:36:26 +08:00
const struct vop_win_data * win = vop_win - > data ;
bool visible ;
int ret ;
2015-11-30 18:22:42 +08:00
struct drm_rect * dest = & vop_plane_state - > dest ;
struct drm_rect * src = & vop_plane_state - > src ;
struct drm_rect clip ;
2015-06-26 17:14:46 +08:00
int min_scale = win - > phy - > scl ? FRAC_16_16 ( 1 , 8 ) :
DRM_PLANE_HELPER_NO_SCALING ;
int max_scale = win - > phy - > scl ? FRAC_16_16 ( 8 , 1 ) :
DRM_PLANE_HELPER_NO_SCALING ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
if ( ! crtc | | ! fb )
goto out_disable ;
2016-03-04 11:04:03 +00:00
crtc_state = drm_atomic_get_existing_crtc_state ( state - > state , crtc ) ;
if ( WARN_ON ( ! crtc_state ) )
return - EINVAL ;
2015-11-30 18:22:42 +08:00
src - > x1 = state - > src_x ;
src - > y1 = state - > src_y ;
src - > x2 = state - > src_x + state - > src_w ;
src - > y2 = state - > src_y + state - > src_h ;
dest - > x1 = state - > crtc_x ;
dest - > y1 = state - > crtc_y ;
dest - > x2 = state - > crtc_x + state - > crtc_w ;
dest - > y2 = state - > crtc_y + state - > crtc_h ;
clip . x1 = 0 ;
clip . y1 = 0 ;
2016-03-04 11:04:03 +00:00
clip . x2 = crtc_state - > adjusted_mode . hdisplay ;
clip . y2 = crtc_state - > adjusted_mode . vdisplay ;
2015-11-30 18:22:42 +08:00
ret = drm_plane_helper_check_update ( plane , crtc , state - > fb ,
src , dest , & clip ,
2015-06-26 17:14:46 +08:00
min_scale ,
max_scale ,
2015-11-30 18:22:42 +08:00
true , true , & visible ) ;
2014-08-22 18:36:26 +08:00
if ( ret )
return ret ;
if ( ! visible )
2015-11-30 18:22:42 +08:00
goto out_disable ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
vop_plane_state - > format = vop_convert_format ( fb - > pixel_format ) ;
if ( vop_plane_state - > format < 0 )
return vop_plane_state - > format ;
2015-07-20 16:16:49 +08:00
2015-11-30 18:22:42 +08:00
/*
* Src . x1 can be odd when do clip , but yuv plane start point
* need align with 2 pixel .
*/
if ( is_yuv_support ( fb - > pixel_format ) & & ( ( src - > x1 > > 16 ) % 2 ) )
2014-08-22 18:36:26 +08:00
return - EINVAL ;
2015-11-30 18:22:42 +08:00
vop_plane_state - > enable = true ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
return 0 ;
2015-07-20 16:16:49 +08:00
2015-11-30 18:22:42 +08:00
out_disable :
vop_plane_state - > enable = false ;
return 0 ;
}
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
static void vop_plane_atomic_disable ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
{
struct vop_plane_state * vop_plane_state = to_vop_plane_state ( old_state ) ;
struct vop_win * vop_win = to_vop_win ( plane ) ;
const struct vop_win_data * win = vop_win - > data ;
struct vop * vop = to_vop ( old_state - > crtc ) ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
if ( ! old_state - > crtc )
return ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
spin_lock ( & vop - > reg_lock ) ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
VOP_WIN_SET ( vop , win , enable , 0 ) ;
2015-07-20 16:16:49 +08:00
2015-11-30 18:22:42 +08:00
spin_unlock ( & vop - > reg_lock ) ;
2015-07-20 16:16:49 +08:00
2015-11-30 18:22:42 +08:00
vop_plane_state - > enable = false ;
}
2015-07-20 16:16:49 +08:00
2015-11-30 18:22:42 +08:00
static void vop_plane_atomic_update ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
{
struct drm_plane_state * state = plane - > state ;
struct drm_crtc * crtc = state - > crtc ;
struct vop_win * vop_win = to_vop_win ( plane ) ;
struct vop_plane_state * vop_plane_state = to_vop_plane_state ( state ) ;
const struct vop_win_data * win = vop_win - > data ;
struct vop * vop = to_vop ( state - > crtc ) ;
struct drm_framebuffer * fb = state - > fb ;
unsigned int actual_w , actual_h ;
unsigned int dsp_stx , dsp_sty ;
uint32_t act_info , dsp_info , dsp_st ;
struct drm_rect * src = & vop_plane_state - > src ;
struct drm_rect * dest = & vop_plane_state - > dest ;
struct drm_gem_object * obj , * uv_obj ;
struct rockchip_gem_object * rk_obj , * rk_uv_obj ;
unsigned long offset ;
dma_addr_t dma_addr ;
uint32_t val ;
bool rb_swap ;
2015-07-20 16:16:49 +08:00
2014-08-22 18:36:26 +08:00
/*
2015-11-30 18:22:42 +08:00
* can ' t update plane when vop is disabled .
2014-08-22 18:36:26 +08:00
*/
2015-11-30 18:22:42 +08:00
if ( ! crtc )
return ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
if ( WARN_ON ( ! vop - > is_enabled ) )
return ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
if ( ! vop_plane_state - > enable ) {
vop_plane_atomic_disable ( plane , old_state ) ;
return ;
2014-08-22 18:36:26 +08:00
}
2015-11-30 18:22:42 +08:00
obj = rockchip_fb_get_gem_obj ( fb , 0 ) ;
rk_obj = to_rockchip_obj ( obj ) ;
actual_w = drm_rect_width ( src ) > > 16 ;
actual_h = drm_rect_height ( src ) > > 16 ;
act_info = ( actual_h - 1 ) < < 16 | ( ( actual_w - 1 ) & 0xffff ) ;
dsp_info = ( drm_rect_height ( dest ) - 1 ) < < 16 ;
dsp_info | = ( drm_rect_width ( dest ) - 1 ) & 0xffff ;
dsp_stx = dest - > x1 + crtc - > mode . htotal - crtc - > mode . hsync_start ;
dsp_sty = dest - > y1 + crtc - > mode . vtotal - crtc - > mode . vsync_start ;
dsp_st = dsp_sty < < 16 | ( dsp_stx & 0xffff ) ;
offset = ( src - > x1 > > 16 ) * drm_format_plane_cpp ( fb - > pixel_format , 0 ) ;
offset + = ( src - > y1 > > 16 ) * fb - > pitches [ 0 ] ;
vop_plane_state - > yrgb_mst = rk_obj - > dma_addr + offset + fb - > offsets [ 0 ] ;
2014-08-22 18:36:26 +08:00
spin_lock ( & vop - > reg_lock ) ;
2015-11-30 18:22:42 +08:00
VOP_WIN_SET ( vop , win , format , vop_plane_state - > format ) ;
VOP_WIN_SET ( vop , win , yrgb_vir , fb - > pitches [ 0 ] > > 2 ) ;
VOP_WIN_SET ( vop , win , yrgb_mst , vop_plane_state - > yrgb_mst ) ;
if ( is_yuv_support ( fb - > pixel_format ) ) {
int hsub = drm_format_horz_chroma_subsampling ( fb - > pixel_format ) ;
int vsub = drm_format_vert_chroma_subsampling ( fb - > pixel_format ) ;
int bpp = drm_format_plane_cpp ( fb - > pixel_format , 1 ) ;
uv_obj = rockchip_fb_get_gem_obj ( fb , 1 ) ;
rk_uv_obj = to_rockchip_obj ( uv_obj ) ;
offset = ( src - > x1 > > 16 ) * bpp / hsub ;
offset + = ( src - > y1 > > 16 ) * fb - > pitches [ 1 ] / vsub ;
dma_addr = rk_uv_obj - > dma_addr + offset + fb - > offsets [ 1 ] ;
VOP_WIN_SET ( vop , win , uv_vir , fb - > pitches [ 1 ] > > 2 ) ;
VOP_WIN_SET ( vop , win , uv_mst , dma_addr ) ;
2015-07-20 16:16:49 +08:00
}
2015-06-26 17:14:46 +08:00
if ( win - > phy - > scl )
scl_vop_cal_scl_fac ( vop , win , actual_w , actual_h ,
2015-11-30 18:22:42 +08:00
drm_rect_width ( dest ) , drm_rect_height ( dest ) ,
2015-06-26 17:14:46 +08:00
fb - > pixel_format ) ;
2015-11-30 18:22:42 +08:00
VOP_WIN_SET ( vop , win , act_info , act_info ) ;
VOP_WIN_SET ( vop , win , dsp_info , dsp_info ) ;
VOP_WIN_SET ( vop , win , dsp_st , dsp_st ) ;
2015-06-26 17:14:46 +08:00
2015-11-30 18:22:42 +08:00
rb_swap = has_rb_swapped ( fb - > pixel_format ) ;
2015-05-11 19:55:39 +09:00
VOP_WIN_SET ( vop , win , rb_swap , rb_swap ) ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
if ( is_alpha_support ( fb - > pixel_format ) ) {
2014-08-22 18:36:26 +08:00
VOP_WIN_SET ( vop , win , dst_alpha_ctl ,
DST_FACTOR_M0 ( ALPHA_SRC_INVERSE ) ) ;
val = SRC_ALPHA_EN ( 1 ) | SRC_COLOR_M0 ( ALPHA_SRC_PRE_MUL ) |
SRC_ALPHA_M0 ( ALPHA_STRAIGHT ) |
SRC_BLEND_M0 ( ALPHA_PER_PIX ) |
SRC_ALPHA_CAL_M0 ( ALPHA_NO_SATURATION ) |
SRC_FACTOR_M0 ( ALPHA_ONE ) ;
VOP_WIN_SET ( vop , win , src_alpha_ctl , val ) ;
} else {
VOP_WIN_SET ( vop , win , src_alpha_ctl , SRC_ALPHA_EN ( 0 ) ) ;
}
VOP_WIN_SET ( vop , win , enable , 1 ) ;
spin_unlock ( & vop - > reg_lock ) ;
}
2015-11-30 18:22:42 +08:00
static const struct drm_plane_helper_funcs plane_helper_funcs = {
. atomic_check = vop_plane_atomic_check ,
. atomic_update = vop_plane_atomic_update ,
. atomic_disable = vop_plane_atomic_disable ,
} ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
void vop_atomic_plane_reset ( struct drm_plane * plane )
2014-08-22 18:36:26 +08:00
{
2015-11-30 18:22:42 +08:00
struct vop_plane_state * vop_plane_state =
to_vop_plane_state ( plane - > state ) ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
if ( plane - > state & & plane - > state - > fb )
drm_framebuffer_unreference ( plane - > state - > fb ) ;
kfree ( vop_plane_state ) ;
vop_plane_state = kzalloc ( sizeof ( * vop_plane_state ) , GFP_KERNEL ) ;
if ( ! vop_plane_state )
return ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
plane - > state = & vop_plane_state - > base ;
plane - > state - > plane = plane ;
2014-08-22 18:36:26 +08:00
}
2015-11-30 18:22:42 +08:00
struct drm_plane_state *
vop_atomic_plane_duplicate_state ( struct drm_plane * plane )
2014-08-22 18:36:26 +08:00
{
2015-11-30 18:22:42 +08:00
struct vop_plane_state * old_vop_plane_state ;
struct vop_plane_state * vop_plane_state ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
if ( WARN_ON ( ! plane - > state ) )
return NULL ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
old_vop_plane_state = to_vop_plane_state ( plane - > state ) ;
vop_plane_state = kmemdup ( old_vop_plane_state ,
sizeof ( * vop_plane_state ) , GFP_KERNEL ) ;
if ( ! vop_plane_state )
return NULL ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
__drm_atomic_helper_plane_duplicate_state ( plane ,
& vop_plane_state - > base ) ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
return & vop_plane_state - > base ;
2014-08-22 18:36:26 +08:00
}
2015-11-30 18:22:42 +08:00
static void vop_atomic_plane_destroy_state ( struct drm_plane * plane ,
struct drm_plane_state * state )
2014-08-22 18:36:26 +08:00
{
2015-11-30 18:22:42 +08:00
struct vop_plane_state * vop_state = to_vop_plane_state ( state ) ;
__drm_atomic_helper_plane_destroy_state ( plane , state ) ;
kfree ( vop_state ) ;
2014-08-22 18:36:26 +08:00
}
static const struct drm_plane_funcs vop_plane_funcs = {
2015-11-30 18:22:42 +08:00
. update_plane = drm_atomic_helper_update_plane ,
. disable_plane = drm_atomic_helper_disable_plane ,
2014-08-22 18:36:26 +08:00
. destroy = vop_plane_destroy ,
2015-11-30 18:22:42 +08:00
. reset = vop_atomic_plane_reset ,
. atomic_duplicate_state = vop_atomic_plane_duplicate_state ,
. atomic_destroy_state = vop_atomic_plane_destroy_state ,
2014-08-22 18:36:26 +08:00
} ;
int rockchip_drm_crtc_mode_config ( struct drm_crtc * crtc ,
int connector_type ,
int out_mode )
{
struct vop * vop = to_vop ( crtc ) ;
2015-12-16 18:11:24 +08:00
if ( WARN_ON ( ! vop - > is_enabled ) )
return - EINVAL ;
switch ( connector_type ) {
case DRM_MODE_CONNECTOR_LVDS :
VOP_CTRL_SET ( vop , rgb_en , 1 ) ;
break ;
case DRM_MODE_CONNECTOR_eDP :
VOP_CTRL_SET ( vop , edp_en , 1 ) ;
break ;
case DRM_MODE_CONNECTOR_HDMIA :
VOP_CTRL_SET ( vop , hdmi_en , 1 ) ;
break ;
2016-01-06 16:12:54 +08:00
case DRM_MODE_CONNECTOR_DSI :
VOP_CTRL_SET ( vop , mipi_en , 1 ) ;
break ;
2015-12-16 18:11:24 +08:00
default :
DRM_ERROR ( " unsupport connector_type[%d] \n " , connector_type ) ;
return - EINVAL ;
} ;
VOP_CTRL_SET ( vop , out_mode , out_mode ) ;
2014-08-22 18:36:26 +08:00
return 0 ;
}
2015-01-07 16:16:18 +01:00
EXPORT_SYMBOL_GPL ( rockchip_drm_crtc_mode_config ) ;
2014-08-22 18:36:26 +08:00
static int vop_crtc_enable_vblank ( struct drm_crtc * crtc )
{
struct vop * vop = to_vop ( crtc ) ;
unsigned long flags ;
2015-11-30 18:22:42 +08:00
if ( WARN_ON ( ! vop - > is_enabled ) )
2014-08-22 18:36:26 +08:00
return - EPERM ;
spin_lock_irqsave ( & vop - > irq_lock , flags ) ;
2015-12-15 08:36:55 +08:00
VOP_INTR_SET_TYPE ( vop , enable , FS_INTR , 1 ) ;
2014-08-22 18:36:26 +08:00
spin_unlock_irqrestore ( & vop - > irq_lock , flags ) ;
return 0 ;
}
static void vop_crtc_disable_vblank ( struct drm_crtc * crtc )
{
struct vop * vop = to_vop ( crtc ) ;
unsigned long flags ;
2015-11-30 18:22:42 +08:00
if ( WARN_ON ( ! vop - > is_enabled ) )
2014-08-22 18:36:26 +08:00
return ;
2015-01-22 14:37:56 +08:00
2014-08-22 18:36:26 +08:00
spin_lock_irqsave ( & vop - > irq_lock , flags ) ;
2015-12-15 08:36:55 +08:00
VOP_INTR_SET_TYPE ( vop , enable , FS_INTR , 0 ) ;
2014-08-22 18:36:26 +08:00
spin_unlock_irqrestore ( & vop - > irq_lock , flags ) ;
}
2015-11-30 18:22:42 +08:00
static void vop_crtc_wait_for_update ( struct drm_crtc * crtc )
{
struct vop * vop = to_vop ( crtc ) ;
reinit_completion ( & vop - > wait_update_complete ) ;
WARN_ON ( ! wait_for_completion_timeout ( & vop - > wait_update_complete , 100 ) ) ;
}
2016-03-11 17:21:17 +00:00
static void vop_crtc_cancel_pending_vblank ( struct drm_crtc * crtc ,
struct drm_file * file_priv )
{
struct drm_device * drm = crtc - > dev ;
struct vop * vop = to_vop ( crtc ) ;
struct drm_pending_vblank_event * e ;
unsigned long flags ;
spin_lock_irqsave ( & drm - > event_lock , flags ) ;
e = vop - > event ;
if ( e & & e - > base . file_priv = = file_priv ) {
vop - > event = NULL ;
e - > base . destroy ( & e - > base ) ;
file_priv - > event_space + = sizeof ( e - > event ) ;
}
spin_unlock_irqrestore ( & drm - > event_lock , flags ) ;
}
2014-08-22 18:36:26 +08:00
static const struct rockchip_crtc_funcs private_crtc_funcs = {
. enable_vblank = vop_crtc_enable_vblank ,
. disable_vblank = vop_crtc_disable_vblank ,
2015-11-30 18:22:42 +08:00
. wait_for_update = vop_crtc_wait_for_update ,
2016-03-11 17:21:17 +00:00
. cancel_pending_vblank = vop_crtc_cancel_pending_vblank ,
2014-08-22 18:36:26 +08:00
} ;
static bool vop_crtc_mode_fixup ( struct drm_crtc * crtc ,
const struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
2016-01-06 12:03:53 +08:00
struct vop * vop = to_vop ( crtc ) ;
adjusted_mode - > clock =
clk_round_rate ( vop - > dclk , mode - > clock * 1000 ) / 1000 ;
2014-08-22 18:36:26 +08:00
return true ;
}
2015-11-30 18:22:42 +08:00
static void vop_crtc_enable ( struct drm_crtc * crtc )
2014-08-22 18:36:26 +08:00
{
struct vop * vop = to_vop ( crtc ) ;
2015-11-30 18:22:42 +08:00
struct drm_display_mode * adjusted_mode = & crtc - > state - > adjusted_mode ;
2014-08-22 18:36:26 +08:00
u16 hsync_len = adjusted_mode - > hsync_end - adjusted_mode - > hsync_start ;
u16 hdisplay = adjusted_mode - > hdisplay ;
u16 htotal = adjusted_mode - > htotal ;
u16 hact_st = adjusted_mode - > htotal - adjusted_mode - > hsync_start ;
u16 hact_end = hact_st + hdisplay ;
u16 vdisplay = adjusted_mode - > vdisplay ;
u16 vtotal = adjusted_mode - > vtotal ;
u16 vsync_len = adjusted_mode - > vsync_end - adjusted_mode - > vsync_start ;
u16 vact_st = adjusted_mode - > vtotal - adjusted_mode - > vsync_start ;
u16 vact_end = vact_st + vdisplay ;
uint32_t val ;
2015-11-30 18:22:42 +08:00
vop_enable ( crtc ) ;
2014-08-22 18:36:26 +08:00
/*
2015-12-16 18:08:17 +08:00
* If dclk rate is zero , mean that scanout is stop ,
* we don ' t need wait any more .
2014-08-22 18:36:26 +08:00
*/
2015-12-16 18:08:17 +08:00
if ( clk_get_rate ( vop - > dclk ) ) {
/*
* Rk3288 vop timing register is immediately , when configure
* display timing on display time , may cause tearing .
*
* Vop standby will take effect at end of current frame ,
* if dsp hold valid irq happen , it means standby complete .
*
* mode set :
* standby and wait complete - - > | - - - -
* | display time
* | - - - -
* | - - - > dsp hold irq
* configure display timing - - > |
* standby exit |
* | new frame start .
*/
reinit_completion ( & vop - > dsp_hold_completion ) ;
vop_dsp_hold_valid_irq_enable ( vop ) ;
spin_lock ( & vop - > reg_lock ) ;
VOP_CTRL_SET ( vop , standby , 1 ) ;
spin_unlock ( & vop - > reg_lock ) ;
wait_for_completion ( & vop - > dsp_hold_completion ) ;
vop_dsp_hold_valid_irq_disable ( vop ) ;
}
2014-08-22 18:36:26 +08:00
val = 0x8 ;
2015-01-22 11:15:02 +08:00
val | = ( adjusted_mode - > flags & DRM_MODE_FLAG_NHSYNC ) ? 0 : 1 ;
val | = ( adjusted_mode - > flags & DRM_MODE_FLAG_NVSYNC ) ? 0 : ( 1 < < 1 ) ;
2014-08-22 18:36:26 +08:00
VOP_CTRL_SET ( vop , pin_pol , val ) ;
VOP_CTRL_SET ( vop , htotal_pw , ( htotal < < 16 ) | hsync_len ) ;
val = hact_st < < 16 ;
val | = hact_end ;
VOP_CTRL_SET ( vop , hact_st_end , val ) ;
VOP_CTRL_SET ( vop , hpost_st_end , val ) ;
VOP_CTRL_SET ( vop , vtotal_pw , ( vtotal < < 16 ) | vsync_len ) ;
val = vact_st < < 16 ;
val | = vact_end ;
VOP_CTRL_SET ( vop , vact_st_end , val ) ;
VOP_CTRL_SET ( vop , vpost_st_end , val ) ;
clk_set_rate ( vop - > dclk , adjusted_mode - > clock * 1000 ) ;
2015-12-16 18:08:17 +08:00
VOP_CTRL_SET ( vop , standby , 0 ) ;
2014-08-22 18:36:26 +08:00
}
2015-11-30 18:22:42 +08:00
static void vop_crtc_atomic_flush ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
2014-08-22 18:36:26 +08:00
{
struct vop * vop = to_vop ( crtc ) ;
2015-11-30 18:22:42 +08:00
if ( WARN_ON ( ! vop - > is_enabled ) )
return ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
spin_lock ( & vop - > reg_lock ) ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
vop_cfg_done ( vop ) ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
spin_unlock ( & vop - > reg_lock ) ;
2014-08-22 18:36:26 +08:00
}
2015-11-30 18:22:42 +08:00
static void vop_crtc_atomic_begin ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
2014-08-22 18:36:26 +08:00
{
2015-11-30 18:22:42 +08:00
struct vop * vop = to_vop ( crtc ) ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
if ( crtc - > state - > event ) {
WARN_ON ( drm_crtc_vblank_get ( crtc ) ! = 0 ) ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
vop - > event = crtc - > state - > event ;
crtc - > state - > event = NULL ;
}
2014-08-22 18:36:26 +08:00
}
2015-11-30 18:22:42 +08:00
static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
. enable = vop_crtc_enable ,
. disable = vop_crtc_disable ,
. mode_fixup = vop_crtc_mode_fixup ,
. atomic_flush = vop_crtc_atomic_flush ,
. atomic_begin = vop_crtc_atomic_begin ,
} ;
2014-08-22 18:36:26 +08:00
static void vop_crtc_destroy ( struct drm_crtc * crtc )
{
drm_crtc_cleanup ( crtc ) ;
}
static const struct drm_crtc_funcs vop_crtc_funcs = {
2015-11-30 18:22:42 +08:00
. set_config = drm_atomic_helper_set_config ,
. page_flip = drm_atomic_helper_page_flip ,
2014-08-22 18:36:26 +08:00
. destroy = vop_crtc_destroy ,
2015-11-30 18:22:42 +08:00
. reset = drm_atomic_helper_crtc_reset ,
. atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_crtc_destroy_state ,
2014-08-22 18:36:26 +08:00
} ;
2015-11-30 18:22:42 +08:00
static bool vop_win_pending_is_complete ( struct vop_win * vop_win )
2014-08-22 18:36:26 +08:00
{
2015-11-30 18:22:42 +08:00
struct drm_plane * plane = & vop_win - > base ;
struct vop_plane_state * state = to_vop_plane_state ( plane - > state ) ;
dma_addr_t yrgb_mst ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
if ( ! state - > enable )
return VOP_WIN_GET ( vop_win - > vop , vop_win - > data , enable ) = = 0 ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
yrgb_mst = VOP_WIN_GET_YRGBADDR ( vop_win - > vop , vop_win - > data ) ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
return yrgb_mst = = state - > yrgb_mst ;
2014-08-22 18:36:26 +08:00
}
2015-11-30 18:22:42 +08:00
static void vop_handle_vblank ( struct vop * vop )
2014-08-22 18:36:26 +08:00
{
2015-11-30 18:22:42 +08:00
struct drm_device * drm = vop - > drm_dev ;
struct drm_crtc * crtc = & vop - > crtc ;
unsigned long flags ;
int i ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
for ( i = 0 ; i < vop - > data - > win_size ; i + + ) {
if ( ! vop_win_pending_is_complete ( & vop - > win [ i ] ) )
return ;
2014-08-22 18:36:26 +08:00
}
2015-11-30 18:22:42 +08:00
if ( vop - > event ) {
spin_lock_irqsave ( & drm - > event_lock , flags ) ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
drm_crtc_send_vblank_event ( crtc , vop - > event ) ;
drm_crtc_vblank_put ( crtc ) ;
vop - > event = NULL ;
2014-08-22 18:36:26 +08:00
2015-11-30 18:22:42 +08:00
spin_unlock_irqrestore ( & drm - > event_lock , flags ) ;
2014-08-22 18:36:26 +08:00
}
2015-11-30 18:22:42 +08:00
if ( ! completion_done ( & vop - > wait_update_complete ) )
complete ( & vop - > wait_update_complete ) ;
2014-08-22 18:36:26 +08:00
}
static irqreturn_t vop_isr ( int irq , void * data )
{
struct vop * vop = data ;
2015-11-23 15:21:08 +08:00
struct drm_crtc * crtc = & vop - > crtc ;
2015-12-15 08:36:55 +08:00
uint32_t active_irqs ;
2014-08-22 18:36:26 +08:00
unsigned long flags ;
2015-02-04 13:10:31 +08:00
int ret = IRQ_NONE ;
2014-08-22 18:36:26 +08:00
/*
2015-12-15 08:36:55 +08:00
* interrupt register has interrupt status , enable and clear bits , we
2014-08-22 18:36:26 +08:00
* must hold irq_lock to avoid a race with enable / disable_vblank ( ) .
*/
spin_lock_irqsave ( & vop - > irq_lock , flags ) ;
2015-12-15 08:36:55 +08:00
active_irqs = VOP_INTR_GET_TYPE ( vop , status , INTR_MASK ) ;
2014-08-22 18:36:26 +08:00
/* Clear all active interrupt sources */
if ( active_irqs )
2015-12-15 08:36:55 +08:00
VOP_INTR_SET_TYPE ( vop , clear , active_irqs , 1 ) ;
2014-08-22 18:36:26 +08:00
spin_unlock_irqrestore ( & vop - > irq_lock , flags ) ;
/* This is expected for vop iommu irqs, since the irq is shared */
if ( ! active_irqs )
return IRQ_NONE ;
2015-02-04 13:10:31 +08:00
if ( active_irqs & DSP_HOLD_VALID_INTR ) {
complete ( & vop - > dsp_hold_completion ) ;
active_irqs & = ~ DSP_HOLD_VALID_INTR ;
ret = IRQ_HANDLED ;
2014-08-22 18:36:26 +08:00
}
2015-02-04 13:10:31 +08:00
if ( active_irqs & FS_INTR ) {
2015-11-23 15:21:08 +08:00
drm_crtc_handle_vblank ( crtc ) ;
2015-11-30 18:22:42 +08:00
vop_handle_vblank ( vop ) ;
2015-02-04 13:10:31 +08:00
active_irqs & = ~ FS_INTR ;
2015-11-30 18:22:42 +08:00
ret = IRQ_HANDLED ;
2015-02-04 13:10:31 +08:00
}
2014-08-22 18:36:26 +08:00
2015-02-04 13:10:31 +08:00
/* Unhandled irqs are spurious. */
if ( active_irqs )
DRM_ERROR ( " Unknown VOP IRQs: %#02x \n " , active_irqs ) ;
return ret ;
2014-08-22 18:36:26 +08:00
}
static int vop_create_crtc ( struct vop * vop )
{
const struct vop_data * vop_data = vop - > data ;
struct device * dev = vop - > dev ;
struct drm_device * drm_dev = vop - > drm_dev ;
2016-03-07 14:00:52 -08:00
struct drm_plane * primary = NULL , * cursor = NULL , * plane , * tmp ;
2014-08-22 18:36:26 +08:00
struct drm_crtc * crtc = & vop - > crtc ;
struct device_node * port ;
int ret ;
int i ;
/*
* Create drm_plane for primary and cursor planes first , since we need
* to pass them to drm_crtc_init_with_planes , which sets the
* " possible_crtcs " to the newly initialized crtc .
*/
for ( i = 0 ; i < vop_data - > win_size ; i + + ) {
struct vop_win * vop_win = & vop - > win [ i ] ;
const struct vop_win_data * win_data = vop_win - > data ;
if ( win_data - > type ! = DRM_PLANE_TYPE_PRIMARY & &
win_data - > type ! = DRM_PLANE_TYPE_CURSOR )
continue ;
ret = drm_universal_plane_init ( vop - > drm_dev , & vop_win - > base ,
0 , & vop_plane_funcs ,
win_data - > phy - > data_formats ,
win_data - > phy - > nformats ,
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
win_data - > type , NULL ) ;
2014-08-22 18:36:26 +08:00
if ( ret ) {
DRM_ERROR ( " failed to initialize plane \n " ) ;
goto err_cleanup_planes ;
}
plane = & vop_win - > base ;
2015-11-30 18:22:42 +08:00
drm_plane_helper_add ( plane , & plane_helper_funcs ) ;
2014-08-22 18:36:26 +08:00
if ( plane - > type = = DRM_PLANE_TYPE_PRIMARY )
primary = plane ;
else if ( plane - > type = = DRM_PLANE_TYPE_CURSOR )
cursor = plane ;
}
ret = drm_crtc_init_with_planes ( drm_dev , crtc , primary , cursor ,
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
& vop_crtc_funcs , NULL ) ;
2014-08-22 18:36:26 +08:00
if ( ret )
2016-03-07 14:00:52 -08:00
goto err_cleanup_planes ;
2014-08-22 18:36:26 +08:00
drm_crtc_helper_add ( crtc , & vop_crtc_helper_funcs ) ;
/*
* Create drm_planes for overlay windows with possible_crtcs restricted
* to the newly created crtc .
*/
for ( i = 0 ; i < vop_data - > win_size ; i + + ) {
struct vop_win * vop_win = & vop - > win [ i ] ;
const struct vop_win_data * win_data = vop_win - > data ;
unsigned long possible_crtcs = 1 < < drm_crtc_index ( crtc ) ;
if ( win_data - > type ! = DRM_PLANE_TYPE_OVERLAY )
continue ;
ret = drm_universal_plane_init ( vop - > drm_dev , & vop_win - > base ,
possible_crtcs ,
& vop_plane_funcs ,
win_data - > phy - > data_formats ,
win_data - > phy - > nformats ,
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
win_data - > type , NULL ) ;
2014-08-22 18:36:26 +08:00
if ( ret ) {
DRM_ERROR ( " failed to initialize overlay plane \n " ) ;
goto err_cleanup_crtc ;
}
2015-11-30 18:22:42 +08:00
drm_plane_helper_add ( & vop_win - > base , & plane_helper_funcs ) ;
2014-08-22 18:36:26 +08:00
}
port = of_get_child_by_name ( dev - > of_node , " port " ) ;
if ( ! port ) {
DRM_ERROR ( " no port node found in %s \n " ,
dev - > of_node - > full_name ) ;
2016-03-07 14:00:52 -08:00
ret = - ENOENT ;
2014-08-22 18:36:26 +08:00
goto err_cleanup_crtc ;
}
2015-02-04 13:10:31 +08:00
init_completion ( & vop - > dsp_hold_completion ) ;
2015-11-30 18:22:42 +08:00
init_completion ( & vop - > wait_update_complete ) ;
2014-08-22 18:36:26 +08:00
crtc - > port = port ;
2015-11-23 15:21:08 +08:00
rockchip_register_crtc_funcs ( crtc , & private_crtc_funcs ) ;
2014-08-22 18:36:26 +08:00
return 0 ;
err_cleanup_crtc :
drm_crtc_cleanup ( crtc ) ;
err_cleanup_planes :
2016-03-07 14:00:52 -08:00
list_for_each_entry_safe ( plane , tmp , & drm_dev - > mode_config . plane_list ,
head )
2014-08-22 18:36:26 +08:00
drm_plane_cleanup ( plane ) ;
return ret ;
}
static void vop_destroy_crtc ( struct vop * vop )
{
struct drm_crtc * crtc = & vop - > crtc ;
2016-03-07 14:00:52 -08:00
struct drm_device * drm_dev = vop - > drm_dev ;
struct drm_plane * plane , * tmp ;
2014-08-22 18:36:26 +08:00
2015-11-23 15:21:08 +08:00
rockchip_unregister_crtc_funcs ( crtc ) ;
2014-08-22 18:36:26 +08:00
of_node_put ( crtc - > port ) ;
2016-03-07 14:00:52 -08:00
/*
* We need to cleanup the planes now . Why ?
*
* The planes are " &vop->win[i].base " . That means the memory is
* all part of the big " struct vop " chunk of memory . That memory
* was devm allocated and associated with this component . We need to
* free it ourselves before vop_unbind ( ) finishes .
*/
list_for_each_entry_safe ( plane , tmp , & drm_dev - > mode_config . plane_list ,
head )
vop_plane_destroy ( plane ) ;
/*
* Destroy CRTC after vop_plane_destroy ( ) since vop_disable_plane ( )
* references the CRTC .
*/
2014-08-22 18:36:26 +08:00
drm_crtc_cleanup ( crtc ) ;
}
static int vop_initial ( struct vop * vop )
{
const struct vop_data * vop_data = vop - > data ;
const struct vop_reg_data * init_table = vop_data - > init_table ;
struct reset_control * ahb_rst ;
int i , ret ;
vop - > hclk = devm_clk_get ( vop - > dev , " hclk_vop " ) ;
if ( IS_ERR ( vop - > hclk ) ) {
dev_err ( vop - > dev , " failed to get hclk source \n " ) ;
return PTR_ERR ( vop - > hclk ) ;
}
vop - > aclk = devm_clk_get ( vop - > dev , " aclk_vop " ) ;
if ( IS_ERR ( vop - > aclk ) ) {
dev_err ( vop - > dev , " failed to get aclk source \n " ) ;
return PTR_ERR ( vop - > aclk ) ;
}
vop - > dclk = devm_clk_get ( vop - > dev , " dclk_vop " ) ;
if ( IS_ERR ( vop - > dclk ) ) {
dev_err ( vop - > dev , " failed to get dclk source \n " ) ;
return PTR_ERR ( vop - > dclk ) ;
}
ret = clk_prepare ( vop - > dclk ) ;
if ( ret < 0 ) {
dev_err ( vop - > dev , " failed to prepare dclk \n " ) ;
2015-11-06 13:22:24 +01:00
return ret ;
2014-08-22 18:36:26 +08:00
}
2015-11-06 13:22:24 +01:00
/* Enable both the hclk and aclk to setup the vop */
ret = clk_prepare_enable ( vop - > hclk ) ;
2014-08-22 18:36:26 +08:00
if ( ret < 0 ) {
2015-11-06 13:22:24 +01:00
dev_err ( vop - > dev , " failed to prepare/enable hclk \n " ) ;
2014-08-22 18:36:26 +08:00
goto err_unprepare_dclk ;
}
2015-11-06 13:22:24 +01:00
ret = clk_prepare_enable ( vop - > aclk ) ;
2014-08-22 18:36:26 +08:00
if ( ret < 0 ) {
2015-11-06 13:22:24 +01:00
dev_err ( vop - > dev , " failed to prepare/enable aclk \n " ) ;
goto err_disable_hclk ;
2014-08-22 18:36:26 +08:00
}
2015-11-06 13:22:24 +01:00
2014-08-22 18:36:26 +08:00
/*
* do hclk_reset , reset all vop registers .
*/
ahb_rst = devm_reset_control_get ( vop - > dev , " ahb " ) ;
if ( IS_ERR ( ahb_rst ) ) {
dev_err ( vop - > dev , " failed to get ahb reset \n " ) ;
ret = PTR_ERR ( ahb_rst ) ;
2015-11-06 13:22:24 +01:00
goto err_disable_aclk ;
2014-08-22 18:36:26 +08:00
}
reset_control_assert ( ahb_rst ) ;
usleep_range ( 10 , 20 ) ;
reset_control_deassert ( ahb_rst ) ;
memcpy ( vop - > regsbak , vop - > regs , vop - > len ) ;
for ( i = 0 ; i < vop_data - > table_size ; i + + )
vop_writel ( vop , init_table [ i ] . offset , init_table [ i ] . value ) ;
for ( i = 0 ; i < vop_data - > win_size ; i + + ) {
const struct vop_win_data * win = & vop_data - > win [ i ] ;
VOP_WIN_SET ( vop , win , enable , 0 ) ;
}
vop_cfg_done ( vop ) ;
/*
* do dclk_reset , let all config take affect .
*/
vop - > dclk_rst = devm_reset_control_get ( vop - > dev , " dclk " ) ;
if ( IS_ERR ( vop - > dclk_rst ) ) {
dev_err ( vop - > dev , " failed to get dclk reset \n " ) ;
ret = PTR_ERR ( vop - > dclk_rst ) ;
2015-11-06 13:22:24 +01:00
goto err_disable_aclk ;
2014-08-22 18:36:26 +08:00
}
reset_control_assert ( vop - > dclk_rst ) ;
usleep_range ( 10 , 20 ) ;
reset_control_deassert ( vop - > dclk_rst ) ;
clk_disable ( vop - > hclk ) ;
2015-11-06 13:22:24 +01:00
clk_disable ( vop - > aclk ) ;
2014-08-22 18:36:26 +08:00
2015-01-22 14:37:56 +08:00
vop - > is_enabled = false ;
2014-08-22 18:36:26 +08:00
return 0 ;
2015-11-06 13:22:24 +01:00
err_disable_aclk :
clk_disable_unprepare ( vop - > aclk ) ;
2014-08-22 18:36:26 +08:00
err_disable_hclk :
2015-11-06 13:22:24 +01:00
clk_disable_unprepare ( vop - > hclk ) ;
2014-08-22 18:36:26 +08:00
err_unprepare_dclk :
clk_unprepare ( vop - > dclk ) ;
return ret ;
}
/*
* Initialize the vop - > win array elements .
*/
static void vop_win_init ( struct vop * vop )
{
const struct vop_data * vop_data = vop - > data ;
unsigned int i ;
for ( i = 0 ; i < vop_data - > win_size ; i + + ) {
struct vop_win * vop_win = & vop - > win [ i ] ;
const struct vop_win_data * win_data = & vop_data - > win [ i ] ;
vop_win - > data = win_data ;
vop_win - > vop = vop ;
}
}
static int vop_bind ( struct device * dev , struct device * master , void * data )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
const struct vop_data * vop_data ;
struct drm_device * drm_dev = data ;
struct vop * vop ;
struct resource * res ;
size_t alloc_size ;
2015-04-20 01:00:53 +02:00
int ret , irq ;
2014-08-22 18:36:26 +08:00
2015-12-15 08:58:26 +08:00
vop_data = of_device_get_match_data ( dev ) ;
2014-08-22 18:36:26 +08:00
if ( ! vop_data )
return - ENODEV ;
/* Allocate vop struct and its vop_win array */
alloc_size = sizeof ( * vop ) + sizeof ( * vop - > win ) * vop_data - > win_size ;
vop = devm_kzalloc ( dev , alloc_size , GFP_KERNEL ) ;
if ( ! vop )
return - ENOMEM ;
vop - > dev = dev ;
vop - > data = vop_data ;
vop - > drm_dev = drm_dev ;
dev_set_drvdata ( dev , vop ) ;
vop_win_init ( vop ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
vop - > len = resource_size ( res ) ;
vop - > regs = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( vop - > regs ) )
return PTR_ERR ( vop - > regs ) ;
vop - > regsbak = devm_kzalloc ( dev , vop - > len , GFP_KERNEL ) ;
if ( ! vop - > regsbak )
return - ENOMEM ;
ret = vop_initial ( vop ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " cannot initial vop dev - err %d \n " , ret ) ;
return ret ;
}
2015-04-20 01:00:53 +02:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
2014-08-22 18:36:26 +08:00
dev_err ( dev , " cannot find irq for vop \n " ) ;
2015-04-20 01:00:53 +02:00
return irq ;
2014-08-22 18:36:26 +08:00
}
2015-04-20 01:00:53 +02:00
vop - > irq = ( unsigned int ) irq ;
2014-08-22 18:36:26 +08:00
spin_lock_init ( & vop - > reg_lock ) ;
spin_lock_init ( & vop - > irq_lock ) ;
mutex_init ( & vop - > vsync_mutex ) ;
2015-11-30 18:22:42 +08:00
ret = devm_request_irq ( dev , vop - > irq , vop_isr ,
IRQF_SHARED , dev_name ( dev ) , vop ) ;
2014-08-22 18:36:26 +08:00
if ( ret )
return ret ;
/* IRQ is initially disabled; it gets enabled in power_on */
disable_irq ( vop - > irq ) ;
ret = vop_create_crtc ( vop ) ;
if ( ret )
return ret ;
pm_runtime_enable ( & pdev - > dev ) ;
return 0 ;
}
static void vop_unbind ( struct device * dev , struct device * master , void * data )
{
struct vop * vop = dev_get_drvdata ( dev ) ;
pm_runtime_disable ( dev ) ;
vop_destroy_crtc ( vop ) ;
}
2015-12-15 08:58:26 +08:00
const struct component_ops vop_component_ops = {
2014-08-22 18:36:26 +08:00
. bind = vop_bind ,
. unbind = vop_unbind ,
} ;
2015-12-31 13:40:11 +11:00
EXPORT_SYMBOL_GPL ( vop_component_ops ) ;