2016-01-04 18:36:34 +01:00
/*
* Copyright ( c ) 2015 MediaTek Inc .
*
* 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 .
*/
# include <drm/drmP.h>
# include <linux/clk.h>
# include <linux/component.h>
# include <linux/of_device.h>
# include <linux/of_irq.h>
# include <linux/platform_device.h>
# include "mtk_drm_crtc.h"
# include "mtk_drm_ddp_comp.h"
# define DISP_REG_OVL_INTEN 0x0004
# define OVL_FME_CPL_INT BIT(1)
# define DISP_REG_OVL_INTSTA 0x0008
# define DISP_REG_OVL_EN 0x000c
# define DISP_REG_OVL_RST 0x0014
# define DISP_REG_OVL_ROI_SIZE 0x0020
# define DISP_REG_OVL_ROI_BGCLR 0x0028
# define DISP_REG_OVL_SRC_CON 0x002c
# define DISP_REG_OVL_CON(n) (0x0030 + 0x20 * (n))
# define DISP_REG_OVL_SRC_SIZE(n) (0x0038 + 0x20 * (n))
# define DISP_REG_OVL_OFFSET(n) (0x003c + 0x20 * (n))
# define DISP_REG_OVL_PITCH(n) (0x0044 + 0x20 * (n))
# define DISP_REG_OVL_RDMA_CTRL(n) (0x00c0 + 0x20 * (n))
# define DISP_REG_OVL_RDMA_GMC(n) (0x00c8 + 0x20 * (n))
2017-03-31 19:30:39 +08:00
# define DISP_REG_OVL_ADDR_MT2701 0x0040
2017-03-31 19:30:30 +08:00
# define DISP_REG_OVL_ADDR_MT8173 0x0f40
# define DISP_REG_OVL_ADDR(ovl, n) ((ovl)->data->addr + 0x20 * (n))
2016-01-04 18:36:34 +01:00
# define OVL_RDMA_MEM_GMC 0x40402020
# define OVL_CON_BYTE_SWAP BIT(24)
2017-01-24 12:40:58 +08:00
# define OVL_CON_MTX_YUV_TO_RGB (6 << 16)
2017-03-31 19:30:30 +08:00
# define OVL_CON_CLRFMT_RGB (1 << 12)
2016-01-04 18:36:34 +01:00
# define OVL_CON_CLRFMT_RGBA8888 (2 << 12)
# define OVL_CON_CLRFMT_ARGB8888 (3 << 12)
2017-01-24 12:40:58 +08:00
# define OVL_CON_CLRFMT_UYVY (4 << 12)
# define OVL_CON_CLRFMT_YUYV (5 << 12)
2017-03-31 19:30:30 +08:00
# define OVL_CON_CLRFMT_RGB565(ovl) ((ovl)->data->fmt_rgb565_is_0 ? \
0 : OVL_CON_CLRFMT_RGB )
# define OVL_CON_CLRFMT_RGB888(ovl) ((ovl)->data->fmt_rgb565_is_0 ? \
OVL_CON_CLRFMT_RGB : 0 )
2016-01-04 18:36:34 +01:00
# define OVL_CON_AEN BIT(8)
# define OVL_CON_ALPHA 0xff
2017-03-31 19:30:30 +08:00
struct mtk_disp_ovl_data {
unsigned int addr ;
bool fmt_rgb565_is_0 ;
} ;
2016-01-04 18:36:34 +01:00
/**
* struct mtk_disp_ovl - DISP_OVL driver structure
* @ ddp_comp - structure containing type enum and hardware resources
* @ crtc - associated crtc to report vblank events to
*/
struct mtk_disp_ovl {
struct mtk_ddp_comp ddp_comp ;
struct drm_crtc * crtc ;
2017-03-31 19:30:30 +08:00
const struct mtk_disp_ovl_data * data ;
2016-01-04 18:36:34 +01:00
} ;
2017-03-31 19:30:29 +08:00
static inline struct mtk_disp_ovl * comp_to_ovl ( struct mtk_ddp_comp * comp )
{
return container_of ( comp , struct mtk_disp_ovl , ddp_comp ) ;
}
2016-01-04 18:36:34 +01:00
static irqreturn_t mtk_disp_ovl_irq_handler ( int irq , void * dev_id )
{
struct mtk_disp_ovl * priv = dev_id ;
struct mtk_ddp_comp * ovl = & priv - > ddp_comp ;
/* Clear frame completion interrupt */
writel ( 0x0 , ovl - > regs + DISP_REG_OVL_INTSTA ) ;
if ( ! priv - > crtc )
return IRQ_NONE ;
mtk_crtc_ddp_irq ( priv - > crtc , ovl ) ;
return IRQ_HANDLED ;
}
static void mtk_ovl_enable_vblank ( struct mtk_ddp_comp * comp ,
struct drm_crtc * crtc )
{
2017-03-31 19:30:29 +08:00
struct mtk_disp_ovl * ovl = comp_to_ovl ( comp ) ;
2016-01-04 18:36:34 +01:00
2017-03-31 19:30:29 +08:00
ovl - > crtc = crtc ;
2016-09-29 11:29:49 +08:00
writel ( 0x0 , comp - > regs + DISP_REG_OVL_INTSTA ) ;
2016-01-04 18:36:34 +01:00
writel_relaxed ( OVL_FME_CPL_INT , comp - > regs + DISP_REG_OVL_INTEN ) ;
}
static void mtk_ovl_disable_vblank ( struct mtk_ddp_comp * comp )
{
2017-03-31 19:30:29 +08:00
struct mtk_disp_ovl * ovl = comp_to_ovl ( comp ) ;
2016-01-04 18:36:34 +01:00
2017-03-31 19:30:29 +08:00
ovl - > crtc = NULL ;
2016-01-04 18:36:34 +01:00
writel_relaxed ( 0x0 , comp - > regs + DISP_REG_OVL_INTEN ) ;
}
static void mtk_ovl_start ( struct mtk_ddp_comp * comp )
{
writel_relaxed ( 0x1 , comp - > regs + DISP_REG_OVL_EN ) ;
}
static void mtk_ovl_stop ( struct mtk_ddp_comp * comp )
{
writel_relaxed ( 0x0 , comp - > regs + DISP_REG_OVL_EN ) ;
}
static void mtk_ovl_config ( struct mtk_ddp_comp * comp , unsigned int w ,
2016-07-28 10:22:55 +08:00
unsigned int h , unsigned int vrefresh ,
unsigned int bpc )
2016-01-04 18:36:34 +01:00
{
if ( w ! = 0 & & h ! = 0 )
writel_relaxed ( h < < 16 | w , comp - > regs + DISP_REG_OVL_ROI_SIZE ) ;
writel_relaxed ( 0x0 , comp - > regs + DISP_REG_OVL_ROI_BGCLR ) ;
writel ( 0x1 , comp - > regs + DISP_REG_OVL_RST ) ;
writel ( 0x0 , comp - > regs + DISP_REG_OVL_RST ) ;
}
2018-08-09 10:15:45 +08:00
static unsigned int mtk_ovl_layer_nr ( struct mtk_ddp_comp * comp )
{
return 4 ;
}
2016-01-04 18:36:34 +01:00
static void mtk_ovl_layer_on ( struct mtk_ddp_comp * comp , unsigned int idx )
{
unsigned int reg ;
writel ( 0x1 , comp - > regs + DISP_REG_OVL_RDMA_CTRL ( idx ) ) ;
writel ( OVL_RDMA_MEM_GMC , comp - > regs + DISP_REG_OVL_RDMA_GMC ( idx ) ) ;
reg = readl ( comp - > regs + DISP_REG_OVL_SRC_CON ) ;
reg = reg | BIT ( idx ) ;
writel ( reg , comp - > regs + DISP_REG_OVL_SRC_CON ) ;
}
static void mtk_ovl_layer_off ( struct mtk_ddp_comp * comp , unsigned int idx )
{
unsigned int reg ;
reg = readl ( comp - > regs + DISP_REG_OVL_SRC_CON ) ;
reg = reg & ~ BIT ( idx ) ;
writel ( reg , comp - > regs + DISP_REG_OVL_SRC_CON ) ;
writel ( 0x0 , comp - > regs + DISP_REG_OVL_RDMA_CTRL ( idx ) ) ;
}
2017-03-31 19:30:30 +08:00
static unsigned int ovl_fmt_convert ( struct mtk_disp_ovl * ovl , unsigned int fmt )
2016-01-04 18:36:34 +01:00
{
2018-08-09 10:15:42 +08:00
/* The return value in switch "MEM_MODE_INPUT_FORMAT_XXX"
* is defined in mediatek HW data sheet .
* The alphabet order in XXX is no relation to data
* arrangement in memory .
*/
2016-01-04 18:36:34 +01:00
switch ( fmt ) {
default :
case DRM_FORMAT_RGB565 :
2017-03-31 19:30:30 +08:00
return OVL_CON_CLRFMT_RGB565 ( ovl ) ;
2016-01-04 18:36:34 +01:00
case DRM_FORMAT_BGR565 :
2017-03-31 19:30:30 +08:00
return OVL_CON_CLRFMT_RGB565 ( ovl ) | OVL_CON_BYTE_SWAP ;
2016-01-04 18:36:34 +01:00
case DRM_FORMAT_RGB888 :
2017-03-31 19:30:30 +08:00
return OVL_CON_CLRFMT_RGB888 ( ovl ) ;
2016-01-04 18:36:34 +01:00
case DRM_FORMAT_BGR888 :
2017-03-31 19:30:30 +08:00
return OVL_CON_CLRFMT_RGB888 ( ovl ) | OVL_CON_BYTE_SWAP ;
2016-01-04 18:36:34 +01:00
case DRM_FORMAT_RGBX8888 :
case DRM_FORMAT_RGBA8888 :
return OVL_CON_CLRFMT_ARGB8888 ;
case DRM_FORMAT_BGRX8888 :
case DRM_FORMAT_BGRA8888 :
return OVL_CON_CLRFMT_ARGB8888 | OVL_CON_BYTE_SWAP ;
case DRM_FORMAT_XRGB8888 :
case DRM_FORMAT_ARGB8888 :
return OVL_CON_CLRFMT_RGBA8888 ;
case DRM_FORMAT_XBGR8888 :
case DRM_FORMAT_ABGR8888 :
return OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP ;
2017-01-24 12:40:58 +08:00
case DRM_FORMAT_UYVY :
return OVL_CON_CLRFMT_UYVY | OVL_CON_MTX_YUV_TO_RGB ;
case DRM_FORMAT_YUYV :
return OVL_CON_CLRFMT_YUYV | OVL_CON_MTX_YUV_TO_RGB ;
2016-01-04 18:36:34 +01:00
}
}
static void mtk_ovl_layer_config ( struct mtk_ddp_comp * comp , unsigned int idx ,
struct mtk_plane_state * state )
{
2017-03-31 19:30:30 +08:00
struct mtk_disp_ovl * ovl = comp_to_ovl ( comp ) ;
2016-01-04 18:36:34 +01:00
struct mtk_plane_pending_state * pending = & state - > pending ;
unsigned int addr = pending - > addr ;
unsigned int pitch = pending - > pitch & 0xffff ;
unsigned int fmt = pending - > format ;
unsigned int offset = ( pending - > y < < 16 ) | pending - > x ;
unsigned int src_size = ( pending - > height < < 16 ) | pending - > width ;
unsigned int con ;
if ( ! pending - > enable )
mtk_ovl_layer_off ( comp , idx ) ;
2017-03-31 19:30:30 +08:00
con = ovl_fmt_convert ( ovl , fmt ) ;
2016-01-04 18:36:34 +01:00
if ( idx ! = 0 )
con | = OVL_CON_AEN | OVL_CON_ALPHA ;
writel_relaxed ( con , comp - > regs + DISP_REG_OVL_CON ( idx ) ) ;
writel_relaxed ( pitch , comp - > regs + DISP_REG_OVL_PITCH ( idx ) ) ;
writel_relaxed ( src_size , comp - > regs + DISP_REG_OVL_SRC_SIZE ( idx ) ) ;
writel_relaxed ( offset , comp - > regs + DISP_REG_OVL_OFFSET ( idx ) ) ;
2017-03-31 19:30:30 +08:00
writel_relaxed ( addr , comp - > regs + DISP_REG_OVL_ADDR ( ovl , idx ) ) ;
2016-01-04 18:36:34 +01:00
if ( pending - > enable )
mtk_ovl_layer_on ( comp , idx ) ;
}
static const struct mtk_ddp_comp_funcs mtk_disp_ovl_funcs = {
. config = mtk_ovl_config ,
. start = mtk_ovl_start ,
. stop = mtk_ovl_stop ,
. enable_vblank = mtk_ovl_enable_vblank ,
. disable_vblank = mtk_ovl_disable_vblank ,
2018-08-09 10:15:45 +08:00
. layer_nr = mtk_ovl_layer_nr ,
2016-01-04 18:36:34 +01:00
. layer_on = mtk_ovl_layer_on ,
. layer_off = mtk_ovl_layer_off ,
. layer_config = mtk_ovl_layer_config ,
} ;
static int mtk_disp_ovl_bind ( struct device * dev , struct device * master ,
void * data )
{
struct mtk_disp_ovl * priv = dev_get_drvdata ( dev ) ;
struct drm_device * drm_dev = data ;
int ret ;
ret = mtk_ddp_comp_register ( drm_dev , & priv - > ddp_comp ) ;
if ( ret < 0 ) {
2017-07-18 16:43:04 -05:00
dev_err ( dev , " Failed to register component %pOF: %d \n " ,
dev - > of_node , ret ) ;
2016-01-04 18:36:34 +01:00
return ret ;
}
return 0 ;
}
static void mtk_disp_ovl_unbind ( struct device * dev , struct device * master ,
void * data )
{
struct mtk_disp_ovl * priv = dev_get_drvdata ( dev ) ;
struct drm_device * drm_dev = data ;
mtk_ddp_comp_unregister ( drm_dev , & priv - > ddp_comp ) ;
}
static const struct component_ops mtk_disp_ovl_component_ops = {
. bind = mtk_disp_ovl_bind ,
. unbind = mtk_disp_ovl_unbind ,
} ;
static int mtk_disp_ovl_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct mtk_disp_ovl * priv ;
int comp_id ;
int irq ;
int ret ;
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
return irq ;
comp_id = mtk_ddp_comp_get_id ( dev - > of_node , MTK_DISP_OVL ) ;
if ( comp_id < 0 ) {
dev_err ( dev , " Failed to identify by alias: %d \n " , comp_id ) ;
return comp_id ;
}
ret = mtk_ddp_comp_init ( dev , dev - > of_node , & priv - > ddp_comp , comp_id ,
& mtk_disp_ovl_funcs ) ;
if ( ret ) {
dev_err ( dev , " Failed to initialize component: %d \n " , ret ) ;
return ret ;
}
2017-03-31 19:30:30 +08:00
priv - > data = of_device_get_match_data ( dev ) ;
2016-01-04 18:36:34 +01:00
platform_set_drvdata ( pdev , priv ) ;
2016-11-18 11:06:10 +01:00
ret = devm_request_irq ( dev , irq , mtk_disp_ovl_irq_handler ,
IRQF_TRIGGER_NONE , dev_name ( dev ) , priv ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to request irq %d: %d \n " , irq , ret ) ;
return ret ;
}
2016-01-04 18:36:34 +01:00
ret = component_add ( dev , & mtk_disp_ovl_component_ops ) ;
if ( ret )
dev_err ( dev , " Failed to add component: %d \n " , ret ) ;
return ret ;
}
static int mtk_disp_ovl_remove ( struct platform_device * pdev )
{
component_del ( & pdev - > dev , & mtk_disp_ovl_component_ops ) ;
return 0 ;
}
2017-03-31 19:30:39 +08:00
static const struct mtk_disp_ovl_data mt2701_ovl_driver_data = {
. addr = DISP_REG_OVL_ADDR_MT2701 ,
. fmt_rgb565_is_0 = false ,
} ;
2017-03-31 19:30:30 +08:00
static const struct mtk_disp_ovl_data mt8173_ovl_driver_data = {
. addr = DISP_REG_OVL_ADDR_MT8173 ,
. fmt_rgb565_is_0 = true ,
} ;
2016-01-04 18:36:34 +01:00
static const struct of_device_id mtk_disp_ovl_driver_dt_match [ ] = {
2017-03-31 19:30:39 +08:00
{ . compatible = " mediatek,mt2701-disp-ovl " ,
. data = & mt2701_ovl_driver_data } ,
2017-03-31 19:30:30 +08:00
{ . compatible = " mediatek,mt8173-disp-ovl " ,
. data = & mt8173_ovl_driver_data } ,
2016-01-04 18:36:34 +01:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , mtk_disp_ovl_driver_dt_match ) ;
struct platform_driver mtk_disp_ovl_driver = {
. probe = mtk_disp_ovl_probe ,
. remove = mtk_disp_ovl_remove ,
. driver = {
. name = " mediatek-disp-ovl " ,
. owner = THIS_MODULE ,
. of_match_table = mtk_disp_ovl_driver_dt_match ,
} ,
} ;