rk3288 chromium: drm grafic fb support for x11 mali gpu

This commit is contained in:
yzq 2014-03-26 11:48:43 +08:00
parent b21384df76
commit bdafdac384
50 changed files with 11489 additions and 4 deletions

View File

@ -201,6 +201,8 @@ config DRM_SAVAGE
source "drivers/gpu/drm/exynos/Kconfig"
source "drivers/gpu/drm/rockchip/Kconfig"
source "drivers/gpu/drm/vmwgfx/Kconfig"
source "drivers/gpu/drm/gma500/Kconfig"

View File

@ -45,6 +45,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
obj-$(CONFIG_DRM_GMA500) += gma500/
obj-$(CONFIG_DRM_UDL) += udl/
obj-$(CONFIG_DRM_AST) += ast/

View File

@ -3461,7 +3461,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
ret = -ENOSPC;
goto out;
}
fb->pixel_format = crtc->fb->pixel_format;
if (crtc->fb->pixel_format != fb->pixel_format) {
DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
ret = -EINVAL;

View File

@ -37,7 +37,7 @@
#include <drm/drmP.h>
#include <drm/drm_core.h>
unsigned int drm_debug = 0; /* 1 to enable debug output */
unsigned int drm_debug = 0xf; /* 1 to enable debug output */
EXPORT_SYMBOL(drm_debug);
unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */

View File

@ -0,0 +1,54 @@
config DRM_ROCKCHIP
tristate "DRM Support for ROCKCHIP "
depends on DRM && ARCH_MULTIPLATFORM
select DRM_KMS_HELPER
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
help
Choose this option if you have a ROCKCHIP soc chipset.
If M is selected the module will be called rockchipdrm.
config DRM_ROCKCHIP_IOMMU
bool "ROCKCHIP DRM IOMMU Support"
depends on DRM_ROCKCHIP && ARM_DMA_USE_IOMMU
help
Choose this option if you want to use IOMMU feature for DRM.
config DRM_ROCKCHIP_DMABUF
bool "ROCKCHIP DRM DMABUF"
depends on DRM_ROCKCHIP
help
Choose this option if you want to use DMABUF feature for DRM.
config DRM_RK3188_FIMD
bool "RK3188 DRM FIMD"
depends on OF && DRM_ROCKCHIP
select FB_MODE_HELPERS
select VIDEOMODE_HELPERS
help
Choose this option if you want to use Rockchip FIMD for DRM.
config DRM_RK3288_FIMD
bool "RK3288 DRM FIMD"
depends on OF && DRM_ROCKCHIP
select FB_MODE_HELPERS
select VIDEOMODE_HELPERS
help
Choose this option if you want to use Rockchip FIMD for DRM.
config DRM_ROCKCHIP_HDMI
bool "Rockchip DRM HDMI"
depends on DRM_ROCKCHIP
help
Choose this option if you want to use Rockchip HDMI for DRM.
config DRM_ROCKCHIP_VIDI
bool "Rockchip DRM Virtual Display"
depends on DRM_ROCKCHIP
help
Choose this option if you want to use rockchip VIDI for DRM.
source "drivers/gpu/drm/rockchip/screen/Kconfig"

View File

@ -0,0 +1,22 @@
#
# Makefile for the drm device driver. This driver provides support for the
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_encoder.o rockchip_drm_connector.o \
rockchip_drm_crtc.o rockchip_drm_fbdev.o rockchip_drm_fb.o \
rockchip_drm_buf.o rockchip_drm_gem.o rockchip_drm_core.o \
rockchip_drm_plane.o
rockchipdrm-$(CONFIG_DRM_ROCKCHIP_IOMMU) += rockchip_drm_iommu.o
rockchipdrm-$(CONFIG_DRM_ROCKCHIP_DMABUF) += rockchip_drm_dmabuf.o
rockchipdrm-$(CONFIG_DRM_RK3188_FIMD) += rk3188_drm_fimd.o
rockchipdrm-$(CONFIG_DRM_RK3288_FIMD) += rk3288_drm_fimd.o
rockchipdrm-$(CONFIG_DRM_ROCKCHIP_HDMI) += rockchip_hdmi.o rockchip_mixer.o \
rockchip_ddc.o rockchip_hdmiphy.o \
rockchip_drm_hdmi.o
rockchipdrm-$(CONFIG_DRM_ROCKCHIP_VIDI) += rockchip_drm_vidi.o
obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
obj-y += screen/
obj-y += transmitter/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,386 @@
/********************************************************************
** display output interface supported by rockchip lcdc *
********************************************************************/
/* */
#define OUT_P888 0 //24bit screen,connect to lcdc D0~D23
#define OUT_P666 1 //18bit screen,connect to lcdc D0~D17
#define OUT_P565 2
#define OUT_S888x 4
#define OUT_CCIR656 6
#define OUT_S888 8
#define OUT_S888DUMY 12
#define OUT_RGB_AAA 15
#define OUT_P16BPP4 24
#define OUT_D888_P666 0x21 //18bit screen,connect to lcdc D2~D7, D10~D15, D18~D23
#define OUT_D888_P565 0x22
/*******************register definition**********************/
#define SYS_CTRL (0x00)
#define m_WIN0_EN (1<<0)
#define m_WIN1_EN (1<<1)
#define m_HWC_EN (1<<2)
#define m_WIN0_FORMAT (7<<3)
#define m_WIN1_FORMAT (7<<6)
#define m_HWC_COLOR_MODE (1<<9)
#define m_HWC_SIZE (1<<10)
#define m_WIN0_3D_EN (1<<11)
#define m_WIN0_3D_MODE (7<<12)
#define m_WIN0_RB_SWAP (1<<15)
#define m_WIN0_ALPHA_SWAP (1<<16)
#define m_WIN0_Y8_SWAP (1<<17)
#define m_WIN0_UV_SWAP (1<<18)
#define m_WIN1_RB_SWAP (1<<19)
#define m_WIN1_ALPHA_SWAP (1<<20)
#define m_WIN1_BL_SWAP (1<<21)
#define m_WIN0_OTSD_DISABLE (1<<22)
#define m_WIN1_OTSD_DISABLE (1<<23)
#define m_DMA_BURST_LENGTH (3<<24)
#define m_HWC_LODAD_EN (1<<26)
#define m_WIN1_LUT_EN (1<<27)
#define m_DSP_LUT_EN (1<<28)
#define m_DMA_STOP (1<<29)
#define m_LCDC_STANDBY (1<<30)
#define m_AUTO_GATING_EN (1<<31)
#define v_WIN0_EN(x) (((x)&1)<<0)
#define v_WIN1_EN(x) (((x)&1)<<1)
#define v_HWC_EN(x) (((x)&1)<<2)
#define v_WIN0_FORMAT(x) (((x)&7)<<3)
#define v_WIN1_FORMAT(x) (((x)&7)<<6)
#define v_HWC_COLOR_MODE(x) (((x)&1)<<9)
#define v_HWC_SIZE(x) (((x)&1)<<10)
#define v_WIN0_3D_EN(x) (((x)&1)<<11)
#define v_WIN0_3D_MODE(x) (((x)&7)<<12)
#define v_WIN0_RB_SWAP(x) (((x)&1)<<15)
#define v_WIN0_ALPHA_SWAP(x) (((x)&1)<<16)
#define v_WIN0_Y8_SWAP(x) (((x)&1)<<17)
#define v_WIN0_UV_SWAP(x) (((x)&1)<<18)
#define v_WIN1_RB_SWAP(x) (((x)&1)<<19)
#define v_WIN1_ALPHA_SWAP(x) (((x)&1)<<20)
#define v_WIN1_BL_SWAP(x) (((x)&1)<<21)
#define v_WIN0_OTSD_DISABLE(x) (((x)&1)<<22)
#define v_WIN1_OTSD_DISABLE(x) (((x)&1)<<23)
#define v_DMA_BURST_LENGTH(x) (((x)&3)<<24)
#define v_HWC_LODAD_EN(x) (((x)&1)<<26)
#define v_WIN1_LUT_EN(x) (((x)&1)<<27)
#define v_DSP_LUT_EN(x) (((x)&1)<<28)
#define v_DMA_STOP(x) (((x)&1)<<29)
#define v_LCDC_STANDBY(x) (((x)&1)<<30)
#define v_AUTO_GATING_EN(x) (((x)&1)<<31)
#define DSP_CTRL0 (0x04)
#define m_DSP_OUT_FORMAT (0x0f<<0)
#define m_HSYNC_POL (1<<4)
#define m_VSYNC_POL (1<<5)
#define m_DEN_POL (1<<6)
#define m_DCLK_POL (1<<7)
#define m_WIN0_TOP (1<<8)
#define m_DITHER_UP_EN (1<<9)
#define m_DITHER_DOWN_MODE (1<<10)
#define m_DITHER_DOWN_EN (1<<11)
#define m_INTERLACE_DSP_EN (1<<12)
#define m_INTERLACE_POL (1<<13)
#define m_WIN0_INTERLACE_EN (1<<14)
#define m_WIN1_INTERLACE_EN (1<<15)
#define m_WIN0_YRGB_DEFLICK_EN (1<<16)
#define m_WIN0_CBR_DEFLICK_EN (1<<17)
#define m_WIN0_ALPHA_MODE (1<<18)
#define m_WIN1_ALPHA_MODE (1<<19)
#define m_WIN0_CSC_MODE (3<<20)
#define m_WIN1_CSC_MODE (1<<22)
#define m_WIN0_YUV_CLIP (1<<23)
#define m_DSP_CCIR656_AVG (1<<24)
#define m_DCLK_OUTPUT_MODE (1<<25)
#define m_DCLK_PHASE_LOCK (1<<26)
#define m_DITHER_DOWN_SEL (3<<27)
#define m_ALPHA_MODE_SEL0 (1<<29)
#define m_ALPHA_MODE_SEL1 (1<<30)
#define m_DIFF_DCLK_EN (1<<31)
#define v_DSP_OUT_FORMAT(x) (((x)&0x0f)<<0)
#define v_HSYNC_POL(x) (((x)&1)<<4)
#define v_VSYNC_POL(x) (((x)&1)<<5)
#define v_DEN_POL(x) (((x)&1)<<6)
#define v_DCLK_POL(x) (((x)&1)<<7)
#define v_WIN0_TOP(x) (((x)&1)<<8)
#define v_DITHER_UP_EN(x) (((x)&1)<<9)
#define v_DITHER_DOWN_MODE(x) (((x)&1)<<10)
#define v_DITHER_DOWN_EN(x) (((x)&1)<<11)
#define v_INTERLACE_DSP_EN(x) (((x)&1)<<12)
#define v_INTERLACE_POL(x) (((x)&1)<<13)
#define v_WIN0_INTERLACE_EN(x) (((x)&1)<<14)
#define v_WIN1_INTERLACE_EN(x) (((x)&1)<<15)
#define v_WIN0_YRGB_DEFLICK_EN(x) (((x)&1)<<16)
#define v_WIN0_CBR_DEFLICK_EN(x) (((x)&1)<<17)
#define v_WIN0_ALPHA_MODE(x) (((x)&1)<<18)
#define v_WIN1_ALPHA_MODE(x) (((x)&1)<<19)
#define v_WIN0_CSC_MODE(x) (((x)&3)<<20)
#define v_WIN1_CSC_MODE(x) (((x)&1)<<22)
#define v_WIN0_YUV_CLIP(x) (((x)&1)<<23)
#define v_DSP_CCIR656_AVG(x) (((x)&1)<<24)
#define v_DCLK_OUTPUT_MODE(x) (((x)&1)<<25)
#define v_DCLK_PHASE_LOCK(x) (((x)&1)<<26)
#define v_DITHER_DOWN_SEL(x) (((x)&1)<<27)
#define v_ALPHA_MODE_SEL0(x) (((x)&1)<<29)
#define v_ALPHA_MODE_SEL1(x) (((x)&1)<<30)
#define v_DIFF_DCLK_EN(x) (((x)&1)<<31)
#define DSP_CTRL1 (0x08)
#define m_BG_COLOR (0xffffff<<0)
#define m_BG_B (0xff<<0)
#define m_BG_G (0xff<<8)
#define m_BG_R (0xff<<16)
#define m_BLANK_EN (1<<24)
#define m_BLACK_EN (1<<25)
#define m_DSP_BG_SWAP (1<<26)
#define m_DSP_RB_SWAP (1<<27)
#define m_DSP_RG_SWAP (1<<28)
#define m_DSP_DELTA_SWAP (1<<29)
#define m_DSP_DUMMY_SWAP (1<<30)
#define m_DSP_OUT_ZERO (1<<31)
#define v_BG_COLOR(x) (((x)&0xffffff)<<0)
#define v_BG_B(x) (((x)&0xff)<<0)
#define v_BG_G(x) (((x)&0xff)<<8)
#define v_BG_R(x) (((x)&0xff)<<16)
#define v_BLANK_EN(x) (((x)&1)<<24)
#define v_BLACK_EN(x) (((x)&1)<<25)
#define v_DSP_BG_SWAP(x) (((x)&1)<<26)
#define v_DSP_RB_SWAP(x) (((x)&1)<<27)
#define v_DSP_RG_SWAP(x) (((x)&1)<<28)
#define v_DSP_DELTA_SWAP(x) (((x)&1)<<29)
#define v_DSP_DUMMY_SWAP(x) (((x)&1)<<30)
#define v_DSP_OUT_ZERO(x) (((x)&1)<<31)
#define MCU_CTRL (0x0c)
#define m_MCU_PIX_TOTAL (0x3f<<0)
#define m_MCU_CS_ST (0x0f<<6)
#define m_MCU_CS_END (0x3f<<10)
#define m_MCU_RW_ST (0x0f<<16)
#define m_MCU_RW_END (0x3f<<20)
#define m_MCU_CLK_SEL (1<<26)
#define m_MCU_HOLD_MODE (1<<27)
#define m_MCU_FS_HOLD_STA (1<<28)
#define m_MCU_RS_SELECT (1<<29)
#define m_MCU_BYPASS (1<<30)
#define m_MCU_TYPE (1<<31)
#define v_MCU_PIX_TOTAL(x) (((x)&0x3f)<<0)
#define v_MCU_CS_ST(x) (((x)&0x0f)<<6)
#define v_MCU_CS_END(x) (((x)&0x3f)<<10)
#define v_MCU_RW_ST(x) (((x)&0x0f)<<16)
#define v_MCU_RW_END(x) (((x)&0x3f)<<20)
#define v_MCU_CLK_SEL(x) (((x)&1)<<26)
#define v_MCU_HOLD_MODE(x) (((x)&1)<<27)
#define v_MCU_FS_HOLD_STA(x) (((x)&1)<<28)
#define v_MCU_RS_SELECT(x) (((x)&1)<<29)
#define v_MCU_BYPASS(x) (((x)&1)<<30)
#define v_MCU_TYPE(x) (((x)&1)<<31)
#define INT_STATUS (0x10)
#define m_HS_INT_STA (1<<0) //status
#define m_FS_INT_STA (1<<1)
#define m_LF_INT_STA (1<<2)
#define m_BUS_ERR_INT_STA (1<<3)
#define m_HS_INT_EN (1<<4) //enable
#define m_FS_INT_EN (1<<5)
#define m_LF_INT_EN (1<<6)
#define m_BUS_ERR_INT_EN (1<<7)
#define m_HS_INT_CLEAR (1<<8) //auto clear
#define m_FS_INT_CLEAR (1<<9)
#define m_LF_INT_CLEAR (1<<10)
#define m_BUS_ERR_INT_CLEAR (1<<11)
#define m_LF_INT_NUM (0xfff<<12)
#define v_HS_INT_EN(x) (((x)&1)<<4)
#define v_FS_INT_EN(x) (((x)&1)<<5)
#define v_LF_INT_EN(x) (((x)&1)<<6)
#define v_BUS_ERR_INT_EN(x) (((x)&1)<<7)
#define v_HS_INT_CLEAR(x) (((x)&1)<<8)
#define v_FS_INT_CLEAR(x) (((x)&1)<<9)
#define v_LF_INT_CLEAR(x) (((x)&1)<<10)
#define v_BUS_ERR_INT_CLEAR(x) (((x)&1)<<11)
#define v_LF_INT_NUM(x) (((x)&0xfff)<<12)
#define ALPHA_CTRL (0x14)
#define m_WIN0_ALPHA_EN (1<<0)
#define m_WIN1_ALPHA_EN (1<<1)
#define m_HWC_ALPAH_EN (1<<2)
#define m_WIN0_ALPHA_VAL (0xff<<4)
#define m_WIN1_ALPHA_VAL (0xff<<12)
#define m_HWC_ALPAH_VAL (0x0f<<20)
#define v_WIN0_ALPHA_EN(x) (((x)&1)<<0)
#define v_WIN1_ALPHA_EN(x) (((x)&1)<<1)
#define v_HWC_ALPAH_EN(x) (((x)&1)<<2)
#define v_WIN0_ALPHA_VAL(x) (((x)&0xff)<<4)
#define v_WIN1_ALPHA_VAL(x) (((x)&0xff)<<12)
#define v_HWC_ALPAH_VAL(x) (((x)&0x0f)<<20)
#define WIN0_COLOR_KEY (0x18)
#define m_COLOR_KEY_VAL (0xffffff<<0)
#define m_COLOR_KEY_EN (1<<24)
#define v_COLOR_KEY_VAL(x) (((x)&0xffffff)<<0)
#define v_COLOR_KEY_EN(x) (((x)&1)<<24)
#define WIN1_COLOR_KEY (0x1C)
#define WIN0_YRGB_MST0 (0x20)
#define WIN0_CBR_MST0 (0x24)
#define WIN0_YRGB_MST1 (0x28)
#define WIN0_CBR_MST1 (0x2C)
#define WIN_VIR (0x30)
#define m_WIN0_VIR (0x1fff << 0)
#define m_WIN1_VIR (0x1fff << 16)
#define v_WIN0_VIR_VAL(x) ((x)<<0)
#define v_WIN1_VIR_VAL(x) ((x)<<16)
#define v_ARGB888_VIRWIDTH(x) (((x)&0x1fff)<<0)
#define v_RGB888_VIRWIDTH(x) (((((x*3)>>2)+((x)%3))&0x1fff)<<0)
#define v_RGB565_VIRWIDTH(x) ((DIV_ROUND_UP(x,2)&0x1fff)<<0)
#define v_YUV_VIRWIDTH(x) ((DIV_ROUND_UP(x,4)&0x1fff)<<0)
#define v_WIN1_ARGB888_VIRWIDTH(x) (((x)&0x1fff)<<16)
#define v_WIN1_RGB888_VIRWIDTH(x) (((((x*3)>>2)+((x)%3))&0x1fff)<<16)
#define v_WIN1_RGB565_VIRWIDTH(x) ((DIV_ROUND_UP(x,2)&0x1fff)<<16)
#define WIN0_ACT_INFO (0x34)
#define m_ACT_WIDTH (0x1fff<<0)
#define m_ACT_HEIGHT (0x1fff<<16)
#define v_ACT_WIDTH(x) (((x-1)&0x1fff)<<0)
#define v_ACT_HEIGHT(x) (((x-1)&0x1fff)<<16)
#define WIN0_DSP_INFO (0x38)
#define v_DSP_WIDTH(x) (((x-1)&0x7ff)<<0)
#define v_DSP_HEIGHT(x) (((x-1)&0x7ff)<<16)
#define WIN0_DSP_ST (0x3C)
#define v_DSP_STX(x) (((x)&0xfff)<<0)
#define v_DSP_STY(x) (((x)&0xfff)<<16)
#define WIN0_SCL_FACTOR_YRGB (0x40)
#define v_X_SCL_FACTOR(x) (((x)&0xffff)<<0)
#define v_Y_SCL_FACTOR(x) (((x)&0xffff)<<16)
#define WIN0_SCL_FACTOR_CBR (0x44)
#define WIN0_SCL_OFFSET (0x48)
#define WIN1_MST (0x4C)
#define WIN1_DSP_INFO (0x50)
#define WIN1_DSP_ST (0x54)
#define HWC_MST (0x58)
#define HWC_DSP_ST (0x5C)
#define HWC_COLOR_LUT0 (0x60)
#define HWC_COLOR_LUT1 (0x64)
#define HWC_COLOR_LUT2 (0x68)
#define DSP_HTOTAL_HS_END (0x6C)
#define v_HSYNC(x) (((x)&0xfff)<<0) //hsync pulse width
#define v_HORPRD(x) (((x)&0xfff)<<16) //horizontal period
#define DSP_HACT_ST_END (0x70)
#define v_HAEP(x) (((x)&0xfff)<<0) //horizontal active end point
#define v_HASP(x) (((x)&0xfff)<<16) //horizontal active start point
#define DSP_VTOTAL_VS_END (0x74)
#define v_VSYNC(x) (((x)&0xfff)<<0)
#define v_VERPRD(x) (((x)&0xfff)<<16)
#define DSP_VACT_ST_END (0x78)
#define v_VAEP(x) (((x)&0xfff)<<0)
#define v_VASP(x) (((x)&0xfff)<<16)
#define DSP_VS_ST_END_F1 (0x7C)
#define DSP_VACT_ST_END_F1 (0x80)
#define REG_CFG_DONE (0x90)
#define MCU_BYPASS_WPORT (0x100)
#define MCU_BYPASS_RPORT (0x200)
#define WIN1_LUT_ADDR (0x400)
#define DSP_LUT_ADDR (0x800)
/*
RK3026/RK3028A max output resolution 1920x1080
support IEP instead of 3d
*/
//#ifdef CONFIG_ARCH_RK3026
//SYS_CTRL 0x00
#define m_DIRECT_PATCH_EN (1<<11)
#define m_DIRECT_PATH_LAY_SEL (1<<12)
#define v_DIRECT_PATCH_EN(x) (((x)&1)<<11)
#define v_DIRECT_PATH_LAY_SEL(x) (((x)&1)<<12)
//INT_STATUS 0x10
#define m_WIN0_EMPTY_INTR_EN (1<<24)
#define m_WIN1_EMPTY_INTR_EN (1<<25)
#define m_WIN0_EMPTY_INTR_CLR (1<<26)
#define m_WIN1_EMPTY_INTR_CLR (1<<27)
#define m_WIN0_EMPTY_INTR_STA (1<<28)
#define m_WIN1_EMPTY_INTR_STA (1<<29)
#define v_WIN0_EMPTY_INTR_EN(x) (((x)&1)<<24)
#define v_WIN1_EMPTY_INTR_EN(x) (((x)&1)<<25)
#define v_WIN0_EMPTY_INTR_CLR(x) (((x)&1)<<26)
#define v_WIN1_EMPTY_INTR_CLR(x) (((x)&1)<<27)
#define v_WIN0_EMPTY_INTR_STA(x) (((x)&1)<<28)
#define v_WIN1_EMPTY_INTR_STA(x) (((x)&1)<<29)
//#endif
#define CalScale(x, y) ((((u32)(x-1))*0x1000)/(y-1))
static inline void lcdc_writel(struct fimd_context *ctx,u32 offset,u32 v)
{
u32 *_pv = (u32*)ctx->regsbak;
_pv += (offset >> 2);
*_pv = v;
writel_relaxed(v,ctx->regs+offset);
}
static inline u32 lcdc_readl(struct fimd_context *ctx,u32 offset)
{
u32 v;
u32 *_pv = (u32*)ctx->regsbak;
_pv += (offset >> 2);
v = readl_relaxed(ctx->regs+offset);
*_pv = v;
return v;
}
static inline u32 lcdc_read_bit(struct fimd_context *ctx,u32 offset,u32 msk)
{
u32 _v = readl_relaxed(ctx->regs+offset);
_v &= msk;
return (_v >> msk);
}
static inline void lcdc_set_bit(struct fimd_context *ctx,u32 offset,u32 msk)
{
u32* _pv = (u32*)ctx->regsbak;
_pv += (offset >> 2);
(*_pv) |= msk;
writel_relaxed(*_pv,ctx->regs + offset);
}
static inline void lcdc_clr_bit(struct fimd_context *ctx,u32 offset,u32 msk)
{
u32* _pv = (u32*)ctx->regsbak;
_pv += (offset >> 2);
(*_pv) &= (~msk);
writel_relaxed(*_pv,ctx->regs + offset);
}
static inline void lcdc_msk_reg(struct fimd_context *ctx,u32 offset,u32 msk,u32 v)
{
u32 *_pv = (u32*)ctx->regsbak;
_pv += (offset >> 2);
(*_pv) &= (~msk);
(*_pv) |= v;
writel_relaxed(*_pv,ctx->regs+offset);
}
static inline void lcdc_cfg_done(struct fimd_context *ctx)
{
writel_relaxed(0x01,ctx->regs+REG_CFG_DONE);
dsb();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,203 @@
/* rockchip_drm_buf.c
*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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/drmP.h>
#include <drm/rockchip_drm.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_gem.h"
#include "rockchip_drm_buf.h"
#include "rockchip_drm_iommu.h"
static int lowlevel_buffer_allocate(struct drm_device *dev,
unsigned int flags, struct rockchip_drm_gem_buf *buf)
{
int ret = 0;
enum dma_attr attr;
unsigned int nr_pages;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (buf->dma_addr) {
DRM_DEBUG_KMS("already allocated.\n");
return 0;
}
init_dma_attrs(&buf->dma_attrs);
/*
* if ROCKCHIP_BO_CONTIG, fully physically contiguous memory
* region will be allocated else physically contiguous
* as possible.
*/
if (!(flags & ROCKCHIP_BO_NONCONTIG))
dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &buf->dma_attrs);
/*
* if ROCKCHIP_BO_WC or ROCKCHIP_BO_NONCACHABLE, writecombine mapping
* else cachable mapping.
*/
if (flags & ROCKCHIP_BO_WC || !(flags & ROCKCHIP_BO_CACHABLE))
attr = DMA_ATTR_WRITE_COMBINE;
else
attr = DMA_ATTR_NON_CONSISTENT;
dma_set_attr(attr, &buf->dma_attrs);
dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buf->dma_attrs);
nr_pages = buf->size >> PAGE_SHIFT;
if (!is_drm_iommu_supported(dev)) {
dma_addr_t start_addr;
unsigned int i = 0;
buf->pages = kzalloc(sizeof(struct page) * nr_pages,
GFP_KERNEL);
if (!buf->pages) {
DRM_ERROR("failed to allocate pages.\n");
return -ENOMEM;
}
buf->kvaddr = dma_alloc_attrs(dev->dev, buf->size,
&buf->dma_addr, GFP_KERNEL,
&buf->dma_attrs);
if (!buf->kvaddr) {
DRM_ERROR("failed to allocate buffer.\n");
kfree(buf->pages);
return -ENOMEM;
}
start_addr = buf->dma_addr;
while (i < nr_pages) {
buf->pages[i] = phys_to_page(start_addr);
start_addr += PAGE_SIZE;
i++;
}
} else {
buf->pages = dma_alloc_attrs(dev->dev, buf->size,
&buf->dma_addr, GFP_KERNEL,
&buf->dma_attrs);
if (!buf->pages) {
DRM_ERROR("failed to allocate buffer.\n");
return -ENOMEM;
}
}
buf->sgt = drm_prime_pages_to_sg(buf->pages, nr_pages);
if (!buf->sgt) {
DRM_ERROR("failed to get sg table.\n");
ret = -ENOMEM;
goto err_free_attrs;
}
DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n",
(unsigned long)buf->dma_addr,
buf->size);
return ret;
err_free_attrs:
dma_free_attrs(dev->dev, buf->size, buf->pages,
(dma_addr_t)buf->dma_addr, &buf->dma_attrs);
buf->dma_addr = (dma_addr_t)NULL;
if (!is_drm_iommu_supported(dev))
kfree(buf->pages);
return ret;
}
static void lowlevel_buffer_deallocate(struct drm_device *dev,
unsigned int flags, struct rockchip_drm_gem_buf *buf)
{
DRM_DEBUG_KMS("%s.\n", __FILE__);
if (!buf->dma_addr) {
DRM_DEBUG_KMS("dma_addr is invalid.\n");
return;
}
DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n",
(unsigned long)buf->dma_addr,
buf->size);
sg_free_table(buf->sgt);
kfree(buf->sgt);
buf->sgt = NULL;
if (!is_drm_iommu_supported(dev)) {
dma_free_attrs(dev->dev, buf->size, buf->kvaddr,
(dma_addr_t)buf->dma_addr, &buf->dma_attrs);
kfree(buf->pages);
} else
dma_free_attrs(dev->dev, buf->size, buf->pages,
(dma_addr_t)buf->dma_addr, &buf->dma_attrs);
buf->dma_addr = (dma_addr_t)NULL;
}
struct rockchip_drm_gem_buf *rockchip_drm_init_buf(struct drm_device *dev,
unsigned int size)
{
struct rockchip_drm_gem_buf *buffer;
DRM_DEBUG_KMS("%s.\n", __FILE__);
DRM_DEBUG_KMS("desired size = 0x%x\n", size);
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
if (!buffer) {
DRM_ERROR("failed to allocate rockchip_drm_gem_buf.\n");
return NULL;
}
buffer->size = size;
return buffer;
}
void rockchip_drm_fini_buf(struct drm_device *dev,
struct rockchip_drm_gem_buf *buffer)
{
DRM_DEBUG_KMS("%s.\n", __FILE__);
if (!buffer) {
DRM_DEBUG_KMS("buffer is null.\n");
return;
}
kfree(buffer);
buffer = NULL;
}
int rockchip_drm_alloc_buf(struct drm_device *dev,
struct rockchip_drm_gem_buf *buf, unsigned int flags)
{
/*
* allocate memory region and set the memory information
* to vaddr and dma_addr of a buffer object.
*/
if (lowlevel_buffer_allocate(dev, flags, buf) < 0)
return -ENOMEM;
return 0;
}
void rockchip_drm_free_buf(struct drm_device *dev,
unsigned int flags, struct rockchip_drm_gem_buf *buffer)
{
lowlevel_buffer_deallocate(dev, flags, buffer);
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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.
*/
#ifndef _ROCKCHIP_DRM_BUF_H_
#define _ROCKCHIP_DRM_BUF_H_
/* create and initialize buffer object. */
struct rockchip_drm_gem_buf *rockchip_drm_init_buf(struct drm_device *dev,
unsigned int size);
/* destroy buffer object. */
void rockchip_drm_fini_buf(struct drm_device *dev,
struct rockchip_drm_gem_buf *buffer);
/* allocate physical memory region and setup sgt. */
int rockchip_drm_alloc_buf(struct drm_device *dev,
struct rockchip_drm_gem_buf *buf,
unsigned int flags);
/* release physical memory region, and sgt. */
void rockchip_drm_free_buf(struct drm_device *dev,
unsigned int flags,
struct rockchip_drm_gem_buf *buffer);
#endif

View File

@ -0,0 +1,382 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/rockchip_drm.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_encoder.h"
#define to_rockchip_connector(x) container_of(x, struct rockchip_drm_connector,\
drm_connector)
struct rockchip_drm_connector {
struct drm_connector drm_connector;
uint32_t encoder_id;
struct rockchip_drm_manager *manager;
uint32_t dpms;
};
/* convert rockchip_video_timings to drm_display_mode */
static inline void
convert_to_display_mode(struct drm_display_mode *mode,
struct rockchip_drm_panel_info *panel)
{
struct fb_videomode *timing = &panel->timing;
DRM_DEBUG_KMS("%s\n", __FILE__);
mode->clock = timing->pixclock / 1000;
mode->vrefresh = timing->refresh;
mode->hdisplay = timing->xres;
mode->hsync_start = mode->hdisplay + timing->right_margin;
mode->hsync_end = mode->hsync_start + timing->hsync_len;
mode->htotal = mode->hsync_end + timing->left_margin;
mode->vdisplay = timing->yres;
mode->vsync_start = mode->vdisplay + timing->lower_margin;
mode->vsync_end = mode->vsync_start + timing->vsync_len;
mode->vtotal = mode->vsync_end + timing->upper_margin;
mode->width_mm = panel->width_mm;
mode->height_mm = panel->height_mm;
if (timing->vmode & FB_VMODE_INTERLACED)
mode->flags |= DRM_MODE_FLAG_INTERLACE;
if (timing->vmode & FB_VMODE_DOUBLE)
mode->flags |= DRM_MODE_FLAG_DBLSCAN;
}
/* convert drm_display_mode to rockchip_video_timings */
static inline void
convert_to_video_timing(struct fb_videomode *timing,
struct drm_display_mode *mode)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
memset(timing, 0, sizeof(*timing));
timing->pixclock = mode->clock * 1000;
timing->refresh = drm_mode_vrefresh(mode);
timing->xres = mode->hdisplay;
timing->right_margin = mode->hsync_start - mode->hdisplay;
timing->hsync_len = mode->hsync_end - mode->hsync_start;
timing->left_margin = mode->htotal - mode->hsync_end;
timing->yres = mode->vdisplay;
timing->lower_margin = mode->vsync_start - mode->vdisplay;
timing->vsync_len = mode->vsync_end - mode->vsync_start;
timing->upper_margin = mode->vtotal - mode->vsync_end;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
timing->vmode = FB_VMODE_INTERLACED;
else
timing->vmode = FB_VMODE_NONINTERLACED;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
timing->vmode |= FB_VMODE_DOUBLE;
}
static int rockchip_drm_connector_get_modes(struct drm_connector *connector)
{
struct rockchip_drm_connector *rockchip_connector =
to_rockchip_connector(connector);
struct rockchip_drm_manager *manager = rockchip_connector->manager;
struct rockchip_drm_display_ops *display_ops = manager->display_ops;
struct edid *edid = NULL;
unsigned int count = 0;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!display_ops) {
DRM_DEBUG_KMS("display_ops is null.\n");
return 0;
}
/*
* if get_edid() exists then get_edid() callback of hdmi side
* is called to get edid data through i2c interface else
* get timing from the FIMD driver(display controller).
*
* P.S. in case of lcd panel, count is always 1 if success
* because lcd panel has only one mode.
*/
if (display_ops->get_edid) {
edid = display_ops->get_edid(manager->dev, connector);
if (IS_ERR_OR_NULL(edid)) {
ret = PTR_ERR(edid);
edid = NULL;
DRM_ERROR("Panel operation get_edid failed %d\n", ret);
goto out;
}
count = drm_add_edid_modes(connector, edid);
if (!count) {
DRM_ERROR("Add edid modes failed %d\n", count);
goto out;
}
drm_mode_connector_update_edid_property(connector, edid);
} else {
struct rockchip_drm_panel_info *panel;
struct drm_display_mode *mode = drm_mode_create(connector->dev);
if (!mode) {
DRM_ERROR("failed to create a new display mode.\n");
return 0;
}
if (display_ops->get_panel)
panel = display_ops->get_panel(manager->dev);
else {
drm_mode_destroy(connector->dev, mode);
return 0;
}
convert_to_display_mode(mode, panel);
connector->display_info.width_mm = mode->width_mm;
connector->display_info.height_mm = mode->height_mm;
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
count = 1;
}
out:
kfree(edid);
return count;
}
static int rockchip_drm_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct rockchip_drm_connector *rockchip_connector =
to_rockchip_connector(connector);
struct rockchip_drm_manager *manager = rockchip_connector->manager;
struct rockchip_drm_display_ops *display_ops = manager->display_ops;
struct fb_videomode timing;
int ret = MODE_BAD;
DRM_DEBUG_KMS("%s\n", __FILE__);
convert_to_video_timing(&timing, mode);
if (display_ops && display_ops->check_timing)
if (!display_ops->check_timing(manager->dev, (void *)&timing))
ret = MODE_OK;
return ret;
}
struct drm_encoder *rockchip_drm_best_encoder(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct rockchip_drm_connector *rockchip_connector =
to_rockchip_connector(connector);
struct drm_mode_object *obj;
struct drm_encoder *encoder;
DRM_DEBUG_KMS("%s\n", __FILE__);
obj = drm_mode_object_find(dev, rockchip_connector->encoder_id,
DRM_MODE_OBJECT_ENCODER);
if (!obj) {
DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
rockchip_connector->encoder_id);
return NULL;
}
encoder = obj_to_encoder(obj);
return encoder;
}
static struct drm_connector_helper_funcs rockchip_connector_helper_funcs = {
.get_modes = rockchip_drm_connector_get_modes,
.mode_valid = rockchip_drm_connector_mode_valid,
.best_encoder = rockchip_drm_best_encoder,
};
void rockchip_drm_display_power(struct drm_connector *connector, int mode)
{
struct drm_encoder *encoder = rockchip_drm_best_encoder(connector);
struct rockchip_drm_connector *rockchip_connector;
struct rockchip_drm_manager *manager = rockchip_drm_get_manager(encoder);
struct rockchip_drm_display_ops *display_ops = manager->display_ops;
rockchip_connector = to_rockchip_connector(connector);
if (rockchip_connector->dpms == mode) {
DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
return;
}
if (display_ops && display_ops->power_on)
display_ops->power_on(manager->dev, mode);
rockchip_connector->dpms = mode;
}
static void rockchip_drm_connector_dpms(struct drm_connector *connector,
int mode)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* in case that drm_crtc_helper_set_mode() is called,
* encoder/crtc->funcs->dpms() will be just returned
* because they already were DRM_MODE_DPMS_ON so only
* rockchip_drm_display_power() will be called.
*/
drm_helper_connector_dpms(connector, mode);
rockchip_drm_display_power(connector, mode);
}
static int rockchip_drm_connector_fill_modes(struct drm_connector *connector,
unsigned int max_width, unsigned int max_height)
{
struct rockchip_drm_connector *rockchip_connector =
to_rockchip_connector(connector);
struct rockchip_drm_manager *manager = rockchip_connector->manager;
struct rockchip_drm_manager_ops *ops = manager->ops;
unsigned int width, height;
width = max_width;
height = max_height;
/*
* if specific driver want to find desired_mode using maxmum
* resolution then get max width and height from that driver.
*/
if (ops && ops->get_max_resol)
ops->get_max_resol(manager->dev, &width, &height);
return drm_helper_probe_single_connector_modes(connector, width,
height);
}
/* get detection status of display device. */
static enum drm_connector_status
rockchip_drm_connector_detect(struct drm_connector *connector, bool force)
{
struct rockchip_drm_connector *rockchip_connector =
to_rockchip_connector(connector);
struct rockchip_drm_manager *manager = rockchip_connector->manager;
struct rockchip_drm_display_ops *display_ops =
manager->display_ops;
enum drm_connector_status status = connector_status_disconnected;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (display_ops && display_ops->is_connected) {
if (display_ops->is_connected(manager->dev))
status = connector_status_connected;
else
status = connector_status_disconnected;
}
return status;
}
static void rockchip_drm_connector_destroy(struct drm_connector *connector)
{
struct rockchip_drm_connector *rockchip_connector =
to_rockchip_connector(connector);
DRM_DEBUG_KMS("%s\n", __FILE__);
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(rockchip_connector);
}
static struct drm_connector_funcs rockchip_connector_funcs = {
.dpms = rockchip_drm_connector_dpms,
.fill_modes = rockchip_drm_connector_fill_modes,
.detect = rockchip_drm_connector_detect,
.destroy = rockchip_drm_connector_destroy,
};
struct drm_connector *rockchip_drm_connector_create(struct drm_device *dev,
struct drm_encoder *encoder)
{
struct rockchip_drm_connector *rockchip_connector;
struct rockchip_drm_manager *manager = rockchip_drm_get_manager(encoder);
struct drm_connector *connector;
int type;
int err;
DRM_DEBUG_KMS("%s\n", __FILE__);
rockchip_connector = kzalloc(sizeof(*rockchip_connector), GFP_KERNEL);
if (!rockchip_connector) {
DRM_ERROR("failed to allocate connector\n");
return NULL;
}
connector = &rockchip_connector->drm_connector;
switch (manager->display_ops->type) {
case ROCKCHIP_DISPLAY_TYPE_HDMI:
type = DRM_MODE_CONNECTOR_HDMIA;
connector->interlace_allowed = true;
connector->polled = DRM_CONNECTOR_POLL_HPD;
break;
case ROCKCHIP_DISPLAY_TYPE_VIDI:
type = DRM_MODE_CONNECTOR_VIRTUAL;
connector->polled = DRM_CONNECTOR_POLL_HPD;
break;
case ROCKCHIP_DISPLAY_TYPE_LCD:
type = DRM_MODE_CONNECTOR_LVDS;
break;
default:
type = DRM_MODE_CONNECTOR_Unknown;
break;
}
drm_connector_init(dev, connector, &rockchip_connector_funcs, type);
drm_connector_helper_add(connector, &rockchip_connector_helper_funcs);
err = drm_sysfs_connector_add(connector);
if (err)
goto err_connector;
rockchip_connector->encoder_id = encoder->base.id;
rockchip_connector->manager = manager;
rockchip_connector->dpms = DRM_MODE_DPMS_OFF;
connector->dpms = DRM_MODE_DPMS_OFF;
connector->encoder = encoder;
err = drm_mode_connector_attach_encoder(connector, encoder);
if (err) {
DRM_ERROR("failed to attach a connector to a encoder\n");
goto err_sysfs;
}
DRM_DEBUG_KMS("connector has been created\n");
return connector;
err_sysfs:
drm_sysfs_connector_remove(connector);
err_connector:
drm_connector_cleanup(connector);
kfree(rockchip_connector);
return NULL;
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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.
*/
#ifndef _ROCKCHIP_DRM_CONNECTOR_H_
#define _ROCKCHIP_DRM_CONNECTOR_H_
struct drm_connector *rockchip_drm_connector_create(struct drm_device *dev,
struct drm_encoder *encoder);
struct drm_encoder *rockchip_drm_best_encoder(struct drm_connector *connector);
void rockchip_drm_display_power(struct drm_connector *connector, int mode);
#endif

View File

@ -0,0 +1,241 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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/drmP.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_encoder.h"
#include "rockchip_drm_connector.h"
#include "rockchip_drm_fbdev.h"
static LIST_HEAD(rockchip_drm_subdrv_list);
static int rockchip_drm_create_enc_conn(struct drm_device *dev,
struct rockchip_drm_subdrv *subdrv)
{
struct drm_encoder *encoder;
struct drm_connector *connector;
int ret;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
subdrv->manager->dev = subdrv->dev;
/* create and initialize a encoder for this sub driver. */
encoder = rockchip_drm_encoder_create(dev, subdrv->manager,
(1 << MAX_CRTC) - 1);
if (!encoder) {
DRM_ERROR("failed to create encoder\n");
return -EFAULT;
}
/*
* create and initialize a connector for this sub driver and
* attach the encoder created above to the connector.
*/
connector = rockchip_drm_connector_create(dev, encoder);
if (!connector) {
DRM_ERROR("failed to create connector\n");
ret = -EFAULT;
goto err_destroy_encoder;
}
subdrv->encoder = encoder;
subdrv->connector = connector;
return 0;
err_destroy_encoder:
encoder->funcs->destroy(encoder);
return ret;
}
static void rockchip_drm_destroy_enc_conn(struct rockchip_drm_subdrv *subdrv)
{
if (subdrv->encoder) {
struct drm_encoder *encoder = subdrv->encoder;
encoder->funcs->destroy(encoder);
subdrv->encoder = NULL;
}
if (subdrv->connector) {
struct drm_connector *connector = subdrv->connector;
connector->funcs->destroy(connector);
subdrv->connector = NULL;
}
}
static int rockchip_drm_subdrv_probe(struct drm_device *dev,
struct rockchip_drm_subdrv *subdrv)
{
if (subdrv->probe) {
int ret;
subdrv->drm_dev = dev;
/*
* this probe callback would be called by sub driver
* after setting of all resources to this sub driver,
* such as clock, irq and register map are done or by load()
* of rockchip drm driver.
*
* P.S. note that this driver is considered for modularization.
*/
ret = subdrv->probe(dev, subdrv->dev);
if (ret)
return ret;
}
return 0;
}
static void rockchip_drm_subdrv_remove(struct drm_device *dev,
struct rockchip_drm_subdrv *subdrv)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (subdrv->remove)
subdrv->remove(dev, subdrv->dev);
}
int rockchip_drm_device_register(struct drm_device *dev)
{
struct rockchip_drm_subdrv *subdrv, *n;
unsigned int fine_cnt = 0;
int err;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
if (!dev)
return -EINVAL;
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
list_for_each_entry_safe(subdrv, n, &rockchip_drm_subdrv_list, list) {
err = rockchip_drm_subdrv_probe(dev, subdrv);
if (err) {
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
DRM_DEBUG("rockchip drm subdrv probe failed.\n");
list_del(&subdrv->list);
continue;
}
/*
* if manager is null then it means that this sub driver
* doesn't need encoder and connector.
*/
if (!subdrv->manager) {
fine_cnt++;
continue;
}
err = rockchip_drm_create_enc_conn(dev, subdrv);
if (err) {
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
DRM_DEBUG("failed to create encoder and connector.\n");
rockchip_drm_subdrv_remove(dev, subdrv);
list_del(&subdrv->list);
continue;
}
fine_cnt++;
}
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
if (!fine_cnt)
return -EINVAL;
return 0;
}
EXPORT_SYMBOL_GPL(rockchip_drm_device_register);
int rockchip_drm_device_unregister(struct drm_device *dev)
{
struct rockchip_drm_subdrv *subdrv;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!dev) {
WARN(1, "Unexpected drm device unregister!\n");
return -EINVAL;
}
list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list) {
rockchip_drm_subdrv_remove(dev, subdrv);
rockchip_drm_destroy_enc_conn(subdrv);
}
return 0;
}
EXPORT_SYMBOL_GPL(rockchip_drm_device_unregister);
int rockchip_drm_subdrv_register(struct rockchip_drm_subdrv *subdrv)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!subdrv)
return -EINVAL;
list_add_tail(&subdrv->list, &rockchip_drm_subdrv_list);
return 0;
}
EXPORT_SYMBOL_GPL(rockchip_drm_subdrv_register);
int rockchip_drm_subdrv_unregister(struct rockchip_drm_subdrv *subdrv)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!subdrv)
return -EINVAL;
list_del(&subdrv->list);
return 0;
}
EXPORT_SYMBOL_GPL(rockchip_drm_subdrv_unregister);
int rockchip_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
{
struct rockchip_drm_subdrv *subdrv;
int ret;
list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list) {
if (subdrv->open) {
ret = subdrv->open(dev, subdrv->dev, file);
if (ret)
goto err;
}
}
return 0;
err:
list_for_each_entry_reverse(subdrv, &subdrv->list, list) {
if (subdrv->close)
subdrv->close(dev, subdrv->dev, file);
}
return ret;
}
EXPORT_SYMBOL_GPL(rockchip_drm_subdrv_open);
void rockchip_drm_subdrv_close(struct drm_device *dev, struct drm_file *file)
{
struct rockchip_drm_subdrv *subdrv;
list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list) {
if (subdrv->close)
subdrv->close(dev, subdrv->dev, file);
}
}
EXPORT_SYMBOL_GPL(rockchip_drm_subdrv_close);

View File

@ -0,0 +1,432 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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/drmP.h>
#include <drm/drm_crtc_helper.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_encoder.h"
#include "rockchip_drm_plane.h"
#define to_rockchip_crtc(x) container_of(x, struct rockchip_drm_crtc,\
drm_crtc)
enum rockchip_crtc_mode {
CRTC_MODE_NORMAL, /* normal mode */
CRTC_MODE_BLANK, /* The private plane of crtc is blank */
};
/*
* Exynos specific crtc structure.
*
* @drm_crtc: crtc object.
* @drm_plane: pointer of private plane object for this crtc
* @pipe: a crtc index created at load() with a new crtc object creation
* and the crtc object would be set to private->crtc array
* to get a crtc object corresponding to this pipe from private->crtc
* array when irq interrupt occured. the reason of using this pipe is that
* drm framework doesn't support multiple irq yet.
* we can refer to the crtc to current hardware interrupt occured through
* this pipe value.
* @dpms: store the crtc dpms value
* @mode: store the crtc mode value
*/
struct rockchip_drm_crtc {
struct drm_crtc drm_crtc;
struct drm_plane *plane;
unsigned int pipe;
unsigned int dpms;
enum rockchip_crtc_mode mode;
wait_queue_head_t pending_flip_queue;
atomic_t pending_flip;
};
static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
if (rockchip_crtc->dpms == mode) {
DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
return;
}
if (mode > DRM_MODE_DPMS_ON) {
/* wait for the completion of page flip. */
wait_event(rockchip_crtc->pending_flip_queue,
atomic_read(&rockchip_crtc->pending_flip) == 0);
drm_vblank_off(crtc->dev, rockchip_crtc->pipe);
}
rockchip_drm_fn_encoder(crtc, &mode, rockchip_drm_encoder_crtc_dpms);
rockchip_crtc->dpms = mode;
}
static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL. */
}
static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
{
struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
DRM_DEBUG_KMS("%s\n", __FILE__);
rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
rockchip_plane_commit(rockchip_crtc->plane);
rockchip_plane_dpms(rockchip_crtc->plane, DRM_MODE_DPMS_ON);
}
static bool
rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL */
return true;
}
static int
rockchip_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, int x, int y,
struct drm_framebuffer *old_fb)
{
struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
struct drm_plane *plane = rockchip_crtc->plane;
unsigned int crtc_w;
unsigned int crtc_h;
int pipe = rockchip_crtc->pipe;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* copy the mode data adjusted by mode_fixup() into crtc->mode
* so that hardware can be seet to proper mode.
*/
memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
crtc_w = crtc->fb->width - x;
crtc_h = crtc->fb->height - y;
ret = rockchip_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
x, y, crtc_w, crtc_h);
if (ret)
return ret;
plane->crtc = crtc;
plane->fb = crtc->fb;
rockchip_drm_fn_encoder(crtc, &pipe, rockchip_drm_encoder_crtc_pipe);
return 0;
}
static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
struct drm_plane *plane = rockchip_crtc->plane;
unsigned int crtc_w;
unsigned int crtc_h;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
/* when framebuffer changing is requested, crtc's dpms should be on */
if (rockchip_crtc->dpms > DRM_MODE_DPMS_ON) {
DRM_ERROR("failed framebuffer changing request.\n");
return -EPERM;
}
crtc_w = crtc->fb->width - x;
crtc_h = crtc->fb->height - y;
ret = rockchip_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
x, y, crtc_w, crtc_h);
if (ret)
return ret;
rockchip_drm_crtc_commit(crtc);
return 0;
}
static void rockchip_drm_crtc_load_lut(struct drm_crtc *crtc)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL */
}
static void rockchip_drm_crtc_disable(struct drm_crtc *crtc)
{
struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
DRM_DEBUG_KMS("%s\n", __FILE__);
rockchip_plane_dpms(rockchip_crtc->plane, DRM_MODE_DPMS_OFF);
rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
}
static struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
.dpms = rockchip_drm_crtc_dpms,
.prepare = rockchip_drm_crtc_prepare,
.commit = rockchip_drm_crtc_commit,
.mode_fixup = rockchip_drm_crtc_mode_fixup,
.mode_set = rockchip_drm_crtc_mode_set,
.mode_set_base = rockchip_drm_crtc_mode_set_base,
.load_lut = rockchip_drm_crtc_load_lut,
.disable = rockchip_drm_crtc_disable,
};
static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event)
{
struct drm_device *dev = crtc->dev;
struct rockchip_drm_private *dev_priv = dev->dev_private;
struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
struct drm_framebuffer *old_fb = crtc->fb;
int ret = -EINVAL;
DRM_DEBUG_KMS("%s\n", __FILE__);
/* when the page flip is requested, crtc's dpms should be on */
if (rockchip_crtc->dpms > DRM_MODE_DPMS_ON) {
DRM_ERROR("failed page flip request.\n");
return -EINVAL;
}
mutex_lock(&dev->struct_mutex);
if (event) {
/*
* the pipe from user always is 0 so we can set pipe number
* of current owner to event.
*/
event->pipe = rockchip_crtc->pipe;
ret = drm_vblank_get(dev, rockchip_crtc->pipe);
if (ret) {
DRM_DEBUG("failed to acquire vblank counter\n");
goto out;
}
spin_lock_irq(&dev->event_lock);
list_add_tail(&event->base.link,
&dev_priv->pageflip_event_list);
atomic_set(&rockchip_crtc->pending_flip, 1);
spin_unlock_irq(&dev->event_lock);
crtc->fb = fb;
ret = rockchip_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y,
NULL);
if (ret) {
crtc->fb = old_fb;
spin_lock_irq(&dev->event_lock);
drm_vblank_put(dev, rockchip_crtc->pipe);
list_del(&event->base.link);
spin_unlock_irq(&dev->event_lock);
goto out;
}
}
out:
mutex_unlock(&dev->struct_mutex);
return ret;
}
static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
{
struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
struct rockchip_drm_private *private = crtc->dev->dev_private;
DRM_DEBUG_KMS("%s\n", __FILE__);
private->crtc[rockchip_crtc->pipe] = NULL;
drm_crtc_cleanup(crtc);
kfree(rockchip_crtc);
}
static int rockchip_drm_crtc_set_property(struct drm_crtc *crtc,
struct drm_property *property,
uint64_t val)
{
struct drm_device *dev = crtc->dev;
struct rockchip_drm_private *dev_priv = dev->dev_private;
struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
DRM_DEBUG_KMS("%s\n", __func__);
if (property == dev_priv->crtc_mode_property) {
enum rockchip_crtc_mode mode = val;
if (mode == rockchip_crtc->mode)
return 0;
rockchip_crtc->mode = mode;
switch (mode) {
case CRTC_MODE_NORMAL:
rockchip_drm_crtc_commit(crtc);
break;
case CRTC_MODE_BLANK:
rockchip_plane_dpms(rockchip_crtc->plane,
DRM_MODE_DPMS_OFF);
break;
default:
break;
}
return 0;
}
return -EINVAL;
}
static struct drm_crtc_funcs rockchip_crtc_funcs = {
.set_config = drm_crtc_helper_set_config,
.page_flip = rockchip_drm_crtc_page_flip,
.destroy = rockchip_drm_crtc_destroy,
.set_property = rockchip_drm_crtc_set_property,
};
static const struct drm_prop_enum_list mode_names[] = {
{ CRTC_MODE_NORMAL, "normal" },
{ CRTC_MODE_BLANK, "blank" },
};
static void rockchip_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct rockchip_drm_private *dev_priv = dev->dev_private;
struct drm_property *prop;
DRM_DEBUG_KMS("%s\n", __func__);
prop = dev_priv->crtc_mode_property;
if (!prop) {
prop = drm_property_create_enum(dev, 0, "mode", mode_names,
ARRAY_SIZE(mode_names));
if (!prop)
return;
dev_priv->crtc_mode_property = prop;
}
drm_object_attach_property(&crtc->base, prop, 0);
}
int rockchip_drm_crtc_create(struct drm_device *dev, unsigned int nr)
{
struct rockchip_drm_crtc *rockchip_crtc;
struct rockchip_drm_private *private = dev->dev_private;
struct drm_crtc *crtc;
DRM_DEBUG_KMS("%s\n", __FILE__);
rockchip_crtc = kzalloc(sizeof(*rockchip_crtc), GFP_KERNEL);
if (!rockchip_crtc) {
DRM_ERROR("failed to allocate rockchip crtc\n");
return -ENOMEM;
}
rockchip_crtc->pipe = nr;
rockchip_crtc->dpms = DRM_MODE_DPMS_OFF;
init_waitqueue_head(&rockchip_crtc->pending_flip_queue);
atomic_set(&rockchip_crtc->pending_flip, 0);
rockchip_crtc->plane = rockchip_plane_init(dev, 1 << nr, true);
if (!rockchip_crtc->plane) {
kfree(rockchip_crtc);
return -ENOMEM;
}
crtc = &rockchip_crtc->drm_crtc;
private->crtc[nr] = crtc;
drm_crtc_init(dev, crtc, &rockchip_crtc_funcs);
drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
rockchip_drm_crtc_attach_mode_property(crtc);
return 0;
}
int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
{
struct rockchip_drm_private *private = dev->dev_private;
struct rockchip_drm_crtc *rockchip_crtc =
to_rockchip_crtc(private->crtc[crtc]);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (rockchip_crtc->dpms != DRM_MODE_DPMS_ON)
return -EPERM;
rockchip_drm_fn_encoder(private->crtc[crtc], &crtc,
rockchip_drm_enable_vblank);
return 0;
}
void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
{
struct rockchip_drm_private *private = dev->dev_private;
struct rockchip_drm_crtc *rockchip_crtc =
to_rockchip_crtc(private->crtc[crtc]);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (rockchip_crtc->dpms != DRM_MODE_DPMS_ON)
return;
rockchip_drm_fn_encoder(private->crtc[crtc], &crtc,
rockchip_drm_disable_vblank);
}
void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc)
{
struct rockchip_drm_private *dev_priv = dev->dev_private;
struct drm_pending_vblank_event *e, *t;
struct drm_crtc *drm_crtc = dev_priv->crtc[crtc];
struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(drm_crtc);
unsigned long flags;
DRM_DEBUG_KMS("%s\n", __FILE__);
spin_lock_irqsave(&dev->event_lock, flags);
list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
base.link) {
/* if event's pipe isn't same as crtc then ignore it. */
if (crtc != e->pipe)
continue;
list_del(&e->base.link);
drm_send_vblank_event(dev, -1, e);
drm_vblank_put(dev, crtc);
atomic_set(&rockchip_crtc->pending_flip, 0);
wake_up(&rockchip_crtc->pending_flip_queue);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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.
*/
#ifndef _ROCKCHIP_DRM_CRTC_H_
#define _ROCKCHIP_DRM_CRTC_H_
int rockchip_drm_crtc_create(struct drm_device *dev, unsigned int nr);
int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc);
#endif

View File

@ -0,0 +1,307 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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/drmP.h>
#include <drm/rockchip_drm.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_gem.h"
#include <linux/dma-buf.h>
struct rockchip_drm_dmabuf_attachment {
struct sg_table sgt;
enum dma_data_direction dir;
bool is_mapped;
};
static int rockchip_gem_attach_dma_buf(struct dma_buf *dmabuf,
struct device *dev,
struct dma_buf_attachment *attach)
{
struct rockchip_drm_dmabuf_attachment *rockchip_attach;
rockchip_attach = kzalloc(sizeof(*rockchip_attach), GFP_KERNEL);
if (!rockchip_attach)
return -ENOMEM;
rockchip_attach->dir = DMA_NONE;
attach->priv = rockchip_attach;
return 0;
}
static void rockchip_gem_detach_dma_buf(struct dma_buf *dmabuf,
struct dma_buf_attachment *attach)
{
struct rockchip_drm_dmabuf_attachment *rockchip_attach = attach->priv;
struct sg_table *sgt;
if (!rockchip_attach)
return;
sgt = &rockchip_attach->sgt;
if (rockchip_attach->dir != DMA_NONE)
dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents,
rockchip_attach->dir);
sg_free_table(sgt);
kfree(rockchip_attach);
attach->priv = NULL;
}
static struct sg_table *
rockchip_gem_map_dma_buf(struct dma_buf_attachment *attach,
enum dma_data_direction dir)
{
struct rockchip_drm_dmabuf_attachment *rockchip_attach = attach->priv;
struct rockchip_drm_gem_obj *gem_obj = attach->dmabuf->priv;
struct drm_device *dev = gem_obj->base.dev;
struct rockchip_drm_gem_buf *buf;
struct scatterlist *rd, *wr;
struct sg_table *sgt = NULL;
unsigned int i;
int nents, ret;
DRM_DEBUG_PRIME("%s\n", __FILE__);
/* just return current sgt if already requested. */
if (rockchip_attach->dir == dir && rockchip_attach->is_mapped)
return &rockchip_attach->sgt;
buf = gem_obj->buffer;
if (!buf) {
DRM_ERROR("buffer is null.\n");
return ERR_PTR(-ENOMEM);
}
sgt = &rockchip_attach->sgt;
ret = sg_alloc_table(sgt, buf->sgt->orig_nents, GFP_KERNEL);
if (ret) {
DRM_ERROR("failed to alloc sgt.\n");
return ERR_PTR(-ENOMEM);
}
mutex_lock(&dev->struct_mutex);
rd = buf->sgt->sgl;
wr = sgt->sgl;
for (i = 0; i < sgt->orig_nents; ++i) {
sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
rd = sg_next(rd);
wr = sg_next(wr);
}
if (dir != DMA_NONE) {
nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir);
if (!nents) {
DRM_ERROR("failed to map sgl with iommu.\n");
sg_free_table(sgt);
sgt = ERR_PTR(-EIO);
goto err_unlock;
}
}
rockchip_attach->is_mapped = true;
rockchip_attach->dir = dir;
attach->priv = rockchip_attach;
DRM_DEBUG_PRIME("buffer size = 0x%lx\n", buf->size);
err_unlock:
mutex_unlock(&dev->struct_mutex);
return sgt;
}
static void rockchip_gem_unmap_dma_buf(struct dma_buf_attachment *attach,
struct sg_table *sgt,
enum dma_data_direction dir)
{
/* Nothing to do. */
}
static void rockchip_dmabuf_release(struct dma_buf *dmabuf)
{
struct rockchip_drm_gem_obj *rockchip_gem_obj = dmabuf->priv;
DRM_DEBUG_PRIME("%s\n", __FILE__);
/*
* rockchip_dmabuf_release() call means that file object's
* f_count is 0 and it calls drm_gem_object_handle_unreference()
* to drop the references that these values had been increased
* at drm_prime_handle_to_fd()
*/
if (rockchip_gem_obj->base.export_dma_buf == dmabuf) {
rockchip_gem_obj->base.export_dma_buf = NULL;
/*
* drop this gem object refcount to release allocated buffer
* and resources.
*/
drm_gem_object_unreference_unlocked(&rockchip_gem_obj->base);
}
}
static void *rockchip_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf,
unsigned long page_num)
{
/* TODO */
return NULL;
}
static void rockchip_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf,
unsigned long page_num,
void *addr)
{
/* TODO */
}
static void *rockchip_gem_dmabuf_kmap(struct dma_buf *dma_buf,
unsigned long page_num)
{
/* TODO */
return NULL;
}
static void rockchip_gem_dmabuf_kunmap(struct dma_buf *dma_buf,
unsigned long page_num, void *addr)
{
/* TODO */
}
static int rockchip_gem_dmabuf_mmap(struct dma_buf *dma_buf,
struct vm_area_struct *vma)
{
return -ENOTTY;
}
static struct dma_buf_ops rockchip_dmabuf_ops = {
.attach = rockchip_gem_attach_dma_buf,
.detach = rockchip_gem_detach_dma_buf,
.map_dma_buf = rockchip_gem_map_dma_buf,
.unmap_dma_buf = rockchip_gem_unmap_dma_buf,
.kmap = rockchip_gem_dmabuf_kmap,
.kmap_atomic = rockchip_gem_dmabuf_kmap_atomic,
.kunmap = rockchip_gem_dmabuf_kunmap,
.kunmap_atomic = rockchip_gem_dmabuf_kunmap_atomic,
.mmap = rockchip_gem_dmabuf_mmap,
.release = rockchip_dmabuf_release,
};
struct dma_buf *rockchip_dmabuf_prime_export(struct drm_device *drm_dev,
struct drm_gem_object *obj, int flags)
{
struct rockchip_drm_gem_obj *rockchip_gem_obj = to_rockchip_gem_obj(obj);
return dma_buf_export(rockchip_gem_obj, &rockchip_dmabuf_ops,
rockchip_gem_obj->base.size, flags);
}
struct drm_gem_object *rockchip_dmabuf_prime_import(struct drm_device *drm_dev,
struct dma_buf *dma_buf)
{
struct dma_buf_attachment *attach;
struct sg_table *sgt;
struct scatterlist *sgl;
struct rockchip_drm_gem_obj *rockchip_gem_obj;
struct rockchip_drm_gem_buf *buffer;
int ret;
DRM_DEBUG_PRIME("%s\n", __FILE__);
/* is this one of own objects? */
if (dma_buf->ops == &rockchip_dmabuf_ops) {
struct drm_gem_object *obj;
rockchip_gem_obj = dma_buf->priv;
obj = &rockchip_gem_obj->base;
/* is it from our device? */
if (obj->dev == drm_dev) {
/*
* Importing dmabuf exported from out own gem increases
* refcount on gem itself instead of f_count of dmabuf.
*/
drm_gem_object_reference(obj);
return obj;
}
}
attach = dma_buf_attach(dma_buf, drm_dev->dev);
if (IS_ERR(attach))
return ERR_PTR(-EINVAL);
get_dma_buf(dma_buf);
sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
if (IS_ERR_OR_NULL(sgt)) {
ret = PTR_ERR(sgt);
goto err_buf_detach;
}
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
if (!buffer) {
DRM_ERROR("failed to allocate rockchip_drm_gem_buf.\n");
ret = -ENOMEM;
goto err_unmap_attach;
}
rockchip_gem_obj = rockchip_drm_gem_init(drm_dev, dma_buf->size);
if (!rockchip_gem_obj) {
ret = -ENOMEM;
goto err_free_buffer;
}
sgl = sgt->sgl;
buffer->size = dma_buf->size;
buffer->dma_addr = sg_dma_address(sgl);
if (sgt->nents == 1) {
/* always physically continuous memory if sgt->nents is 1. */
rockchip_gem_obj->flags |= ROCKCHIP_BO_CONTIG;
} else {
/*
* this case could be CONTIG or NONCONTIG type but for now
* sets NONCONTIG.
* TODO. we have to find a way that exporter can notify
* the type of its own buffer to importer.
*/
rockchip_gem_obj->flags |= ROCKCHIP_BO_NONCONTIG;
}
rockchip_gem_obj->buffer = buffer;
buffer->sgt = sgt;
rockchip_gem_obj->base.import_attach = attach;
DRM_DEBUG_PRIME("dma_addr = 0x%x, size = 0x%lx\n", buffer->dma_addr,
buffer->size);
return &rockchip_gem_obj->base;
err_free_buffer:
kfree(buffer);
buffer = NULL;
err_unmap_attach:
dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
err_buf_detach:
dma_buf_detach(dma_buf, attach);
dma_buf_put(dma_buf);
return ERR_PTR(ret);
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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.
*/
#ifndef _ROCKCHIP_DRM_DMABUF_H_
#define _ROCKCHIP_DRM_DMABUF_H_
#ifdef CONFIG_DRM_ROCKCHIP_DMABUF
struct dma_buf *rockchip_dmabuf_prime_export(struct drm_device *drm_dev,
struct drm_gem_object *obj, int flags);
struct drm_gem_object *rockchip_dmabuf_prime_import(struct drm_device *drm_dev,
struct dma_buf *dma_buf);
#else
#define rockchip_dmabuf_prime_export NULL
#define rockchip_dmabuf_prime_import NULL
#endif
#endif

View File

@ -0,0 +1,374 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/rockchip_drm.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_crtc.h"
#include "rockchip_drm_encoder.h"
#include "rockchip_drm_fbdev.h"
#include "rockchip_drm_fb.h"
#include "rockchip_drm_gem.h"
#include "rockchip_drm_plane.h"
#include "rockchip_drm_dmabuf.h"
#include "rockchip_drm_iommu.h"
#define DRIVER_NAME "rockchip"
#define DRIVER_DESC "rockchip Soc DRM"
#define DRIVER_DATE "20140318"
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
#define VBLANK_OFF_DELAY 50000
/* platform device pointer for eynos drm device. */
static struct platform_device *rockchip_drm_pdev;
static int rockchip_drm_load(struct drm_device *dev, unsigned long flags)
{
struct rockchip_drm_private *private;
int ret;
int nr;
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
DRM_DEBUG_DRIVER("%s\n", __FILE__);
private = kzalloc(sizeof(struct rockchip_drm_private), GFP_KERNEL);
if (!private) {
DRM_ERROR("failed to allocate private\n");
return -ENOMEM;
}
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
INIT_LIST_HEAD(&private->pageflip_event_list);
dev->dev_private = (void *)private;
/*
* create mapping to manage iommu table and set a pointer to iommu
* mapping structure to iommu_mapping of private data.
* also this iommu_mapping can be used to check if iommu is supported
* or not.
*/
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
ret = drm_create_iommu_mapping(dev);
if (ret < 0) {
DRM_ERROR("failed to create iommu mapping.\n");
goto err_crtc;
}
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
drm_mode_config_init(dev);
/* init kms poll for handling hpd */
drm_kms_helper_poll_init(dev);
rockchip_drm_mode_config_init(dev);
/*
* ROCKCHIP4 is enough to have two CRTCs and each crtc would be used
* without dependency of hardware.
*/
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
for (nr = 0; nr < MAX_CRTC; nr++) {
ret = rockchip_drm_crtc_create(dev, nr);
if (ret)
goto err_release_iommu_mapping;
}
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
for (nr = 0; nr < MAX_PLANE; nr++) {
struct drm_plane *plane;
unsigned int possible_crtcs = (1 << MAX_CRTC) - 1;
plane = rockchip_plane_init(dev, possible_crtcs, false);
if (!plane)
goto err_release_iommu_mapping;
}
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
ret = drm_vblank_init(dev, MAX_CRTC);
if (ret)
goto err_release_iommu_mapping;
/*
* probe sub drivers such as display controller and hdmi driver,
* that were registered at probe() of platform driver
* to the sub driver and create encoder and connector for them.
*/
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
ret = rockchip_drm_device_register(dev);
if (ret)
goto err_vblank;
/* setup possible_clones. */
rockchip_drm_encoder_setup(dev);
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
/*
* create and configure fb helper and also rockchip specific
* fbdev object.
*/
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
ret = rockchip_drm_fbdev_init(dev);
if (ret) {
DRM_ERROR("failed to initialize drm fbdev\n");
goto err_drm_device;
}
drm_vblank_offdelay = VBLANK_OFF_DELAY;
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
return 0;
err_drm_device:
rockchip_drm_device_unregister(dev);
err_vblank:
drm_vblank_cleanup(dev);
err_release_iommu_mapping:
drm_release_iommu_mapping(dev);
err_crtc:
drm_mode_config_cleanup(dev);
kfree(private);
return ret;
}
static int rockchip_drm_unload(struct drm_device *dev)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
rockchip_drm_fbdev_fini(dev);
rockchip_drm_device_unregister(dev);
drm_vblank_cleanup(dev);
drm_kms_helper_poll_fini(dev);
drm_mode_config_cleanup(dev);
drm_release_iommu_mapping(dev);
kfree(dev->dev_private);
dev->dev_private = NULL;
return 0;
}
static int rockchip_drm_open(struct drm_device *dev, struct drm_file *file)
{
struct drm_rockchip_file_private *file_priv;
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
DRM_DEBUG_DRIVER("%s\n", __FILE__);
file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
if (!file_priv)
return -ENOMEM;
file->driver_priv = file_priv;
return rockchip_drm_subdrv_open(dev, file);
}
static void rockchip_drm_preclose(struct drm_device *dev,
struct drm_file *file)
{
struct rockchip_drm_private *private = dev->dev_private;
struct drm_pending_vblank_event *e, *t;
unsigned long flags;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
/* release events of current file */
spin_lock_irqsave(&dev->event_lock, flags);
list_for_each_entry_safe(e, t, &private->pageflip_event_list,
base.link) {
if (e->base.file_priv == file) {
list_del(&e->base.link);
e->base.destroy(&e->base);
}
}
spin_unlock_irqrestore(&dev->event_lock, flags);
rockchip_drm_subdrv_close(dev, file);
}
static void rockchip_drm_postclose(struct drm_device *dev, struct drm_file *file)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!file->driver_priv)
return;
kfree(file->driver_priv);
file->driver_priv = NULL;
}
static void rockchip_drm_lastclose(struct drm_device *dev)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
rockchip_drm_fbdev_restore_mode(dev);
}
static const struct vm_operations_struct rockchip_drm_gem_vm_ops = {
.fault = rockchip_drm_gem_fault,
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
};
static struct drm_ioctl_desc rockchip_ioctls[] = {
DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_drm_gem_create_ioctl,
DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
rockchip_drm_gem_map_offset_ioctl, DRM_UNLOCKED |
DRM_AUTH),
DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MMAP,
rockchip_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET,
rockchip_drm_gem_get_ioctl, DRM_UNLOCKED),
};
static const struct file_operations rockchip_drm_driver_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.mmap = rockchip_drm_gem_mmap,
.poll = drm_poll,
.read = drm_read,
.unlocked_ioctl = drm_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = drm_compat_ioctl,
#endif
.release = drm_release,
};
static struct drm_driver rockchip_drm_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET |
DRIVER_GEM | DRIVER_PRIME,
.load = rockchip_drm_load,
.unload = rockchip_drm_unload,
.open = rockchip_drm_open,
.preclose = rockchip_drm_preclose,
.lastclose = rockchip_drm_lastclose,
.postclose = rockchip_drm_postclose,
.get_vblank_counter = drm_vblank_count,
.enable_vblank = rockchip_drm_crtc_enable_vblank,
.disable_vblank = rockchip_drm_crtc_disable_vblank,
.gem_init_object = rockchip_drm_gem_init_object,
.gem_free_object = rockchip_drm_gem_free_object,
.gem_vm_ops = &rockchip_drm_gem_vm_ops,
.dumb_create = rockchip_drm_gem_dumb_create,
.dumb_map_offset = rockchip_drm_gem_dumb_map_offset,
.dumb_destroy = rockchip_drm_gem_dumb_destroy,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_export = rockchip_dmabuf_prime_export,
.gem_prime_import = rockchip_dmabuf_prime_import,
.ioctls = rockchip_ioctls,
.fops = &rockchip_drm_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
};
static int rockchip_drm_platform_probe(struct platform_device *pdev)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
rockchip_drm_driver.num_ioctls = DRM_ARRAY_SIZE(rockchip_ioctls);
return drm_platform_init(&rockchip_drm_driver, pdev);
}
static int rockchip_drm_platform_remove(struct platform_device *pdev)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
drm_platform_exit(&rockchip_drm_driver, pdev);
return 0;
}
static struct platform_driver rockchip_drm_platform_driver = {
.probe = rockchip_drm_platform_probe,
.remove = rockchip_drm_platform_remove,
.driver = {
.owner = THIS_MODULE,
.name = "rockchip-drm",
},
};
static int __init rockchip_drm_init(void)
{
int ret;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
#ifdef CONFIG_DRM_RK3288_FIMD
ret = platform_driver_register(&fimd_driver);
if (ret < 0)
goto out_fimd;
#endif
#ifdef CONFIG_DRM_RK3188_FIMD
ret = platform_driver_register(&fimd_driver);
if (ret < 0)
goto out_fimd;
#endif
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
ret = platform_driver_register(&rockchip_drm_platform_driver);
if (ret < 0)
goto out_drm;
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
rockchip_drm_pdev = platform_device_register_simple("rockchip-drm", -1,
NULL, 0);
if (IS_ERR(rockchip_drm_pdev)) {
ret = PTR_ERR(rockchip_drm_pdev);
goto out;
}
printk(KERN_ERR"----->yzq %s %d\n",__func__,__LINE__);
return 0;
out:
platform_driver_unregister(&rockchip_drm_platform_driver);
out_drm:
#ifdef CONFIG_DRM_RK3188_FIMD
platform_driver_unregister(&fimd_driver);
out_fimd:
#endif
#ifdef CONFIG_DRM_RK3288_FIMD
platform_driver_unregister(&fimd_driver);
out_fimd:
#endif
return ret;
}
static void __exit rockchip_drm_exit(void)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
platform_device_unregister(rockchip_drm_pdev);
platform_driver_unregister(&rockchip_drm_platform_driver);
}
module_init(rockchip_drm_init);
module_exit(rockchip_drm_exit);

View File

@ -0,0 +1,350 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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.
*/
#ifndef _ROCKCHIP_DRM_DRV_H_
#define _ROCKCHIP_DRM_DRV_H_
#include <linux/module.h>
#define MAX_CRTC 3
#define MAX_PLANE 5
#define MAX_FB_BUFFER 4
#define DEFAULT_ZPOS -1
#define _wait_for(COND, MS) ({ \
unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \
int ret__ = 0; \
while (!(COND)) { \
if (time_after(jiffies, timeout__)) { \
ret__ = -ETIMEDOUT; \
break; \
} \
} \
ret__; \
})
#define wait_for(COND, MS) _wait_for(COND, MS)
struct drm_device;
struct rockchip_drm_overlay;
struct drm_connector;
extern unsigned int drm_vblank_offdelay;
/* this enumerates display type. */
enum rockchip_drm_output_type {
ROCKCHIP_DISPLAY_TYPE_NONE,
/* RGB or CPU Interface. */
ROCKCHIP_DISPLAY_TYPE_LCD,
/* HDMI Interface. */
ROCKCHIP_DISPLAY_TYPE_HDMI,
/* Virtual Display Interface. */
ROCKCHIP_DISPLAY_TYPE_VIDI,
};
/*
* Exynos drm overlay ops structure.
*
* @mode_set: copy drm overlay info to hw specific overlay info.
* @commit: apply hardware specific overlay data to registers.
* @enable: enable hardware specific overlay.
* @disable: disable hardware specific overlay.
*/
struct rockchip_drm_overlay_ops {
void (*mode_set)(struct device *subdrv_dev,
struct rockchip_drm_overlay *overlay);
void (*commit)(struct device *subdrv_dev, int zpos);
void (*enable)(struct device *subdrv_dev, int zpos);
void (*disable)(struct device *subdrv_dev, int zpos);
};
/*
* Exynos drm common overlay structure.
*
* @fb_x: offset x on a framebuffer to be displayed.
* - the unit is screen coordinates.
* @fb_y: offset y on a framebuffer to be displayed.
* - the unit is screen coordinates.
* @fb_width: width of a framebuffer.
* @fb_height: height of a framebuffer.
* @src_width: width of a partial image to be displayed from framebuffer.
* @src_height: height of a partial image to be displayed from framebuffer.
* @crtc_x: offset x on hardware screen.
* @crtc_y: offset y on hardware screen.
* @crtc_width: window width to be displayed (hardware screen).
* @crtc_height: window height to be displayed (hardware screen).
* @mode_width: width of screen mode.
* @mode_height: height of screen mode.
* @refresh: refresh rate.
* @scan_flag: interlace or progressive way.
* (it could be DRM_MODE_FLAG_*)
* @bpp: pixel size.(in bit)
* @pixel_format: fourcc pixel format of this overlay
* @dma_addr: array of bus(accessed by dma) address to the memory region
* allocated for a overlay.
* @zpos: order of overlay layer(z position).
* @default_win: a window to be enabled.
* @color_key: color key on or off.
* @index_color: if using color key feature then this value would be used
* as index color.
* @local_path: in case of lcd type, local path mode on or off.
* @transparency: transparency on or off.
* @activated: activated or not.
*
* this structure is common to rockchip SoC and its contents would be copied
* to hardware specific overlay info.
*/
struct rockchip_drm_overlay {
unsigned int fb_x;
unsigned int fb_y;
unsigned int fb_width;
unsigned int fb_height;
unsigned int src_width;
unsigned int src_height;
unsigned int crtc_x;
unsigned int crtc_y;
unsigned int crtc_width;
unsigned int crtc_height;
unsigned int mode_width;
unsigned int mode_height;
unsigned int refresh;
unsigned int scan_flag;
unsigned int bpp;
unsigned int pitch;
uint32_t pixel_format;
dma_addr_t dma_addr[MAX_FB_BUFFER];
int zpos;
bool default_win;
bool color_key;
unsigned int index_color;
bool local_path;
bool transparency;
bool activated;
};
/*
* Exynos DRM Display Structure.
* - this structure is common to analog tv, digital tv and lcd panel.
*
* @type: one of ROCKCHIP_DISPLAY_TYPE_LCD and HDMI.
* @is_connected: check for that display is connected or not.
* @get_edid: get edid modes from display driver.
* @get_panel: get panel object from display driver.
* @check_timing: check if timing is valid or not.
* @power_on: display device on or off.
*/
struct rockchip_drm_display_ops {
enum rockchip_drm_output_type type;
bool (*is_connected)(struct device *dev);
struct edid *(*get_edid)(struct device *dev,
struct drm_connector *connector);
void *(*get_panel)(struct device *dev);
int (*check_timing)(struct device *dev, void *timing);
int (*power_on)(struct device *dev, int mode);
};
/*
* Exynos drm manager ops
*
* @dpms: control device power.
* @apply: set timing, vblank and overlay data to registers.
* @mode_fixup: fix mode data comparing to hw specific display mode.
* @mode_set: convert drm_display_mode to hw specific display mode and
* would be called by encoder->mode_set().
* @get_max_resol: get maximum resolution to specific hardware.
* @commit: set current hw specific display mode to hw.
* @enable_vblank: specific driver callback for enabling vblank interrupt.
* @disable_vblank: specific driver callback for disabling vblank interrupt.
* @wait_for_vblank: wait for vblank interrupt to make sure that
* hardware overlay is updated.
*/
struct rockchip_drm_manager_ops {
void (*dpms)(struct device *subdrv_dev, int mode);
void (*apply)(struct device *subdrv_dev);
void (*mode_fixup)(struct device *subdrv_dev,
struct drm_connector *connector,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
void (*mode_set)(struct device *subdrv_dev, void *mode);
void (*get_max_resol)(struct device *subdrv_dev, unsigned int *width,
unsigned int *height);
void (*commit)(struct device *subdrv_dev);
int (*enable_vblank)(struct device *subdrv_dev);
void (*disable_vblank)(struct device *subdrv_dev);
void (*wait_for_vblank)(struct device *subdrv_dev);
};
/*
* Exynos drm common manager structure.
*
* @dev: pointer to device object for subdrv device driver.
* sub drivers such as display controller or hdmi driver,
* have their own device object.
* @ops: pointer to callbacks for rockchip drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control hardware global registers.
* @overlay_ops: pointer to callbacks for rockchip drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control hardware overlay reigsters.
* @display: pointer to callbacks for rockchip drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control display devices such as
* analog tv, digital tv and lcd panel and also get timing data for them.
*/
struct rockchip_drm_manager {
struct device *dev;
int pipe;
struct rockchip_drm_manager_ops *ops;
struct rockchip_drm_overlay_ops *overlay_ops;
struct rockchip_drm_display_ops *display_ops;
};
struct rockchip_drm_g2d_private {
struct device *dev;
struct list_head inuse_cmdlist;
struct list_head event_list;
struct list_head userptr_list;
};
struct rockchip_drm_ipp_private {
struct device *dev;
struct list_head event_list;
};
struct drm_rockchip_file_private {
struct rockchip_drm_g2d_private *g2d_priv;
struct rockchip_drm_ipp_private *ipp_priv;
};
/*
* Exynos drm private structure.
*
* @da_start: start address to device address space.
* with iommu, device address space starts from this address
* otherwise default one.
* @da_space_size: size of device address space.
* if 0 then default value is used for it.
* @da_space_order: order to device address space.
*/
struct rockchip_drm_private {
struct drm_fb_helper *fb_helper;
/* list head for new event to be added. */
struct list_head pageflip_event_list;
/*
* created crtc object would be contained at this array and
* this array is used to be aware of which crtc did it request vblank.
*/
struct drm_crtc *crtc[MAX_CRTC];
struct drm_property *plane_zpos_property;
struct drm_property *crtc_mode_property;
unsigned long da_start;
unsigned long da_space_size;
unsigned long da_space_order;
};
/*
* Exynos drm sub driver structure.
*
* @list: sub driver has its own list object to register to rockchip drm driver.
* @dev: pointer to device object for subdrv device driver.
* @drm_dev: pointer to drm_device and this pointer would be set
* when sub driver calls rockchip_drm_subdrv_register().
* @manager: subdrv has its own manager to control a hardware appropriately
* and we can access a hardware drawing on this manager.
* @probe: this callback would be called by rockchip drm driver after
* subdrv is registered to it.
* @remove: this callback is used to release resources created
* by probe callback.
* @open: this would be called with drm device file open.
* @close: this would be called with drm device file close.
* @encoder: encoder object owned by this sub driver.
* @connector: connector object owned by this sub driver.
*/
struct rockchip_drm_subdrv {
struct list_head list;
struct device *dev;
struct drm_device *drm_dev;
struct rockchip_drm_manager *manager;
int (*probe)(struct drm_device *drm_dev, struct device *dev);
void (*remove)(struct drm_device *drm_dev, struct device *dev);
int (*open)(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file);
void (*close)(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file);
struct drm_encoder *encoder;
struct drm_connector *connector;
};
/*
* this function calls a probe callback registered to sub driver list and
* create its own encoder and connector and then set drm_device object
* to global one.
*/
int rockchip_drm_device_register(struct drm_device *dev);
/*
* this function calls a remove callback registered to sub driver list and
* destroy its own encoder and connetor.
*/
int rockchip_drm_device_unregister(struct drm_device *dev);
/*
* this function would be called by sub drivers such as display controller
* or hdmi driver to register this sub driver object to rockchip drm driver
* and when a sub driver is registered to rockchip drm driver a probe callback
* of the sub driver is called and creates its own encoder and connector.
*/
int rockchip_drm_subdrv_register(struct rockchip_drm_subdrv *drm_subdrv);
/* this function removes subdrv list from rockchip drm driver */
int rockchip_drm_subdrv_unregister(struct rockchip_drm_subdrv *drm_subdrv);
int rockchip_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
void rockchip_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
/*
* this function registers rockchip drm hdmi platform device. It ensures only one
* instance of the device is created.
*/
int rockchip_platform_device_hdmi_register(void);
/*
* this function unregisters rockchip drm hdmi platform device if it exists.
*/
void rockchip_platform_device_hdmi_unregister(void);
/*
* this function registers rockchip drm ipp platform device.
*/
int rockchip_platform_device_ipp_register(void);
/*
* this function unregisters rockchip drm ipp platform device if it exists.
*/
void rockchip_platform_device_ipp_unregister(void);
extern struct platform_driver fimd_driver;
extern struct platform_driver hdmi_driver;
extern struct platform_driver mixer_driver;
extern struct platform_driver rockchip_drm_common_hdmi_driver;
extern struct platform_driver vidi_driver;
extern struct platform_driver g2d_driver;
extern struct platform_driver fimc_driver;
extern struct platform_driver rotator_driver;
extern struct platform_driver gsc_driver;
extern struct platform_driver ipp_driver;
#endif

View File

@ -0,0 +1,518 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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/drmP.h>
#include <drm/drm_crtc_helper.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_encoder.h"
#include "rockchip_drm_connector.h"
#define to_rockchip_encoder(x) container_of(x, struct rockchip_drm_encoder,\
drm_encoder)
/*
* rockchip specific encoder structure.
*
* @drm_encoder: encoder object.
* @manager: specific encoder has its own manager to control a hardware
* appropriately and we can access a hardware drawing on this manager.
* @dpms: store the encoder dpms value.
* @updated: indicate whether overlay data updating is needed or not.
*/
struct rockchip_drm_encoder {
struct drm_crtc *old_crtc;
struct drm_encoder drm_encoder;
struct rockchip_drm_manager *manager;
int dpms;
bool updated;
};
static void rockchip_drm_connector_power(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (rockchip_drm_best_encoder(connector) == encoder) {
DRM_DEBUG_KMS("connector[%d] dpms[%d]\n",
connector->base.id, mode);
rockchip_drm_display_power(connector, mode);
}
}
}
static void rockchip_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct rockchip_drm_manager *manager = rockchip_drm_get_manager(encoder);
struct rockchip_drm_manager_ops *manager_ops = manager->ops;
struct rockchip_drm_encoder *rockchip_encoder = to_rockchip_encoder(encoder);
DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode);
if (rockchip_encoder->dpms == mode) {
DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
return;
}
mutex_lock(&dev->struct_mutex);
switch (mode) {
case DRM_MODE_DPMS_ON:
if (manager_ops && manager_ops->apply)
if (!rockchip_encoder->updated)
manager_ops->apply(manager->dev);
rockchip_drm_connector_power(encoder, mode);
rockchip_encoder->dpms = mode;
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
rockchip_drm_connector_power(encoder, mode);
rockchip_encoder->dpms = mode;
rockchip_encoder->updated = false;
break;
default:
DRM_ERROR("unspecified mode %d\n", mode);
break;
}
mutex_unlock(&dev->struct_mutex);
}
static bool
rockchip_drm_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
struct rockchip_drm_manager *manager = rockchip_drm_get_manager(encoder);
struct rockchip_drm_manager_ops *manager_ops = manager->ops;
DRM_DEBUG_KMS("%s\n", __FILE__);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder)
if (manager_ops && manager_ops->mode_fixup)
manager_ops->mode_fixup(manager->dev, connector,
mode, adjusted_mode);
}
return true;
}
static void disable_plane_to_crtc(struct drm_device *dev,
struct drm_crtc *old_crtc,
struct drm_crtc *new_crtc)
{
struct drm_plane *plane;
/*
* if old_crtc isn't same as encoder->crtc then it means that
* user changed crtc id to another one so the plane to old_crtc
* should be disabled and plane->crtc should be set to new_crtc
* (encoder->crtc)
*/
list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
if (plane->crtc == old_crtc) {
/*
* do not change below call order.
*
* plane->funcs->disable_plane call checks
* if encoder->crtc is same as plane->crtc and if same
* then overlay_ops->disable callback will be called
* to diasble current hw overlay so plane->crtc should
* have new_crtc because new_crtc was set to
* encoder->crtc in advance.
*/
plane->crtc = new_crtc;
plane->funcs->disable_plane(plane);
}
}
}
static void rockchip_drm_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
struct rockchip_drm_manager *manager;
struct rockchip_drm_manager_ops *manager_ops;
DRM_DEBUG_KMS("%s\n", __FILE__);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder) {
struct rockchip_drm_encoder *rockchip_encoder;
rockchip_encoder = to_rockchip_encoder(encoder);
if (rockchip_encoder->old_crtc != encoder->crtc &&
rockchip_encoder->old_crtc) {
/*
* disable a plane to old crtc and change
* crtc of the plane to new one.
*/
disable_plane_to_crtc(dev,
rockchip_encoder->old_crtc,
encoder->crtc);
}
manager = rockchip_drm_get_manager(encoder);
manager_ops = manager->ops;
if (manager_ops && manager_ops->mode_set)
manager_ops->mode_set(manager->dev,
adjusted_mode);
rockchip_encoder->old_crtc = encoder->crtc;
}
}
}
static void rockchip_drm_encoder_prepare(struct drm_encoder *encoder)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL. */
}
static void rockchip_drm_encoder_commit(struct drm_encoder *encoder)
{
struct rockchip_drm_encoder *rockchip_encoder = to_rockchip_encoder(encoder);
struct rockchip_drm_manager *manager = rockchip_encoder->manager;
struct rockchip_drm_manager_ops *manager_ops = manager->ops;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (manager_ops && manager_ops->commit)
manager_ops->commit(manager->dev);
/*
* this will avoid one issue that overlay data is updated to
* real hardware two times.
* And this variable will be used to check if the data was
* already updated or not by rockchip_drm_encoder_dpms function.
*/
rockchip_encoder->updated = true;
/*
* In case of setcrtc, there is no way to update encoder's dpms
* so update it here.
*/
rockchip_encoder->dpms = DRM_MODE_DPMS_ON;
}
void rockchip_drm_encoder_complete_scanout(struct drm_framebuffer *fb)
{
struct rockchip_drm_encoder *rockchip_encoder;
struct rockchip_drm_manager_ops *ops;
struct drm_device *dev = fb->dev;
struct drm_encoder *encoder;
/*
* make sure that overlay data are updated to real hardware
* for all encoders.
*/
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
rockchip_encoder = to_rockchip_encoder(encoder);
ops = rockchip_encoder->manager->ops;
/*
* wait for vblank interrupt
* - this makes sure that overlay data are updated to
* real hardware.
*/
if (ops->wait_for_vblank)
ops->wait_for_vblank(rockchip_encoder->manager->dev);
}
}
static void rockchip_drm_encoder_disable(struct drm_encoder *encoder)
{
struct drm_plane *plane;
struct drm_device *dev = encoder->dev;
rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
/* all planes connected to this encoder should be also disabled. */
list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
if (plane->crtc == encoder->crtc)
plane->funcs->disable_plane(plane);
}
}
static struct drm_encoder_helper_funcs rockchip_encoder_helper_funcs = {
.dpms = rockchip_drm_encoder_dpms,
.mode_fixup = rockchip_drm_encoder_mode_fixup,
.mode_set = rockchip_drm_encoder_mode_set,
.prepare = rockchip_drm_encoder_prepare,
.commit = rockchip_drm_encoder_commit,
.disable = rockchip_drm_encoder_disable,
};
static void rockchip_drm_encoder_destroy(struct drm_encoder *encoder)
{
struct rockchip_drm_encoder *rockchip_encoder =
to_rockchip_encoder(encoder);
DRM_DEBUG_KMS("%s\n", __FILE__);
rockchip_encoder->manager->pipe = -1;
drm_encoder_cleanup(encoder);
kfree(rockchip_encoder);
}
static struct drm_encoder_funcs rockchip_encoder_funcs = {
.destroy = rockchip_drm_encoder_destroy,
};
static unsigned int rockchip_drm_encoder_clones(struct drm_encoder *encoder)
{
struct drm_encoder *clone;
struct drm_device *dev = encoder->dev;
struct rockchip_drm_encoder *rockchip_encoder = to_rockchip_encoder(encoder);
struct rockchip_drm_display_ops *display_ops =
rockchip_encoder->manager->display_ops;
unsigned int clone_mask = 0;
int cnt = 0;
list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
switch (display_ops->type) {
case ROCKCHIP_DISPLAY_TYPE_LCD:
case ROCKCHIP_DISPLAY_TYPE_HDMI:
case ROCKCHIP_DISPLAY_TYPE_VIDI:
clone_mask |= (1 << (cnt++));
break;
default:
continue;
}
}
return clone_mask;
}
void rockchip_drm_encoder_setup(struct drm_device *dev)
{
struct drm_encoder *encoder;
DRM_DEBUG_KMS("%s\n", __FILE__);
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
encoder->possible_clones = rockchip_drm_encoder_clones(encoder);
}
struct drm_encoder *
rockchip_drm_encoder_create(struct drm_device *dev,
struct rockchip_drm_manager *manager,
unsigned int possible_crtcs)
{
struct drm_encoder *encoder;
struct rockchip_drm_encoder *rockchip_encoder;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!manager || !possible_crtcs)
return NULL;
if (!manager->dev)
return NULL;
rockchip_encoder = kzalloc(sizeof(*rockchip_encoder), GFP_KERNEL);
if (!rockchip_encoder) {
DRM_ERROR("failed to allocate encoder\n");
return NULL;
}
rockchip_encoder->dpms = DRM_MODE_DPMS_OFF;
rockchip_encoder->manager = manager;
encoder = &rockchip_encoder->drm_encoder;
encoder->possible_crtcs = possible_crtcs;
DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
drm_encoder_init(dev, encoder, &rockchip_encoder_funcs,
DRM_MODE_ENCODER_TMDS);
drm_encoder_helper_add(encoder, &rockchip_encoder_helper_funcs);
DRM_DEBUG_KMS("encoder has been created\n");
return encoder;
}
struct rockchip_drm_manager *rockchip_drm_get_manager(struct drm_encoder *encoder)
{
return to_rockchip_encoder(encoder)->manager;
}
void rockchip_drm_fn_encoder(struct drm_crtc *crtc, void *data,
void (*fn)(struct drm_encoder *, void *))
{
struct drm_device *dev = crtc->dev;
struct drm_encoder *encoder;
struct rockchip_drm_private *private = dev->dev_private;
struct rockchip_drm_manager *manager;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
/*
* if crtc is detached from encoder, check pipe,
* otherwise check crtc attached to encoder
*/
if (!encoder->crtc) {
manager = to_rockchip_encoder(encoder)->manager;
if (manager->pipe < 0 ||
private->crtc[manager->pipe] != crtc)
continue;
} else {
if (encoder->crtc != crtc)
continue;
}
fn(encoder, data);
}
}
void rockchip_drm_enable_vblank(struct drm_encoder *encoder, void *data)
{
struct rockchip_drm_manager *manager =
to_rockchip_encoder(encoder)->manager;
struct rockchip_drm_manager_ops *manager_ops = manager->ops;
int crtc = *(int *)data;
if (manager->pipe != crtc)
return;
if (manager_ops->enable_vblank)
manager_ops->enable_vblank(manager->dev);
}
void rockchip_drm_disable_vblank(struct drm_encoder *encoder, void *data)
{
struct rockchip_drm_manager *manager =
to_rockchip_encoder(encoder)->manager;
struct rockchip_drm_manager_ops *manager_ops = manager->ops;
int crtc = *(int *)data;
if (manager->pipe != crtc)
return;
if (manager_ops->disable_vblank)
manager_ops->disable_vblank(manager->dev);
}
void rockchip_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data)
{
struct rockchip_drm_encoder *rockchip_encoder = to_rockchip_encoder(encoder);
struct rockchip_drm_manager *manager = rockchip_encoder->manager;
struct rockchip_drm_manager_ops *manager_ops = manager->ops;
int mode = *(int *)data;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (manager_ops && manager_ops->dpms)
manager_ops->dpms(manager->dev, mode);
/*
* if this condition is ok then it means that the crtc is already
* detached from encoder and last function for detaching is properly
* done, so clear pipe from manager to prevent repeated call.
*/
if (mode > DRM_MODE_DPMS_ON) {
if (!encoder->crtc)
manager->pipe = -1;
}
}
void rockchip_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data)
{
struct rockchip_drm_manager *manager =
to_rockchip_encoder(encoder)->manager;
int pipe = *(int *)data;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* when crtc is detached from encoder, this pipe is used
* to select manager operation
*/
manager->pipe = pipe;
}
void rockchip_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data)
{
struct rockchip_drm_manager *manager =
to_rockchip_encoder(encoder)->manager;
struct rockchip_drm_overlay_ops *overlay_ops = manager->overlay_ops;
struct rockchip_drm_overlay *overlay = data;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (overlay_ops && overlay_ops->mode_set)
overlay_ops->mode_set(manager->dev, overlay);
}
void rockchip_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data)
{
struct rockchip_drm_manager *manager =
to_rockchip_encoder(encoder)->manager;
struct rockchip_drm_overlay_ops *overlay_ops = manager->overlay_ops;
int zpos = DEFAULT_ZPOS;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (data)
zpos = *(int *)data;
if (overlay_ops && overlay_ops->commit)
overlay_ops->commit(manager->dev, zpos);
}
void rockchip_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data)
{
struct rockchip_drm_manager *manager =
to_rockchip_encoder(encoder)->manager;
struct rockchip_drm_overlay_ops *overlay_ops = manager->overlay_ops;
int zpos = DEFAULT_ZPOS;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (data)
zpos = *(int *)data;
if (overlay_ops && overlay_ops->enable)
overlay_ops->enable(manager->dev, zpos);
}
void rockchip_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data)
{
struct rockchip_drm_manager *manager =
to_rockchip_encoder(encoder)->manager;
struct rockchip_drm_overlay_ops *overlay_ops = manager->overlay_ops;
int zpos = DEFAULT_ZPOS;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (data)
zpos = *(int *)data;
if (overlay_ops && overlay_ops->disable)
overlay_ops->disable(manager->dev, zpos);
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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.
*/
#ifndef _ROCKCHIP_DRM_ENCODER_H_
#define _ROCKCHIP_DRM_ENCODER_H_
struct rockchip_drm_manager;
void rockchip_drm_encoder_setup(struct drm_device *dev);
struct drm_encoder *rockchip_drm_encoder_create(struct drm_device *dev,
struct rockchip_drm_manager *mgr,
unsigned int possible_crtcs);
struct rockchip_drm_manager *
rockchip_drm_get_manager(struct drm_encoder *encoder);
void rockchip_drm_fn_encoder(struct drm_crtc *crtc, void *data,
void (*fn)(struct drm_encoder *, void *));
void rockchip_drm_enable_vblank(struct drm_encoder *encoder, void *data);
void rockchip_drm_disable_vblank(struct drm_encoder *encoder, void *data);
void rockchip_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data);
void rockchip_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data);
void rockchip_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data);
void rockchip_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data);
void rockchip_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data);
void rockchip_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data);
void rockchip_drm_encoder_complete_scanout(struct drm_framebuffer *fb);
#endif

View File

@ -0,0 +1,336 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
#include <uapi/drm/rockchip_drm.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_fb.h"
#include "rockchip_drm_gem.h"
#include "rockchip_drm_iommu.h"
#include "rockchip_drm_encoder.h"
#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
/*
* rockchip specific framebuffer structure.
*
* @fb: drm framebuffer obejct.
* @buf_cnt: a buffer count to drm framebuffer.
* @rockchip_gem_obj: array of rockchip specific gem object containing a gem object.
*/
struct rockchip_drm_fb {
struct drm_framebuffer fb;
unsigned int buf_cnt;
struct rockchip_drm_gem_obj *rockchip_gem_obj[MAX_FB_BUFFER];
};
static int check_fb_gem_memory_type(struct drm_device *drm_dev,
struct rockchip_drm_gem_obj *rockchip_gem_obj)
{
unsigned int flags;
/*
* if rockchip drm driver supports iommu then framebuffer can use
* all the buffer types.
*/
if (is_drm_iommu_supported(drm_dev))
return 0;
flags = rockchip_gem_obj->flags;
/*
* without iommu support, not support physically non-continuous memory
* for framebuffer.
*/
if (IS_NONCONTIG_BUFFER(flags)) {
DRM_ERROR("cannot use this gem memory type for fb.\n");
return -EINVAL;
}
return 0;
}
static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
{
struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
unsigned int i;
DRM_DEBUG_KMS("%s\n", __FILE__);
/* make sure that overlay data are updated before relesing fb. */
rockchip_drm_encoder_complete_scanout(fb);
drm_framebuffer_cleanup(fb);
for (i = 0; i < ARRAY_SIZE(rockchip_fb->rockchip_gem_obj); i++) {
struct drm_gem_object *obj;
if (rockchip_fb->rockchip_gem_obj[i] == NULL)
continue;
obj = &rockchip_fb->rockchip_gem_obj[i]->base;
drm_gem_object_unreference_unlocked(obj);
}
kfree(rockchip_fb);
rockchip_fb = NULL;
}
static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
struct drm_file *file_priv,
unsigned int *handle)
{
struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
DRM_DEBUG_KMS("%s\n", __FILE__);
/* This fb should have only one gem object. */
if (WARN_ON(rockchip_fb->buf_cnt != 1))
return -EINVAL;
return drm_gem_handle_create(file_priv,
&rockchip_fb->rockchip_gem_obj[0]->base, handle);
}
static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv, unsigned flags,
unsigned color, struct drm_clip_rect *clips,
unsigned num_clips)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* TODO */
return 0;
}
static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
.destroy = rockchip_drm_fb_destroy,
.create_handle = rockchip_drm_fb_create_handle,
.dirty = rockchip_drm_fb_dirty,
};
void rockchip_drm_fb_set_buf_cnt(struct drm_framebuffer *fb,
unsigned int cnt)
{
struct rockchip_drm_fb *rockchip_fb;
rockchip_fb = to_rockchip_fb(fb);
rockchip_fb->buf_cnt = cnt;
}
unsigned int rockchip_drm_fb_get_buf_cnt(struct drm_framebuffer *fb)
{
struct rockchip_drm_fb *rockchip_fb;
rockchip_fb = to_rockchip_fb(fb);
return rockchip_fb->buf_cnt;
}
struct drm_framebuffer *
rockchip_drm_framebuffer_init(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object *obj)
{
struct rockchip_drm_fb *rockchip_fb;
struct rockchip_drm_gem_obj *rockchip_gem_obj;
int ret;
rockchip_gem_obj = to_rockchip_gem_obj(obj);
ret = check_fb_gem_memory_type(dev, rockchip_gem_obj);
if (ret < 0) {
DRM_ERROR("cannot use this gem memory type for fb.\n");
return ERR_PTR(-EINVAL);
}
rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
if (!rockchip_fb) {
DRM_ERROR("failed to allocate rockchip drm framebuffer\n");
return ERR_PTR(-ENOMEM);
}
drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
rockchip_fb->rockchip_gem_obj[0] = rockchip_gem_obj;
ret = drm_framebuffer_init(dev, &rockchip_fb->fb, &rockchip_drm_fb_funcs);
if (ret) {
DRM_ERROR("failed to initialize framebuffer\n");
return ERR_PTR(ret);
}
return &rockchip_fb->fb;
}
static u32 rockchip_drm_format_num_buffers(struct drm_mode_fb_cmd2 *mode_cmd)
{
unsigned int cnt = 0;
if (mode_cmd->pixel_format != DRM_FORMAT_NV12)
return drm_format_num_planes(mode_cmd->pixel_format);
while (cnt != MAX_FB_BUFFER) {
if (!mode_cmd->handles[cnt])
break;
cnt++;
}
/*
* check if NV12 or NV12M.
*
* NV12
* handles[0] = base1, offsets[0] = 0
* handles[1] = base1, offsets[1] = Y_size
*
* NV12M
* handles[0] = base1, offsets[0] = 0
* handles[1] = base2, offsets[1] = 0
*/
if (cnt == 2) {
/*
* in case of NV12 format, offsets[1] is not 0 and
* handles[0] is same as handles[1].
*/
if (mode_cmd->offsets[1] &&
mode_cmd->handles[0] == mode_cmd->handles[1])
cnt = 1;
}
return cnt;
}
static struct drm_framebuffer *
rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
struct drm_mode_fb_cmd2 *mode_cmd)
{
struct drm_gem_object *obj;
struct rockchip_drm_gem_obj *rockchip_gem_obj;
struct rockchip_drm_fb *rockchip_fb;
int i, ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
if (!rockchip_fb) {
DRM_ERROR("failed to allocate rockchip drm framebuffer\n");
return ERR_PTR(-ENOMEM);
}
obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
if (!obj) {
DRM_ERROR("failed to lookup gem object\n");
ret = -ENOENT;
goto err_free;
}
drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
rockchip_fb->rockchip_gem_obj[0] = to_rockchip_gem_obj(obj);
rockchip_fb->buf_cnt = rockchip_drm_format_num_buffers(mode_cmd);
DRM_DEBUG_KMS("buf_cnt = %d\n", rockchip_fb->buf_cnt);
for (i = 1; i < rockchip_fb->buf_cnt; i++) {
obj = drm_gem_object_lookup(dev, file_priv,
mode_cmd->handles[i]);
if (!obj) {
DRM_ERROR("failed to lookup gem object\n");
ret = -ENOENT;
rockchip_fb->buf_cnt = i;
goto err_unreference;
}
rockchip_gem_obj = to_rockchip_gem_obj(obj);
rockchip_fb->rockchip_gem_obj[i] = rockchip_gem_obj;
ret = check_fb_gem_memory_type(dev, rockchip_gem_obj);
if (ret < 0) {
DRM_ERROR("cannot use this gem memory type for fb.\n");
goto err_unreference;
}
}
ret = drm_framebuffer_init(dev, &rockchip_fb->fb, &rockchip_drm_fb_funcs);
if (ret) {
DRM_ERROR("failed to init framebuffer.\n");
goto err_unreference;
}
return &rockchip_fb->fb;
err_unreference:
for (i = 0; i < rockchip_fb->buf_cnt; i++) {
struct drm_gem_object *obj;
obj = &rockchip_fb->rockchip_gem_obj[i]->base;
if (obj)
drm_gem_object_unreference_unlocked(obj);
}
err_free:
kfree(rockchip_fb);
return ERR_PTR(ret);
}
struct rockchip_drm_gem_buf *rockchip_drm_fb_buffer(struct drm_framebuffer *fb,
int index)
{
struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
struct rockchip_drm_gem_buf *buffer;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (index >= MAX_FB_BUFFER)
return NULL;
buffer = rockchip_fb->rockchip_gem_obj[index]->buffer;
if (!buffer)
return NULL;
DRM_DEBUG_KMS("dma_addr = 0x%lx\n", (unsigned long)buffer->dma_addr);
return buffer;
}
static void rockchip_drm_output_poll_changed(struct drm_device *dev)
{
struct rockchip_drm_private *private = dev->dev_private;
struct drm_fb_helper *fb_helper = private->fb_helper;
if (fb_helper)
drm_fb_helper_hotplug_event(fb_helper);
}
static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
.fb_create = rockchip_user_fb_create,
.output_poll_changed = rockchip_drm_output_poll_changed,
};
void rockchip_drm_mode_config_init(struct drm_device *dev)
{
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
/*
* set max width and height as default value(4096x4096).
* this value would be used to check framebuffer size limitation
* at drm_mode_addfb().
*/
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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.
*/
#ifndef _ROCKCHIP_DRM_FB_H_
#define _ROCKCHIP_DRM_FB_H
struct drm_framebuffer *
rockchip_drm_framebuffer_init(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object *obj);
/* get memory information of a drm framebuffer */
struct rockchip_drm_gem_buf *rockchip_drm_fb_buffer(struct drm_framebuffer *fb,
int index);
void rockchip_drm_mode_config_init(struct drm_device *dev);
/* set a buffer count to drm framebuffer. */
void rockchip_drm_fb_set_buf_cnt(struct drm_framebuffer *fb,
unsigned int cnt);
/* get a buffer count to drm framebuffer. */
unsigned int rockchip_drm_fb_get_buf_cnt(struct drm_framebuffer *fb);
#endif

View File

@ -0,0 +1,355 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_fb.h"
#include "rockchip_drm_gem.h"
#include "rockchip_drm_iommu.h"
#define MAX_CONNECTOR 4
#define PREFERRED_BPP 32
#define to_rockchip_fbdev(x) container_of(x, struct rockchip_drm_fbdev,\
drm_fb_helper)
struct rockchip_drm_fbdev {
struct drm_fb_helper drm_fb_helper;
struct rockchip_drm_gem_obj *rockchip_gem_obj;
};
static int rockchip_drm_fb_mmap(struct fb_info *info,
struct vm_area_struct *vma)
{
struct drm_fb_helper *helper = info->par;
struct rockchip_drm_fbdev *rockchip_fbd = to_rockchip_fbdev(helper);
struct rockchip_drm_gem_obj *rockchip_gem_obj = rockchip_fbd->rockchip_gem_obj;
struct rockchip_drm_gem_buf *buffer = rockchip_gem_obj->buffer;
unsigned long vm_size;
int ret;
DRM_DEBUG_KMS("%s\n", __func__);
vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
vm_size = vma->vm_end - vma->vm_start;
if (vm_size > buffer->size)
return -EINVAL;
ret = dma_mmap_attrs(helper->dev->dev, vma, buffer->pages,
buffer->dma_addr, buffer->size, &buffer->dma_attrs);
if (ret < 0) {
DRM_ERROR("failed to mmap.\n");
return ret;
}
return 0;
}
static struct fb_ops rockchip_drm_fb_ops = {
.owner = THIS_MODULE,
.fb_mmap = rockchip_drm_fb_mmap,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_check_var = drm_fb_helper_check_var,
.fb_set_par = drm_fb_helper_set_par,
.fb_blank = drm_fb_helper_blank,
.fb_pan_display = drm_fb_helper_pan_display,
.fb_setcmap = drm_fb_helper_setcmap,
};
static int rockchip_drm_fbdev_update(struct drm_fb_helper *helper,
struct drm_framebuffer *fb)
{
struct fb_info *fbi = helper->fbdev;
struct drm_device *dev = helper->dev;
struct rockchip_drm_gem_buf *buffer;
unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
unsigned long offset;
DRM_DEBUG_KMS("%s\n", __FILE__);
drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
/* RGB formats use only one buffer */
buffer = rockchip_drm_fb_buffer(fb, 0);
if (!buffer) {
DRM_LOG_KMS("buffer is null.\n");
return -EFAULT;
}
/* map pages with kernel virtual space. */
if (!buffer->kvaddr) {
if (is_drm_iommu_supported(dev)) {
unsigned int nr_pages = buffer->size >> PAGE_SHIFT;
buffer->kvaddr = vmap(buffer->pages, nr_pages, VM_MAP,
pgprot_writecombine(PAGE_KERNEL));
} else {
phys_addr_t dma_addr = buffer->dma_addr;
if (dma_addr)
buffer->kvaddr = phys_to_virt(dma_addr);
else
buffer->kvaddr = (void __iomem *)NULL;
}
if (!buffer->kvaddr) {
DRM_ERROR("failed to map pages to kernel space.\n");
return -EIO;
}
}
/* buffer count to framebuffer always is 1 at booting time. */
rockchip_drm_fb_set_buf_cnt(fb, 1);
offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
offset += fbi->var.yoffset * fb->pitches[0];
dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
fbi->screen_base = buffer->kvaddr + offset;
if (is_drm_iommu_supported(dev))
fbi->fix.smem_start = (unsigned long)
(page_to_phys(sg_page(buffer->sgt->sgl)) + offset);
else
fbi->fix.smem_start = (unsigned long)buffer->dma_addr;
fbi->screen_size = size;
fbi->fix.smem_len = size;
return 0;
}
static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
struct rockchip_drm_fbdev *rockchip_fbdev = to_rockchip_fbdev(helper);
struct rockchip_drm_gem_obj *rockchip_gem_obj;
struct drm_device *dev = helper->dev;
struct fb_info *fbi;
struct drm_mode_fb_cmd2 mode_cmd = { 0 };
struct platform_device *pdev = dev->platformdev;
unsigned long size;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
sizes->surface_width, sizes->surface_height,
sizes->surface_bpp);
mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height;
mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
sizes->surface_depth);
mutex_lock(&dev->struct_mutex);
fbi = framebuffer_alloc(0, &pdev->dev);
if (!fbi) {
DRM_ERROR("failed to allocate fb info.\n");
ret = -ENOMEM;
goto out;
}
size = mode_cmd.pitches[0] * mode_cmd.height;
/* 0 means to allocate physically continuous memory */
rockchip_gem_obj = rockchip_drm_gem_create(dev, 0, size);
if (IS_ERR(rockchip_gem_obj)) {
ret = PTR_ERR(rockchip_gem_obj);
goto err_release_framebuffer;
}
rockchip_fbdev->rockchip_gem_obj = rockchip_gem_obj;
helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd,
&rockchip_gem_obj->base);
if (IS_ERR(helper->fb)) {
DRM_ERROR("failed to create drm framebuffer.\n");
ret = PTR_ERR(helper->fb);
goto err_destroy_gem;
}
helper->fbdev = fbi;
fbi->par = helper;
fbi->flags = FBINFO_FLAG_DEFAULT;
fbi->fbops = &rockchip_drm_fb_ops;
ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
if (ret) {
DRM_ERROR("failed to allocate cmap.\n");
goto err_destroy_framebuffer;
}
ret = rockchip_drm_fbdev_update(helper, helper->fb);
if (ret < 0)
goto err_dealloc_cmap;
mutex_unlock(&dev->struct_mutex);
return ret;
err_dealloc_cmap:
fb_dealloc_cmap(&fbi->cmap);
err_destroy_framebuffer:
drm_framebuffer_cleanup(helper->fb);
err_destroy_gem:
rockchip_drm_gem_destroy(rockchip_gem_obj);
err_release_framebuffer:
framebuffer_release(fbi);
/*
* if failed, all resources allocated above would be released by
* drm_mode_config_cleanup() when drm_load() had been called prior
* to any specific driver such as fimd or hdmi driver.
*/
out:
mutex_unlock(&dev->struct_mutex);
return ret;
}
static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
.fb_probe = rockchip_drm_fbdev_create,
};
int rockchip_drm_fbdev_init(struct drm_device *dev)
{
struct rockchip_drm_fbdev *fbdev;
struct rockchip_drm_private *private = dev->dev_private;
struct drm_fb_helper *helper;
unsigned int num_crtc;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
return 0;
fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
if (!fbdev) {
DRM_ERROR("failed to allocate drm fbdev.\n");
return -ENOMEM;
}
private->fb_helper = helper = &fbdev->drm_fb_helper;
helper->funcs = &rockchip_drm_fb_helper_funcs;
num_crtc = dev->mode_config.num_crtc;
ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR);
if (ret < 0) {
DRM_ERROR("failed to initialize drm fb helper.\n");
goto err_init;
}
ret = drm_fb_helper_single_add_all_connectors(helper);
if (ret < 0) {
DRM_ERROR("failed to register drm_fb_helper_connector.\n");
goto err_setup;
}
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(dev);
ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
if (ret < 0) {
DRM_ERROR("failed to set up hw configuration.\n");
goto err_setup;
}
return 0;
err_setup:
drm_fb_helper_fini(helper);
err_init:
private->fb_helper = NULL;
kfree(fbdev);
return ret;
}
static void rockchip_drm_fbdev_destroy(struct drm_device *dev,
struct drm_fb_helper *fb_helper)
{
struct rockchip_drm_fbdev *rockchip_fbd = to_rockchip_fbdev(fb_helper);
struct rockchip_drm_gem_obj *rockchip_gem_obj = rockchip_fbd->rockchip_gem_obj;
struct drm_framebuffer *fb;
if (is_drm_iommu_supported(dev) && rockchip_gem_obj->buffer->kvaddr)
vunmap(rockchip_gem_obj->buffer->kvaddr);
/* release drm framebuffer and real buffer */
if (fb_helper->fb && fb_helper->fb->funcs) {
fb = fb_helper->fb;
if (fb) {
drm_framebuffer_unregister_private(fb);
drm_framebuffer_remove(fb);
}
}
/* release linux framebuffer */
if (fb_helper->fbdev) {
struct fb_info *info;
int ret;
info = fb_helper->fbdev;
ret = unregister_framebuffer(info);
if (ret < 0)
DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
if (info->cmap.len)
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
}
drm_fb_helper_fini(fb_helper);
}
void rockchip_drm_fbdev_fini(struct drm_device *dev)
{
struct rockchip_drm_private *private = dev->dev_private;
struct rockchip_drm_fbdev *fbdev;
if (!private || !private->fb_helper)
return;
fbdev = to_rockchip_fbdev(private->fb_helper);
if (fbdev->rockchip_gem_obj)
rockchip_drm_gem_destroy(fbdev->rockchip_gem_obj);
rockchip_drm_fbdev_destroy(dev, private->fb_helper);
kfree(fbdev);
private->fb_helper = NULL;
}
void rockchip_drm_fbdev_restore_mode(struct drm_device *dev)
{
struct rockchip_drm_private *private = dev->dev_private;
if (!private || !private->fb_helper)
return;
drm_modeset_lock_all(dev);
drm_fb_helper_restore_fbdev_mode(private->fb_helper);
drm_modeset_unlock_all(dev);
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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.
*/
#ifndef _ROCKCHIP_DRM_FBDEV_H_
#define _ROCKCHIP_DRM_FBDEV_H_
int rockchip_drm_fbdev_init(struct drm_device *dev);
int rockchip_drm_fbdev_reinit(struct drm_device *dev);
void rockchip_drm_fbdev_fini(struct drm_device *dev);
void rockchip_drm_fbdev_restore_mode(struct drm_device *dev);
#endif

View File

@ -0,0 +1,817 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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/drmP.h>
#include <linux/shmem_fs.h>
#include <drm/rockchip_drm.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_gem.h"
#include "rockchip_drm_buf.h"
static unsigned int convert_to_vm_err_msg(int msg)
{
unsigned int out_msg;
switch (msg) {
case 0:
case -ERESTARTSYS:
case -EINTR:
out_msg = VM_FAULT_NOPAGE;
break;
case -ENOMEM:
out_msg = VM_FAULT_OOM;
break;
default:
out_msg = VM_FAULT_SIGBUS;
break;
}
return out_msg;
}
static int check_gem_flags(unsigned int flags)
{
if (flags & ~(ROCKCHIP_BO_MASK)) {
DRM_ERROR("invalid flags.\n");
return -EINVAL;
}
return 0;
}
static void update_vm_cache_attr(struct rockchip_drm_gem_obj *obj,
struct vm_area_struct *vma)
{
DRM_DEBUG_KMS("flags = 0x%x\n", obj->flags);
/* non-cachable as default. */
if (obj->flags & ROCKCHIP_BO_CACHABLE)
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
else if (obj->flags & ROCKCHIP_BO_WC)
vma->vm_page_prot =
pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
else
vma->vm_page_prot =
pgprot_noncached(vm_get_page_prot(vma->vm_flags));
}
static unsigned long roundup_gem_size(unsigned long size, unsigned int flags)
{
/* TODO */
return roundup(size, PAGE_SIZE);
}
static int rockchip_drm_gem_map_buf(struct drm_gem_object *obj,
struct vm_area_struct *vma,
unsigned long f_vaddr,
pgoff_t page_offset)
{
struct rockchip_drm_gem_obj *rockchip_gem_obj = to_rockchip_gem_obj(obj);
struct rockchip_drm_gem_buf *buf = rockchip_gem_obj->buffer;
struct scatterlist *sgl;
unsigned long pfn;
int i;
if (!buf->sgt)
return -EINTR;
if (page_offset >= (buf->size >> PAGE_SHIFT)) {
DRM_ERROR("invalid page offset\n");
return -EINVAL;
}
sgl = buf->sgt->sgl;
for_each_sg(buf->sgt->sgl, sgl, buf->sgt->nents, i) {
if (page_offset < (sgl->length >> PAGE_SHIFT))
break;
page_offset -= (sgl->length >> PAGE_SHIFT);
}
pfn = __phys_to_pfn(sg_phys(sgl)) + page_offset;
return vm_insert_mixed(vma, f_vaddr, pfn);
}
static int rockchip_drm_gem_handle_create(struct drm_gem_object *obj,
struct drm_file *file_priv,
unsigned int *handle)
{
int ret;
/*
* allocate a id of idr table where the obj is registered
* and handle has the id what user can see.
*/
ret = drm_gem_handle_create(file_priv, obj, handle);
if (ret)
return ret;
DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle);
/* drop reference from allocate - handle holds it now. */
drm_gem_object_unreference_unlocked(obj);
return 0;
}
void rockchip_drm_gem_destroy(struct rockchip_drm_gem_obj *rockchip_gem_obj)
{
struct drm_gem_object *obj;
struct rockchip_drm_gem_buf *buf;
DRM_DEBUG_KMS("%s\n", __FILE__);
obj = &rockchip_gem_obj->base;
buf = rockchip_gem_obj->buffer;
DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count));
/*
* do not release memory region from exporter.
*
* the region will be released by exporter
* once dmabuf's refcount becomes 0.
*/
if (obj->import_attach)
goto out;
rockchip_drm_free_buf(obj->dev, rockchip_gem_obj->flags, buf);
out:
rockchip_drm_fini_buf(obj->dev, buf);
rockchip_gem_obj->buffer = NULL;
if (obj->map_list.map)
drm_gem_free_mmap_offset(obj);
/* release file pointer to gem object. */
drm_gem_object_release(obj);
kfree(rockchip_gem_obj);
rockchip_gem_obj = NULL;
}
unsigned long rockchip_drm_gem_get_size(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *file_priv)
{
struct rockchip_drm_gem_obj *rockchip_gem_obj;
struct drm_gem_object *obj;
obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
return 0;
}
rockchip_gem_obj = to_rockchip_gem_obj(obj);
drm_gem_object_unreference_unlocked(obj);
return rockchip_gem_obj->buffer->size;
}
struct rockchip_drm_gem_obj *rockchip_drm_gem_init(struct drm_device *dev,
unsigned long size)
{
struct rockchip_drm_gem_obj *rockchip_gem_obj;
struct drm_gem_object *obj;
int ret;
rockchip_gem_obj = kzalloc(sizeof(*rockchip_gem_obj), GFP_KERNEL);
if (!rockchip_gem_obj) {
DRM_ERROR("failed to allocate rockchip gem object\n");
return NULL;
}
rockchip_gem_obj->size = size;
obj = &rockchip_gem_obj->base;
ret = drm_gem_object_init(dev, obj, size);
if (ret < 0) {
DRM_ERROR("failed to initialize gem object\n");
kfree(rockchip_gem_obj);
return NULL;
}
DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
return rockchip_gem_obj;
}
struct rockchip_drm_gem_obj *rockchip_drm_gem_create(struct drm_device *dev,
unsigned int flags,
unsigned long size)
{
struct rockchip_drm_gem_obj *rockchip_gem_obj;
struct rockchip_drm_gem_buf *buf;
int ret;
if (!size) {
DRM_ERROR("invalid size.\n");
return ERR_PTR(-EINVAL);
}
size = roundup_gem_size(size, flags);
DRM_DEBUG_KMS("%s\n", __FILE__);
ret = check_gem_flags(flags);
if (ret)
return ERR_PTR(ret);
buf = rockchip_drm_init_buf(dev, size);
if (!buf)
return ERR_PTR(-ENOMEM);
rockchip_gem_obj = rockchip_drm_gem_init(dev, size);
if (!rockchip_gem_obj) {
ret = -ENOMEM;
goto err_fini_buf;
}
rockchip_gem_obj->buffer = buf;
/* set memory type and cache attribute from user side. */
rockchip_gem_obj->flags = flags;
ret = rockchip_drm_alloc_buf(dev, buf, flags);
if (ret < 0) {
drm_gem_object_release(&rockchip_gem_obj->base);
goto err_fini_buf;
}
return rockchip_gem_obj;
err_fini_buf:
rockchip_drm_fini_buf(dev, buf);
return ERR_PTR(ret);
}
int rockchip_drm_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_rockchip_gem_create *args = data;
struct rockchip_drm_gem_obj *rockchip_gem_obj;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
rockchip_gem_obj = rockchip_drm_gem_create(dev, args->flags, args->size);
if (IS_ERR(rockchip_gem_obj))
return PTR_ERR(rockchip_gem_obj);
ret = rockchip_drm_gem_handle_create(&rockchip_gem_obj->base, file_priv,
&args->handle);
if (ret) {
rockchip_drm_gem_destroy(rockchip_gem_obj);
return ret;
}
return 0;
}
dma_addr_t *rockchip_drm_gem_get_dma_addr(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *filp)
{
struct rockchip_drm_gem_obj *rockchip_gem_obj;
struct drm_gem_object *obj;
obj = drm_gem_object_lookup(dev, filp, gem_handle);
if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
return ERR_PTR(-EINVAL);
}
rockchip_gem_obj = to_rockchip_gem_obj(obj);
return &rockchip_gem_obj->buffer->dma_addr;
}
void rockchip_drm_gem_put_dma_addr(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *filp)
{
struct rockchip_drm_gem_obj *rockchip_gem_obj;
struct drm_gem_object *obj;
obj = drm_gem_object_lookup(dev, filp, gem_handle);
if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
return;
}
rockchip_gem_obj = to_rockchip_gem_obj(obj);
drm_gem_object_unreference_unlocked(obj);
/*
* decrease obj->refcount one more time because we has already
* increased it at rockchip_drm_gem_get_dma_addr().
*/
drm_gem_object_unreference_unlocked(obj);
}
int rockchip_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_rockchip_gem_map_off *args = data;
DRM_DEBUG_KMS("%s\n", __FILE__);
DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n",
args->handle, (unsigned long)args->offset);
if (!(dev->driver->driver_features & DRIVER_GEM)) {
DRM_ERROR("does not support GEM.\n");
return -ENODEV;
}
return rockchip_drm_gem_dumb_map_offset(file_priv, dev, args->handle,
&args->offset);
}
static struct drm_file *rockchip_drm_find_drm_file(struct drm_device *drm_dev,
struct file *filp)
{
struct drm_file *file_priv;
/* find current process's drm_file from filelist. */
list_for_each_entry(file_priv, &drm_dev->filelist, lhead)
if (file_priv->filp == filp)
return file_priv;
WARN_ON(1);
return ERR_PTR(-EFAULT);
}
static int rockchip_drm_gem_mmap_buffer(struct file *filp,
struct vm_area_struct *vma)
{
struct drm_gem_object *obj = filp->private_data;
struct rockchip_drm_gem_obj *rockchip_gem_obj = to_rockchip_gem_obj(obj);
struct drm_device *drm_dev = obj->dev;
struct rockchip_drm_gem_buf *buffer;
struct drm_file *file_priv;
unsigned long vm_size;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
vma->vm_private_data = obj;
vma->vm_ops = drm_dev->driver->gem_vm_ops;
/* restore it to driver's fops. */
filp->f_op = fops_get(drm_dev->driver->fops);
file_priv = rockchip_drm_find_drm_file(drm_dev, filp);
if (IS_ERR(file_priv))
return PTR_ERR(file_priv);
/* restore it to drm_file. */
filp->private_data = file_priv;
update_vm_cache_attr(rockchip_gem_obj, vma);
vm_size = vma->vm_end - vma->vm_start;
/*
* a buffer contains information to physically continuous memory
* allocated by user request or at framebuffer creation.
*/
buffer = rockchip_gem_obj->buffer;
/* check if user-requested size is valid. */
if (vm_size > buffer->size)
return -EINVAL;
ret = dma_mmap_attrs(drm_dev->dev, vma, buffer->pages,
buffer->dma_addr, buffer->size,
&buffer->dma_attrs);
if (ret < 0) {
DRM_ERROR("failed to mmap.\n");
return ret;
}
/*
* take a reference to this mapping of the object. And this reference
* is unreferenced by the corresponding vm_close call.
*/
drm_gem_object_reference(obj);
drm_vm_open_locked(drm_dev, vma);
return 0;
}
static const struct file_operations rockchip_drm_gem_fops = {
.mmap = rockchip_drm_gem_mmap_buffer,
};
int rockchip_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_rockchip_gem_mmap *args = data;
struct drm_gem_object *obj;
unsigned int addr;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!(dev->driver->driver_features & DRIVER_GEM)) {
DRM_ERROR("does not support GEM.\n");
return -ENODEV;
}
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
return -EINVAL;
}
/*
* We have to use gem object and its fops for specific mmaper,
* but vm_mmap() can deliver only filp. So we have to change
* filp->f_op and filp->private_data temporarily, then restore
* again. So it is important to keep lock until restoration the
* settings to prevent others from misuse of filp->f_op or
* filp->private_data.
*/
mutex_lock(&dev->struct_mutex);
/*
* Set specific mmper's fops. And it will be restored by
* rockchip_drm_gem_mmap_buffer to dev->driver->fops.
* This is used to call specific mapper temporarily.
*/
file_priv->filp->f_op = &rockchip_drm_gem_fops;
/*
* Set gem object to private_data so that specific mmaper
* can get the gem object. And it will be restored by
* rockchip_drm_gem_mmap_buffer to drm_file.
*/
file_priv->filp->private_data = obj;
addr = vm_mmap(file_priv->filp, 0, args->size,
PROT_READ | PROT_WRITE, MAP_SHARED, 0);
drm_gem_object_unreference(obj);
if (IS_ERR((void *)addr)) {
/* check filp->f_op, filp->private_data are restored */
if (file_priv->filp->f_op == &rockchip_drm_gem_fops) {
file_priv->filp->f_op = fops_get(dev->driver->fops);
file_priv->filp->private_data = file_priv;
}
mutex_unlock(&dev->struct_mutex);
return PTR_ERR((void *)addr);
}
mutex_unlock(&dev->struct_mutex);
args->mapped = addr;
DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
return 0;
}
int rockchip_drm_gem_get_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{ struct rockchip_drm_gem_obj *rockchip_gem_obj;
struct drm_rockchip_gem_info *args = data;
struct drm_gem_object *obj;
mutex_lock(&dev->struct_mutex);
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
mutex_unlock(&dev->struct_mutex);
return -EINVAL;
}
rockchip_gem_obj = to_rockchip_gem_obj(obj);
args->flags = rockchip_gem_obj->flags;
args->size = rockchip_gem_obj->size;
drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
return 0;
}
struct vm_area_struct *rockchip_gem_get_vma(struct vm_area_struct *vma)
{
struct vm_area_struct *vma_copy;
vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
if (!vma_copy)
return NULL;
if (vma->vm_ops && vma->vm_ops->open)
vma->vm_ops->open(vma);
if (vma->vm_file)
get_file(vma->vm_file);
memcpy(vma_copy, vma, sizeof(*vma));
vma_copy->vm_mm = NULL;
vma_copy->vm_next = NULL;
vma_copy->vm_prev = NULL;
return vma_copy;
}
void rockchip_gem_put_vma(struct vm_area_struct *vma)
{
if (!vma)
return;
if (vma->vm_ops && vma->vm_ops->close)
vma->vm_ops->close(vma);
if (vma->vm_file)
fput(vma->vm_file);
kfree(vma);
}
int rockchip_gem_get_pages_from_userptr(unsigned long start,
unsigned int npages,
struct page **pages,
struct vm_area_struct *vma)
{
int get_npages;
/* the memory region mmaped with VM_PFNMAP. */
if (vma_is_io(vma)) {
unsigned int i;
for (i = 0; i < npages; ++i, start += PAGE_SIZE) {
unsigned long pfn;
int ret = follow_pfn(vma, start, &pfn);
if (ret)
return ret;
pages[i] = pfn_to_page(pfn);
}
if (i != npages) {
DRM_ERROR("failed to get user_pages.\n");
return -EINVAL;
}
return 0;
}
get_npages = get_user_pages(current, current->mm, start,
npages, 1, 1, pages, NULL);
get_npages = max(get_npages, 0);
if (get_npages != npages) {
DRM_ERROR("failed to get user_pages.\n");
while (get_npages)
put_page(pages[--get_npages]);
return -EFAULT;
}
return 0;
}
void rockchip_gem_put_pages_to_userptr(struct page **pages,
unsigned int npages,
struct vm_area_struct *vma)
{
if (!vma_is_io(vma)) {
unsigned int i;
for (i = 0; i < npages; i++) {
set_page_dirty_lock(pages[i]);
/*
* undo the reference we took when populating
* the table.
*/
put_page(pages[i]);
}
}
}
int rockchip_gem_map_sgt_with_dma(struct drm_device *drm_dev,
struct sg_table *sgt,
enum dma_data_direction dir)
{
int nents;
mutex_lock(&drm_dev->struct_mutex);
nents = dma_map_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir);
if (!nents) {
DRM_ERROR("failed to map sgl with dma.\n");
mutex_unlock(&drm_dev->struct_mutex);
return nents;
}
mutex_unlock(&drm_dev->struct_mutex);
return 0;
}
void rockchip_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,
struct sg_table *sgt,
enum dma_data_direction dir)
{
dma_unmap_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir);
}
int rockchip_drm_gem_init_object(struct drm_gem_object *obj)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return 0;
}
void rockchip_drm_gem_free_object(struct drm_gem_object *obj)
{
struct rockchip_drm_gem_obj *rockchip_gem_obj;
struct rockchip_drm_gem_buf *buf;
DRM_DEBUG_KMS("%s\n", __FILE__);
rockchip_gem_obj = to_rockchip_gem_obj(obj);
buf = rockchip_gem_obj->buffer;
if (obj->import_attach)
drm_prime_gem_destroy(obj, buf->sgt);
rockchip_drm_gem_destroy(to_rockchip_gem_obj(obj));
}
int rockchip_drm_gem_dumb_create(struct drm_file *file_priv,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
struct rockchip_drm_gem_obj *rockchip_gem_obj;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* alocate memory to be used for framebuffer.
* - this callback would be called by user application
* with DRM_IOCTL_MODE_CREATE_DUMB command.
*/
args->pitch = args->width * ((args->bpp + 7) / 8);
args->size = args->pitch * args->height;
rockchip_gem_obj = rockchip_drm_gem_create(dev, ROCKCHIP_BO_CONTIG |
ROCKCHIP_BO_WC, args->size);
if (IS_ERR(rockchip_gem_obj))
return PTR_ERR(rockchip_gem_obj);
ret = rockchip_drm_gem_handle_create(&rockchip_gem_obj->base, file_priv,
&args->handle);
if (ret) {
rockchip_drm_gem_destroy(rockchip_gem_obj);
return ret;
}
return 0;
}
int rockchip_drm_gem_dumb_map_offset(struct drm_file *file_priv,
struct drm_device *dev, uint32_t handle,
uint64_t *offset)
{
struct drm_gem_object *obj;
int ret = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
mutex_lock(&dev->struct_mutex);
/*
* get offset of memory allocated for drm framebuffer.
* - this callback would be called by user application
* with DRM_IOCTL_MODE_MAP_DUMB command.
*/
obj = drm_gem_object_lookup(dev, file_priv, handle);
if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
ret = -EINVAL;
goto unlock;
}
if (!obj->map_list.map) {
ret = drm_gem_create_mmap_offset(obj);
if (ret)
goto out;
}
*offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
out:
drm_gem_object_unreference(obj);
unlock:
mutex_unlock(&dev->struct_mutex);
return ret;
}
int rockchip_drm_gem_dumb_destroy(struct drm_file *file_priv,
struct drm_device *dev,
unsigned int handle)
{
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* obj->refcount and obj->handle_count are decreased and
* if both them are 0 then rockchip_drm_gem_free_object()
* would be called by callback to release resources.
*/
ret = drm_gem_handle_delete(file_priv, handle);
if (ret < 0) {
DRM_ERROR("failed to delete drm_gem_handle.\n");
return ret;
}
return 0;
}
int rockchip_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct drm_gem_object *obj = vma->vm_private_data;
struct drm_device *dev = obj->dev;
unsigned long f_vaddr;
pgoff_t page_offset;
int ret;
page_offset = ((unsigned long)vmf->virtual_address -
vma->vm_start) >> PAGE_SHIFT;
f_vaddr = (unsigned long)vmf->virtual_address;
mutex_lock(&dev->struct_mutex);
ret = rockchip_drm_gem_map_buf(obj, vma, f_vaddr, page_offset);
if (ret < 0)
DRM_ERROR("failed to map a buffer with user.\n");
mutex_unlock(&dev->struct_mutex);
return convert_to_vm_err_msg(ret);
}
int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct rockchip_drm_gem_obj *rockchip_gem_obj;
struct drm_gem_object *obj;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
/* set vm_area_struct. */
ret = drm_gem_mmap(filp, vma);
if (ret < 0) {
DRM_ERROR("failed to mmap.\n");
return ret;
}
obj = vma->vm_private_data;
rockchip_gem_obj = to_rockchip_gem_obj(obj);
ret = check_gem_flags(rockchip_gem_obj->flags);
if (ret) {
drm_gem_vm_close(vma);
drm_gem_free_mmap_offset(obj);
return ret;
}
vma->vm_flags &= ~VM_PFNMAP;
vma->vm_flags |= VM_MIXEDMAP;
update_vm_cache_attr(rockchip_gem_obj, vma);
return ret;
}

View File

@ -0,0 +1,202 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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.
*/
#ifndef _ROCKCHIP_DRM_GEM_H_
#define _ROCKCHIP_DRM_GEM_H_
#define to_rockchip_gem_obj(x) container_of(x,\
struct rockchip_drm_gem_obj, base)
#define IS_NONCONTIG_BUFFER(f) (f & ROCKCHIP_BO_NONCONTIG)
/*
* rockchip drm gem buffer structure.
*
* @kvaddr: kernel virtual address to allocated memory region.
* *userptr: user space address.
* @dma_addr: bus address(accessed by dma) to allocated memory region.
* - this address could be physical address without IOMMU and
* device address with IOMMU.
* @write: whether pages will be written to by the caller.
* @pages: Array of backing pages.
* @sgt: sg table to transfer page data.
* @size: size of allocated memory region.
* @pfnmap: indicate whether memory region from userptr is mmaped with
* VM_PFNMAP or not.
*/
struct rockchip_drm_gem_buf {
void __iomem *kvaddr;
unsigned long userptr;
dma_addr_t dma_addr;
struct dma_attrs dma_attrs;
unsigned int write;
struct page **pages;
struct sg_table *sgt;
unsigned long size;
bool pfnmap;
};
/*
* rockchip drm buffer structure.
*
* @base: a gem object.
* - a new handle to this gem object would be created
* by drm_gem_handle_create().
* @buffer: a pointer to rockchip_drm_gem_buffer object.
* - contain the information to memory region allocated
* by user request or at framebuffer creation.
* continuous memory region allocated by user request
* or at framebuffer creation.
* @size: size requested from user, in bytes and this size is aligned
* in page unit.
* @vma: a pointer to vm_area.
* @flags: indicate memory type to allocated buffer and cache attruibute.
*
* P.S. this object would be transfered to user as kms_bo.handle so
* user can access the buffer through kms_bo.handle.
*/
struct rockchip_drm_gem_obj {
struct drm_gem_object base;
struct rockchip_drm_gem_buf *buffer;
unsigned long size;
struct vm_area_struct *vma;
unsigned int flags;
};
struct page **rockchip_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask);
/* destroy a buffer with gem object */
void rockchip_drm_gem_destroy(struct rockchip_drm_gem_obj *rockchip_gem_obj);
/* create a private gem object and initialize it. */
struct rockchip_drm_gem_obj *rockchip_drm_gem_init(struct drm_device *dev,
unsigned long size);
/* create a new buffer with gem object */
struct rockchip_drm_gem_obj *rockchip_drm_gem_create(struct drm_device *dev,
unsigned int flags,
unsigned long size);
/*
* request gem object creation and buffer allocation as the size
* that it is calculated with framebuffer information such as width,
* height and bpp.
*/
int rockchip_drm_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/*
* get dma address from gem handle and this function could be used for
* other drivers such as 2d/3d acceleration drivers.
* with this function call, gem object reference count would be increased.
*/
dma_addr_t *rockchip_drm_gem_get_dma_addr(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *filp);
/*
* put dma address from gem handle and this function could be used for
* other drivers such as 2d/3d acceleration drivers.
* with this function call, gem object reference count would be decreased.
*/
void rockchip_drm_gem_put_dma_addr(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *filp);
/* get buffer offset to map to user space. */
int rockchip_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/*
* mmap the physically continuous memory that a gem object contains
* to user space.
*/
int rockchip_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/* map user space allocated by malloc to pages. */
int rockchip_drm_gem_userptr_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/* get buffer information to memory region allocated by gem. */
int rockchip_drm_gem_get_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/* get buffer size to gem handle. */
unsigned long rockchip_drm_gem_get_size(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *file_priv);
/* initialize gem object. */
int rockchip_drm_gem_init_object(struct drm_gem_object *obj);
/* free gem object. */
void rockchip_drm_gem_free_object(struct drm_gem_object *gem_obj);
/* create memory region for drm framebuffer. */
int rockchip_drm_gem_dumb_create(struct drm_file *file_priv,
struct drm_device *dev,
struct drm_mode_create_dumb *args);
/* map memory region for drm framebuffer to user space. */
int rockchip_drm_gem_dumb_map_offset(struct drm_file *file_priv,
struct drm_device *dev, uint32_t handle,
uint64_t *offset);
/*
* destroy memory region allocated.
* - a gem handle and physical memory region pointed by a gem object
* would be released by drm_gem_handle_delete().
*/
int rockchip_drm_gem_dumb_destroy(struct drm_file *file_priv,
struct drm_device *dev,
unsigned int handle);
/* page fault handler and mmap fault address(virtual) to physical memory. */
int rockchip_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
/* set vm_flags and we can change the vm attribute to other one at here. */
int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
static inline int vma_is_io(struct vm_area_struct *vma)
{
return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
}
/* get a copy of a virtual memory region. */
struct vm_area_struct *rockchip_gem_get_vma(struct vm_area_struct *vma);
/* release a userspace virtual memory area. */
void rockchip_gem_put_vma(struct vm_area_struct *vma);
/* get pages from user space. */
int rockchip_gem_get_pages_from_userptr(unsigned long start,
unsigned int npages,
struct page **pages,
struct vm_area_struct *vma);
/* drop the reference to pages. */
void rockchip_gem_put_pages_to_userptr(struct page **pages,
unsigned int npages,
struct vm_area_struct *vma);
/* map sgt with dma region. */
int rockchip_gem_map_sgt_with_dma(struct drm_device *drm_dev,
struct sg_table *sgt,
enum dma_data_direction dir);
/* unmap sgt from dma region. */
void rockchip_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,
struct sg_table *sgt,
enum dma_data_direction dir);
#endif

View File

@ -0,0 +1,483 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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/drmP.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <drm/rockchip_drm.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_hdmi.h"
#define to_context(dev) platform_get_drvdata(to_platform_device(dev))
#define to_subdrv(dev) to_context(dev)
#define get_ctx_from_subdrv(subdrv) container_of(subdrv,\
struct drm_hdmi_context, subdrv);
/* platform device pointer for common drm hdmi device. */
static struct platform_device *rockchip_drm_hdmi_pdev;
/* Common hdmi subdrv needs to access the hdmi and mixer though context.
* These should be initialied by the repective drivers */
static struct rockchip_drm_hdmi_context *hdmi_ctx;
static struct rockchip_drm_hdmi_context *mixer_ctx;
/* these callback points shoud be set by specific drivers. */
static struct rockchip_hdmi_ops *hdmi_ops;
static struct rockchip_mixer_ops *mixer_ops;
struct drm_hdmi_context {
struct rockchip_drm_subdrv subdrv;
struct rockchip_drm_hdmi_context *hdmi_ctx;
struct rockchip_drm_hdmi_context *mixer_ctx;
bool enabled[MIXER_WIN_NR];
};
int rockchip_platform_device_hdmi_register(void)
{
struct platform_device *pdev;
if (rockchip_drm_hdmi_pdev)
return -EEXIST;
pdev = platform_device_register_simple(
"rockchip-drm-hdmi", -1, NULL, 0);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
rockchip_drm_hdmi_pdev = pdev;
return 0;
}
void rockchip_platform_device_hdmi_unregister(void)
{
if (rockchip_drm_hdmi_pdev) {
platform_device_unregister(rockchip_drm_hdmi_pdev);
rockchip_drm_hdmi_pdev = NULL;
}
}
void rockchip_hdmi_drv_attach(struct rockchip_drm_hdmi_context *ctx)
{
if (ctx)
hdmi_ctx = ctx;
}
void rockchip_mixer_drv_attach(struct rockchip_drm_hdmi_context *ctx)
{
if (ctx)
mixer_ctx = ctx;
}
void rockchip_hdmi_ops_register(struct rockchip_hdmi_ops *ops)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
if (ops)
hdmi_ops = ops;
}
void rockchip_mixer_ops_register(struct rockchip_mixer_ops *ops)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
if (ops)
mixer_ops = ops;
}
static bool drm_hdmi_is_connected(struct device *dev)
{
struct drm_hdmi_context *ctx = to_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->is_connected)
return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx);
return false;
}
static struct edid *drm_hdmi_get_edid(struct device *dev,
struct drm_connector *connector)
{
struct drm_hdmi_context *ctx = to_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->get_edid)
return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector);
return NULL;
}
static int drm_hdmi_check_timing(struct device *dev, void *timing)
{
struct drm_hdmi_context *ctx = to_context(dev);
int ret = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* Both, mixer and hdmi should be able to handle the requested mode.
* If any of the two fails, return mode as BAD.
*/
if (mixer_ops && mixer_ops->check_timing)
ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, timing);
if (ret)
return ret;
if (hdmi_ops && hdmi_ops->check_timing)
return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
return 0;
}
static int drm_hdmi_power_on(struct device *dev, int mode)
{
struct drm_hdmi_context *ctx = to_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->power_on)
return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode);
return 0;
}
static struct rockchip_drm_display_ops drm_hdmi_display_ops = {
.type = ROCKCHIP_DISPLAY_TYPE_HDMI,
.is_connected = drm_hdmi_is_connected,
.get_edid = drm_hdmi_get_edid,
.check_timing = drm_hdmi_check_timing,
.power_on = drm_hdmi_power_on,
};
static int drm_hdmi_enable_vblank(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
struct rockchip_drm_subdrv *subdrv = &ctx->subdrv;
struct rockchip_drm_manager *manager = subdrv->manager;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (mixer_ops && mixer_ops->enable_vblank)
return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx,
manager->pipe);
return 0;
}
static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (mixer_ops && mixer_ops->disable_vblank)
return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx);
}
static void drm_hdmi_wait_for_vblank(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (mixer_ops && mixer_ops->wait_for_vblank)
mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx);
}
static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
struct drm_connector *connector,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_display_mode *m;
int mode_ok;
DRM_DEBUG_KMS("%s\n", __FILE__);
drm_mode_set_crtcinfo(adjusted_mode, 0);
mode_ok = drm_hdmi_check_timing(subdrv_dev, adjusted_mode);
/* just return if user desired mode exists. */
if (mode_ok == 0)
return;
/*
* otherwise, find the most suitable mode among modes and change it
* to adjusted_mode.
*/
list_for_each_entry(m, &connector->modes, head) {
mode_ok = drm_hdmi_check_timing(subdrv_dev, m);
if (mode_ok == 0) {
struct drm_mode_object base;
struct list_head head;
DRM_INFO("desired mode doesn't exist so\n");
DRM_INFO("use the most suitable mode among modes.\n");
DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
m->hdisplay, m->vdisplay, m->vrefresh);
/* preserve display mode header while copying. */
head = adjusted_mode->head;
base = adjusted_mode->base;
memcpy(adjusted_mode, m, sizeof(*m));
adjusted_mode->head = head;
adjusted_mode->base = base;
break;
}
}
}
static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->mode_set)
hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
}
static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
unsigned int *width, unsigned int *height)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->get_max_resol)
hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height);
}
static void drm_hdmi_commit(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->commit)
hdmi_ops->commit(ctx->hdmi_ctx->ctx);
}
static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (mixer_ops && mixer_ops->dpms)
mixer_ops->dpms(ctx->mixer_ctx->ctx, mode);
if (hdmi_ops && hdmi_ops->dpms)
hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
}
static void drm_hdmi_apply(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
int i;
DRM_DEBUG_KMS("%s\n", __FILE__);
for (i = 0; i < MIXER_WIN_NR; i++) {
if (!ctx->enabled[i])
continue;
if (mixer_ops && mixer_ops->win_commit)
mixer_ops->win_commit(ctx->mixer_ctx->ctx, i);
}
if (hdmi_ops && hdmi_ops->commit)
hdmi_ops->commit(ctx->hdmi_ctx->ctx);
}
static struct rockchip_drm_manager_ops drm_hdmi_manager_ops = {
.dpms = drm_hdmi_dpms,
.apply = drm_hdmi_apply,
.enable_vblank = drm_hdmi_enable_vblank,
.disable_vblank = drm_hdmi_disable_vblank,
.wait_for_vblank = drm_hdmi_wait_for_vblank,
.mode_fixup = drm_hdmi_mode_fixup,
.mode_set = drm_hdmi_mode_set,
.get_max_resol = drm_hdmi_get_max_resol,
.commit = drm_hdmi_commit,
};
static void drm_mixer_mode_set(struct device *subdrv_dev,
struct rockchip_drm_overlay *overlay)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (mixer_ops && mixer_ops->win_mode_set)
mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay);
}
static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (win < 0 || win > MIXER_WIN_NR) {
DRM_ERROR("mixer window[%d] is wrong\n", win);
return;
}
if (mixer_ops && mixer_ops->win_commit)
mixer_ops->win_commit(ctx->mixer_ctx->ctx, win);
ctx->enabled[win] = true;
}
static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (win < 0 || win > MIXER_WIN_NR) {
DRM_ERROR("mixer window[%d] is wrong\n", win);
return;
}
if (mixer_ops && mixer_ops->win_disable)
mixer_ops->win_disable(ctx->mixer_ctx->ctx, win);
ctx->enabled[win] = false;
}
static struct rockchip_drm_overlay_ops drm_hdmi_overlay_ops = {
.mode_set = drm_mixer_mode_set,
.commit = drm_mixer_commit,
.disable = drm_mixer_disable,
};
static struct rockchip_drm_manager hdmi_manager = {
.pipe = -1,
.ops = &drm_hdmi_manager_ops,
.overlay_ops = &drm_hdmi_overlay_ops,
.display_ops = &drm_hdmi_display_ops,
};
static int hdmi_subdrv_probe(struct drm_device *drm_dev,
struct device *dev)
{
struct rockchip_drm_subdrv *subdrv = to_subdrv(dev);
struct drm_hdmi_context *ctx;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!hdmi_ctx) {
DRM_ERROR("hdmi context not initialized.\n");
return -EFAULT;
}
if (!mixer_ctx) {
DRM_ERROR("mixer context not initialized.\n");
return -EFAULT;
}
ctx = get_ctx_from_subdrv(subdrv);
if (!ctx) {
DRM_ERROR("no drm hdmi context.\n");
return -EFAULT;
}
ctx->hdmi_ctx = hdmi_ctx;
ctx->mixer_ctx = mixer_ctx;
ctx->hdmi_ctx->drm_dev = drm_dev;
ctx->mixer_ctx->drm_dev = drm_dev;
if (mixer_ops->iommu_on)
mixer_ops->iommu_on(ctx->mixer_ctx->ctx, true);
return 0;
}
static void hdmi_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
{
struct drm_hdmi_context *ctx;
struct rockchip_drm_subdrv *subdrv = to_subdrv(dev);
ctx = get_ctx_from_subdrv(subdrv);
if (mixer_ops->iommu_on)
mixer_ops->iommu_on(ctx->mixer_ctx->ctx, false);
}
static int rockchip_drm_hdmi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rockchip_drm_subdrv *subdrv;
struct drm_hdmi_context *ctx;
DRM_DEBUG_KMS("%s\n", __FILE__);
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
DRM_LOG_KMS("failed to alloc common hdmi context.\n");
return -ENOMEM;
}
subdrv = &ctx->subdrv;
subdrv->dev = dev;
subdrv->manager = &hdmi_manager;
subdrv->probe = hdmi_subdrv_probe;
subdrv->remove = hdmi_subdrv_remove;
platform_set_drvdata(pdev, subdrv);
rockchip_drm_subdrv_register(subdrv);
return 0;
}
static int rockchip_drm_hdmi_remove(struct platform_device *pdev)
{
struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
DRM_DEBUG_KMS("%s\n", __FILE__);
rockchip_drm_subdrv_unregister(&ctx->subdrv);
return 0;
}
struct platform_driver rockchip_drm_common_hdmi_driver = {
.probe = rockchip_drm_hdmi_probe,
.remove = rockchip_drm_hdmi_remove,
.driver = {
.name = "rockchip-drm-hdmi",
.owner = THIS_MODULE,
},
};

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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.
*/
#ifndef _ROCKCHIP_DRM_HDMI_H_
#define _ROCKCHIP_DRM_HDMI_H_
#define MIXER_WIN_NR 3
#define MIXER_DEFAULT_WIN 0
/*
* rockchip hdmi common context structure.
*
* @drm_dev: pointer to drm_device.
* @ctx: pointer to the context of specific device driver.
* this context should be hdmi_context or mixer_context.
*/
struct rockchip_drm_hdmi_context {
struct drm_device *drm_dev;
void *ctx;
};
struct rockchip_hdmi_ops {
/* display */
bool (*is_connected)(void *ctx);
struct edid *(*get_edid)(void *ctx,
struct drm_connector *connector);
int (*check_timing)(void *ctx, struct fb_videomode *timing);
int (*power_on)(void *ctx, int mode);
/* manager */
void (*mode_set)(void *ctx, void *mode);
void (*get_max_resol)(void *ctx, unsigned int *width,
unsigned int *height);
void (*commit)(void *ctx);
void (*dpms)(void *ctx, int mode);
};
struct rockchip_mixer_ops {
/* manager */
int (*iommu_on)(void *ctx, bool enable);
int (*enable_vblank)(void *ctx, int pipe);
void (*disable_vblank)(void *ctx);
void (*wait_for_vblank)(void *ctx);
void (*dpms)(void *ctx, int mode);
/* overlay */
void (*win_mode_set)(void *ctx, struct rockchip_drm_overlay *overlay);
void (*win_commit)(void *ctx, int zpos);
void (*win_disable)(void *ctx, int zpos);
/* display */
int (*check_timing)(void *ctx, struct fb_videomode *timing);
};
void rockchip_hdmi_drv_attach(struct rockchip_drm_hdmi_context *ctx);
void rockchip_mixer_drv_attach(struct rockchip_drm_hdmi_context *ctx);
void rockchip_hdmi_ops_register(struct rockchip_hdmi_ops *ops);
void rockchip_mixer_ops_register(struct rockchip_mixer_ops *ops);
#endif

View File

@ -0,0 +1,138 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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 <drmP.h>
#include <drm/rockchip_drm.h>
#include <linux/dma-mapping.h>
#include <linux/iommu.h>
#include <linux/kref.h>
#include <asm/dma-iommu.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_iommu.h"
/*
* drm_create_iommu_mapping - create a mapping structure
*
* @drm_dev: DRM device
*/
int drm_create_iommu_mapping(struct drm_device *drm_dev)
{
struct dma_iommu_mapping *mapping = NULL;
struct rockchip_drm_private *priv = drm_dev->dev_private;
struct device *dev = drm_dev->dev;
if (!priv->da_start)
priv->da_start = ROCKCHIP_DEV_ADDR_START;
if (!priv->da_space_size)
priv->da_space_size = ROCKCHIP_DEV_ADDR_SIZE;
if (!priv->da_space_order)
priv->da_space_order = ROCKCHIP_DEV_ADDR_ORDER;
mapping = arm_iommu_create_mapping(&platform_bus_type, priv->da_start,
priv->da_space_size,
priv->da_space_order);
if (IS_ERR(mapping))
return PTR_ERR(mapping);
dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
GFP_KERNEL);
dma_set_max_seg_size(dev, 0xffffffffu);
dev->archdata.mapping = mapping;
return 0;
}
/*
* drm_release_iommu_mapping - release iommu mapping structure
*
* @drm_dev: DRM device
*
* if mapping->kref becomes 0 then all things related to iommu mapping
* will be released
*/
void drm_release_iommu_mapping(struct drm_device *drm_dev)
{
struct device *dev = drm_dev->dev;
arm_iommu_release_mapping(dev->archdata.mapping);
}
/*
* drm_iommu_attach_device- attach device to iommu mapping
*
* @drm_dev: DRM device
* @subdrv_dev: device to be attach
*
* This function should be called by sub drivers to attach it to iommu
* mapping.
*/
int drm_iommu_attach_device(struct drm_device *drm_dev,
struct device *subdrv_dev)
{
struct device *dev = drm_dev->dev;
int ret;
if (!dev->archdata.mapping) {
DRM_ERROR("iommu_mapping is null.\n");
return -EFAULT;
}
subdrv_dev->dma_parms = devm_kzalloc(subdrv_dev,
sizeof(*subdrv_dev->dma_parms),
GFP_KERNEL);
dma_set_max_seg_size(subdrv_dev, 0xffffffffu);
ret = arm_iommu_attach_device(subdrv_dev, dev->archdata.mapping);
if (ret < 0) {
DRM_DEBUG_KMS("failed iommu attach.\n");
return ret;
}
/*
* Set dma_ops to drm_device just one time.
*
* The dma mapping api needs device object and the api is used
* to allocate physial memory and map it with iommu table.
* If iommu attach succeeded, the sub driver would have dma_ops
* for iommu and also all sub drivers have same dma_ops.
*/
if (!dev->archdata.dma_ops)
dev->archdata.dma_ops = subdrv_dev->archdata.dma_ops;
return 0;
}
/*
* drm_iommu_detach_device -detach device address space mapping from device
*
* @drm_dev: DRM device
* @subdrv_dev: device to be detached
*
* This function should be called by sub drivers to detach it from iommu
* mapping
*/
void drm_iommu_detach_device(struct drm_device *drm_dev,
struct device *subdrv_dev)
{
struct device *dev = drm_dev->dev;
struct dma_iommu_mapping *mapping = dev->archdata.mapping;
if (!mapping || !mapping->domain)
return;
iommu_detach_device(mapping->domain, subdrv_dev);
drm_release_iommu_mapping(drm_dev);
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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.
*/
#ifndef _ROCKCHIP_DRM_IOMMU_H_
#define _ROCKCHIP_DRM_IOMMU_H_
#define ROCKCHIP_DEV_ADDR_START 0x20000000
#define ROCKCHIP_DEV_ADDR_SIZE 0x40000000
#define ROCKCHIP_DEV_ADDR_ORDER 0x0
#ifdef CONFIG_DRM_ROCKCHIP_IOMMU
int drm_create_iommu_mapping(struct drm_device *drm_dev);
void drm_release_iommu_mapping(struct drm_device *drm_dev);
int drm_iommu_attach_device(struct drm_device *drm_dev,
struct device *subdrv_dev);
void drm_iommu_detach_device(struct drm_device *dev_dev,
struct device *subdrv_dev);
static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)
{
#ifdef CONFIG_ARM_DMA_USE_IOMMU
struct device *dev = drm_dev->dev;
return dev->archdata.mapping ? true : false;
#else
return false;
#endif
}
#else
struct dma_iommu_mapping;
static inline int drm_create_iommu_mapping(struct drm_device *drm_dev)
{
return 0;
}
static inline void drm_release_iommu_mapping(struct drm_device *drm_dev)
{
}
static inline int drm_iommu_attach_device(struct drm_device *drm_dev,
struct device *subdrv_dev)
{
return 0;
}
static inline void drm_iommu_detach_device(struct drm_device *drm_dev,
struct device *subdrv_dev)
{
}
static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)
{
return false;
}
#endif
#endif

View File

@ -0,0 +1,305 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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/drmP.h>
#include <drm/rockchip_drm.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_encoder.h"
#include "rockchip_drm_fb.h"
#include "rockchip_drm_gem.h"
#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
struct rockchip_plane {
struct drm_plane base;
struct rockchip_drm_overlay overlay;
bool enabled;
};
static const uint32_t formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_NV12,
DRM_FORMAT_NV12MT,
};
/*
* This function is to get X or Y size shown via screen. This needs length and
* start position of CRTC.
*
* <--- length --->
* CRTC ----------------
* ^ start ^ end
*
* There are six cases from a to f.
*
* <----- SCREEN ----->
* 0 last
* ----------|------------------|----------
* CRTCs
* a -------
* b -------
* c --------------------------
* d --------
* e -------
* f -------
*/
static int rockchip_plane_get_size(int start, unsigned length, unsigned last)
{
int end = start + length;
int size = 0;
if (start <= 0) {
if (end > 0)
size = min_t(unsigned, end, last);
} else if (start <= last) {
size = min_t(unsigned, last - start, length);
}
return size;
}
int rockchip_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
{
struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
struct rockchip_drm_overlay *overlay = &rockchip_plane->overlay;
unsigned int actual_w;
unsigned int actual_h;
int nr;
int i;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
nr = rockchip_drm_fb_get_buf_cnt(fb);
for (i = 0; i < nr; i++) {
struct rockchip_drm_gem_buf *buffer = rockchip_drm_fb_buffer(fb, i);
if (!buffer) {
DRM_LOG_KMS("buffer is null\n");
return -EFAULT;
}
overlay->dma_addr[i] = buffer->dma_addr;
DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n",
i, (unsigned long)overlay->dma_addr[i]);
}
actual_w = rockchip_plane_get_size(crtc_x, crtc_w, crtc->mode.hdisplay);
actual_h = rockchip_plane_get_size(crtc_y, crtc_h, crtc->mode.vdisplay);
if (crtc_x < 0) {
if (actual_w)
src_x -= crtc_x;
crtc_x = 0;
}
if (crtc_y < 0) {
if (actual_h)
src_y -= crtc_y;
crtc_y = 0;
}
/* set drm framebuffer data. */
overlay->fb_x = src_x;
overlay->fb_y = src_y;
overlay->fb_width = fb->width;
overlay->fb_height = fb->height;
overlay->src_width = src_w;
overlay->src_height = src_h;
overlay->bpp = fb->bits_per_pixel;
overlay->pitch = fb->pitches[0];
overlay->pixel_format = fb->pixel_format;
/* set overlay range to be displayed. */
overlay->crtc_x = crtc_x;
overlay->crtc_y = crtc_y;
overlay->crtc_width = actual_w;
overlay->crtc_height = actual_h;
/* set drm mode data. */
overlay->mode_width = crtc->mode.hdisplay;
overlay->mode_height = crtc->mode.vdisplay;
overlay->refresh = crtc->mode.vrefresh;
overlay->scan_flag = crtc->mode.flags;
DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)",
overlay->crtc_x, overlay->crtc_y,
overlay->crtc_width, overlay->crtc_height);
rockchip_drm_fn_encoder(crtc, overlay, rockchip_drm_encoder_plane_mode_set);
return 0;
}
void rockchip_plane_commit(struct drm_plane *plane)
{
struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
struct rockchip_drm_overlay *overlay = &rockchip_plane->overlay;
rockchip_drm_fn_encoder(plane->crtc, &overlay->zpos,
rockchip_drm_encoder_plane_commit);
}
void rockchip_plane_dpms(struct drm_plane *plane, int mode)
{
struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
struct rockchip_drm_overlay *overlay = &rockchip_plane->overlay;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
if (mode == DRM_MODE_DPMS_ON) {
if (rockchip_plane->enabled)
return;
rockchip_drm_fn_encoder(plane->crtc, &overlay->zpos,
rockchip_drm_encoder_plane_enable);
rockchip_plane->enabled = true;
} else {
if (!rockchip_plane->enabled)
return;
rockchip_drm_fn_encoder(plane->crtc, &overlay->zpos,
rockchip_drm_encoder_plane_disable);
rockchip_plane->enabled = false;
}
}
static int
rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
{
int ret;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
ret = rockchip_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y,
crtc_w, crtc_h, src_x >> 16, src_y >> 16,
src_w >> 16, src_h >> 16);
if (ret < 0)
return ret;
plane->crtc = crtc;
rockchip_plane_commit(plane);
rockchip_plane_dpms(plane, DRM_MODE_DPMS_ON);
return 0;
}
static int rockchip_disable_plane(struct drm_plane *plane)
{
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
rockchip_plane_dpms(plane, DRM_MODE_DPMS_OFF);
return 0;
}
static void rockchip_plane_destroy(struct drm_plane *plane)
{
struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
rockchip_disable_plane(plane);
drm_plane_cleanup(plane);
kfree(rockchip_plane);
}
static int rockchip_plane_set_property(struct drm_plane *plane,
struct drm_property *property,
uint64_t val)
{
struct drm_device *dev = plane->dev;
struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
struct rockchip_drm_private *dev_priv = dev->dev_private;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
if (property == dev_priv->plane_zpos_property) {
rockchip_plane->overlay.zpos = val;
return 0;
}
return -EINVAL;
}
static struct drm_plane_funcs rockchip_plane_funcs = {
.update_plane = rockchip_update_plane,
.disable_plane = rockchip_disable_plane,
.destroy = rockchip_plane_destroy,
.set_property = rockchip_plane_set_property,
};
static void rockchip_plane_attach_zpos_property(struct drm_plane *plane)
{
struct drm_device *dev = plane->dev;
struct rockchip_drm_private *dev_priv = dev->dev_private;
struct drm_property *prop;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
prop = dev_priv->plane_zpos_property;
if (!prop) {
prop = drm_property_create_range(dev, 0, "zpos", 0,
MAX_PLANE - 1);
if (!prop)
return;
dev_priv->plane_zpos_property = prop;
}
drm_object_attach_property(&plane->base, prop, 0);
}
struct drm_plane *rockchip_plane_init(struct drm_device *dev,
unsigned int possible_crtcs, bool priv)
{
struct rockchip_plane *rockchip_plane;
int err;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
rockchip_plane = kzalloc(sizeof(struct rockchip_plane), GFP_KERNEL);
if (!rockchip_plane) {
DRM_ERROR("failed to allocate plane\n");
return NULL;
}
err = drm_plane_init(dev, &rockchip_plane->base, possible_crtcs,
&rockchip_plane_funcs, formats, ARRAY_SIZE(formats),
priv);
if (err) {
DRM_ERROR("failed to initialize plane\n");
kfree(rockchip_plane);
return NULL;
}
if (priv)
rockchip_plane->overlay.zpos = DEFAULT_ZPOS;
else
rockchip_plane_attach_zpos_property(&rockchip_plane->base);
return &rockchip_plane->base;
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) ROCKCHIP, Inc.
* Author:yzq<yzq@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.
*/
int rockchip_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h);
void rockchip_plane_commit(struct drm_plane *plane);
void rockchip_plane_dpms(struct drm_plane *plane, int mode);
struct drm_plane *rockchip_plane_init(struct drm_device *dev,
unsigned int possible_crtcs, bool priv);

View File

@ -0,0 +1,4 @@
#
# Generated files
#
*lcd.h

View File

@ -0,0 +1,14 @@
choice
prompt "LCD Panel Select"
config LCD_GENERAL
bool "General lcd panel"
help
select if the panel do not need initialization
config LCD_LD089WU1_MIPI
bool "mipi dsi lcd LD089WU1 1920X1200"
config LCD_B080XAN02_MIPI
bool "mipi dsi lcd B080XAN02 1024X768"
endchoice

View File

@ -0,0 +1,24 @@
obj-$(CONFIG_LCD_GENERAL) += lcd_general.o
obj-$(CONFIG_LCD_LD089WU1_MIPI) += lcd_LD089WU1_mipi.o
obj-$(CONFIG_LCD_B080XAN02_MIPI) += lcd_B080XAN02_mipi.o
quiet_cmd_gen = GEN $@
cmd_gen = cmp -s $< $@ || cp $< $@
lcd-obj := $(filter lcd_%.o,$(obj-y))
lcd-cfile := $(patsubst %.o,%.c,$(lcd-obj))
lcd-cpath := $(src)/$(lcd-cfile)
obj-y := $(filter-out $(lcd-obj),$(obj-y))
$(obj)/lcd.h: $(lcd-cpath) FORCE
$(call if_changed,gen)
$(obj)/rk_screen.o: $(obj)/lcd.h
obj-y += rk_screen.o
clean-files := lcd.h

View File

@ -0,0 +1,72 @@
#ifndef __LCD_B080XAN02__
#define __LCD_B080XAN02__
#if defined(CONFIG_MIPI_DSI)
#include "../transmitter/mipi_dsi.h"
#endif
#include <linux/delay.h>
#define RK_SCREEN_INIT 1
/* about mipi */
#define MIPI_DSI_LANE 4
#define MIPI_DSI_HS_CLK 528*1000000 //1000*1000000
#if defined(RK_SCREEN_INIT)
static struct rk29lcd_info *gLcd_info = NULL;
int rk_lcd_init(void) {
u8 dcs[16] = {0};
if(dsi_is_active() != 1)
return -1;
/*below is changeable*/
dsi_enable_hs_clk(1);
dcs[0] = LPDT;
dcs[1] = dcs_exit_sleep_mode;
dsi_send_dcs_packet(dcs, 2);
msleep(1);
dcs[0] = LPDT;
dcs[1] = dcs_set_display_on;
dsi_send_dcs_packet(dcs, 2);
msleep(10);
//dsi_enable_command_mode(0);
dsi_enable_video_mode(1);
printk("++++++++++++++++%s:%d\n", __func__, __LINE__);
return 0;
}
int rk_lcd_standby(u8 enable) {
u8 dcs[16] = {0};
if(dsi_is_active() != 1)
return -1;
if(enable) {
/*below is changeable*/
dcs[0] = LPDT;
dcs[1] = dcs_set_display_off;
dsi_send_dcs_packet(dcs, 2);
msleep(1);
dcs[0] = LPDT;
dcs[1] = dcs_enter_sleep_mode;
dsi_send_dcs_packet(dcs, 2);
msleep(1);
printk("++++enable++++++++++++%s:%d\n", __func__, __LINE__);
} else {
/*below is changeable*/
rk_lcd_init();
printk("++++++++++++++++%s:%d\n", __func__, __LINE__);
}
return 0;
}
#endif
#endif

View File

@ -0,0 +1,70 @@
#ifndef __LCD_LD089WU1__
#define __LCD_LD089WU1__
#if defined(CONFIG_MIPI_DSI)
#include "../transmitter/mipi_dsi.h"
#endif
#define RK_SCREEN_INIT 1
/* about mipi */
#define MIPI_DSI_LANE 4
#define MIPI_DSI_HS_CLK 1000*1000000
#if defined(RK_SCREEN_INIT)
static struct rk29lcd_info *gLcd_info = NULL;
int rk_lcd_init(void) {
u8 dcs[16] = {0};
if(dsi_is_active() != 1)
return -1;
/*below is changeable*/
dsi_enable_hs_clk(1);
dsi_enable_video_mode(0);
dsi_enable_command_mode(1);
dcs[0] = dcs_exit_sleep_mode;
dsi_send_dcs_packet(dcs, 1);
msleep(1);
dcs[0] = dcs_set_display_on;
dsi_send_dcs_packet(dcs, 1);
msleep(10);
dsi_enable_command_mode(0);
dsi_enable_video_mode(1);
//printk("++++++++++++++++%s:%d\n", __func__, __LINE__);
};
int rk_lcd_standby(u8 enable) {
u8 dcs[16] = {0};
if(dsi_is_active() != 1)
return -1;
if(enable) {
dsi_enable_video_mode(0);
dsi_enable_command_mode(1);
/*below is changeable*/
dcs[0] = dcs_set_display_off;
dsi_send_dcs_packet(dcs, 1);
msleep(1);
dcs[0] = dcs_enter_sleep_mode;
dsi_send_dcs_packet(dcs, 1);
msleep(1);
//printk("++++++++++++++++%s:%d\n", __func__, __LINE__);
} else {
/*below is changeable*/
rk_lcd_init();
//printk("++++++++++++++++%s:%d\n", __func__, __LINE__);
}
};
#endif
#endif

View File

@ -0,0 +1,8 @@
#ifndef __LCD_NULL__
#define __LCD_NULL__
#endif

View File

@ -0,0 +1,65 @@
#include <linux/rk_fb.h>
#include <linux/device.h>
#include "lcd.h"
static struct rk_screen *rk_screen;
int rk_fb_get_prmry_screen(struct rk_screen *screen)
{
memcpy(screen, rk_screen, sizeof(struct rk_screen));
return 0;
}
size_t get_fb_size(void)
{
}
static int rk_screen_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
int ret;
if (!np) {
dev_err(&pdev->dev, "Missing device tree node.\n");
return -EINVAL;
}
rk_screen = devm_kzalloc(&pdev->dev, sizeof(struct rk_screen), GFP_KERNEL);
if (!rk_screen) {
dev_err(&pdev->dev, "kmalloc for rk screen fail!");
return -ENOMEM;
}
ret = rk_fb_prase_timing_dt(np,rk_screen);
dev_info(&pdev->dev, "rockchip screen probe %s\n",
ret? "failed" : "success");
return ret;
}
static const struct of_device_id rk_screen_dt_ids[] = {
{ .compatible = "rockchip,screen", },
{}
};
static struct platform_driver rk_screen_driver = {
.probe = rk_screen_probe,
.driver = {
.name = "rk-screen",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(rk_screen_dt_ids),
},
};
static int __init rk_screen_init(void)
{
return platform_driver_register(&rk_screen_driver);
}
static void __exit rk_screen_exit(void)
{
platform_driver_unregister(&rk_screen_driver);
}
subsys_initcall_sync(rk_screen_init);
module_exit(rk_screen_exit);

View File

@ -0,0 +1,72 @@
menuconfig RK_TRSM
bool "RockChip display transmitter support"
depends on FB_ROCKCHIP
config RK2928_LVDS
bool "RK2928/RK2926 lvds transmitter support"
depends on ARCH_RK2928 && RK_TRSM
config RK3026_LVDS
depends on ARCH_RK3026 && RK_TRSM
bool "RK3026/RK3028A lvds transmitter support"
default y
config RK32_LVDS
bool "RK32 lvds transmitter support"
depends on RK_TRSM
config RK610_LVDS
bool "RK610(Jetta) lvds transmitter support"
depends on MFD_RK610 && RK_TRSM
help
Support Jetta(RK610) to output LCD1 and LVDS.
config RK616_LVDS
bool "RK616(JettaB) lvds,lcd,scaler vido interface support"
depends on MFD_RK616 && RK_TRSM
help
RK616(Jetta B) LVDS,LCD,scaler transmitter support.
config DP_ANX6345
bool "RGB to DisplayPort transmitter anx6345,anx9804,anx9805 support"
depends on RK_TRSM
config DP501
bool"RGB to DisplayPort transmitter dp501 support"
depends on RK_TRSM
config RK32_DP
bool "RK32 RGB to DisplayPort transmitter support "
depends on RK_TRSM
config MIPI_DSI
depends on RK_TRSM
bool "Rockchip MIPI DSI support"
config TC358768_RGB2MIPI
tristate "toshiba TC358768 RGB to MIPI DSI"
depends on MIPI_DSI
help
"a chip that change RGB interface parallel signal into DSI serial signal"
config SSD2828_RGB2MIPI
tristate "solomon SSD2828 RGB to MIPI DSI"
depends on MIPI_DSI
help
"a chip that change RGB interface parallel signal into DSI serial signal"
config RK616_MIPI_DSI
tristate "Rockchip mipi dsi support"
depends on MIPI_DSI
help
Rockchip mipi dsi support.
config RK616_MIPI_DSI_RST
bool "Reset the rockchip mipi dsi"
depends on MFD_RK616 && RK616_MIPI_DSI && RK616_USE_MCLK_12M
default y
help
if you say y here: inset the hdmi, mipi lcd will be reset.

View File

@ -0,0 +1,16 @@
#
# Makefile for display transmitter like lvds edp mipi
#
obj-$(CONFIG_RK2928_LVDS) += rk2928_lvds.o
obj-$(CONFIG_RK3026_LVDS) += rk3026_lvds.o
obj-$(CONFIG_RK610_LVDS) += rk610_lcd.o
obj-$(CONFIG_RK616_LVDS) += rk616_lvds.o
obj-y += rk32_lvds.o
obj-$(CONFIG_DP_ANX6345) += dp_anx6345.o
obj-$(CONFIG_DP501) += dp501.o
obj-$(CONFIG_RK32_DP) += rk32_dp.o rk32_dp_reg.o
obj-$(CONFIG_MIPI_DSI) += mipi_dsi.o
obj-$(CONFIG_RK616_MIPI_DSI) += rk616_mipi_dsi.o
obj-$(CONFIG_TC358768_RGB2MIPI) += tc358768.o
obj-$(CONFIG_SSD2828_RGB2MIPI) += ssd2828.o

View File

@ -0,0 +1,323 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/rk_fb.h>
#include <linux/rockchip/iomap.h>
#include <linux/rockchip/grf.h>
#include "rk32_lvds.h"
//#define TTL_TO_LVDS 1
static struct rk32_lvds *rk32_lvds;
static int rk32_lvds_disable(void)
{
struct rk32_lvds *lvds = rk32_lvds;
writel_relaxed(0x80008000, RK_GRF_VIRT + RK3288_GRF_SOC_CON7);
writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_21); /*disable tx*/
writel_relaxed(0xff, lvds->regs + LVDS_CFG_REG_c); /*disable pll*/
clk_disable_unprepare(lvds->clk);
return 0;
}
static int rk32_lvds_en(void)
{
struct rk32_lvds *lvds = rk32_lvds;
struct rk_screen *screen = &lvds->screen;
u32 h_bp = screen->mode.hsync_len + screen->mode.left_margin;
u32 i,j, val ;
clk_prepare_enable(lvds->clk);
screen->type = SCREEN_RGB;
screen->lcdc_id = 1;
if (screen->lcdc_id == 1) /*lcdc1 = vop little,lcdc0 = vop big*/
val = LVDS_SEL_VOP_LIT | (LVDS_SEL_VOP_LIT << 16);
else
val = LVDS_SEL_VOP_LIT << 16;
writel_relaxed(val, RK_GRF_VIRT + RK3288_GRF_SOC_CON6);
val = screen->lvds_format;
if (screen->type == SCREEN_DUAL_LVDS)
val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN;
else if(screen->type == SCREEN_LVDS)
val |= LVDS_CH0_EN;
//val |= LVDS_MSB;
else if (screen->type == SCREEN_RGB)
val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN;
if (h_bp & 0x01)
val |= LVDS_START_PHASE_RST_1;
val |= (screen->pin_dclk << 8) | (screen->pin_hsync << 9) |
(screen->pin_den << 10);
val |= 0xffff << 16;
//val = 0x08010801;
writel_relaxed(val, RK_GRF_VIRT + RK3288_GRF_SOC_CON7);
if (screen->type == SCREEN_LVDS)
val = 0xbf;
else
val = 0x7f;
#if 0
for(i=0;i<0x200;){
val = readl_relaxed(lvds->regs + i);
printk("0x%08x:0x%08x ",i,val);
i += 4;
if(i % 16 == 0)
printk("\n");
}
#endif
#ifdef TTL_TO_LVDS // 0 ttl 1 lvds
val = 0x007f007f;//0x1<<6 |0x1 <<4;
writel_relaxed(val, RK_GRF_VIRT + 0xc);
lvds_writel(lvds, LVDS_CH0_REG_0, 0x7f);
lvds_writel(lvds, LVDS_CH0_REG_1, 0x40);
lvds_writel(lvds, LVDS_CH0_REG_2, 0x00);
if (screen->type == SCREEN_RGB)
val = 0x1f;
else
val = 0x00;
lvds_writel(lvds, LVDS_CH0_REG_4, 0x3f);
lvds_writel(lvds, LVDS_CH0_REG_5, 0x3f);
lvds_writel(lvds, LVDS_CH0_REG_3, 0x46);
lvds_writel(lvds, LVDS_CH0_REG_d, 0x0a);
lvds_writel(lvds, LVDS_CH0_REG_20,0x44);/* 44:LSB 45:MSB*/
writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_c); /*eanble pll*/
writel_relaxed(0x92, lvds->regs + LVDS_CFG_REG_21); /*enable tx*/
lvds_writel(lvds, 0x100, 0x7f);
lvds_writel(lvds, 0x104, 0x40);
lvds_writel(lvds, 0x108, 0x00);
lvds_writel(lvds, 0x10c, 0x46);
lvds_writel(lvds, 0x110, 0x3f);
lvds_writel(lvds, 0x114, 0x3f);
lvds_writel(lvds, 0x134, 0x0a);
#else
val = readl_relaxed(lvds->regs + 0x88);
printk("0x88:0x%x\n",val);
lvds_writel(lvds, LVDS_CH0_REG_0, 0xbf);
lvds_writel(lvds, LVDS_CH0_REG_1, 0x3f);// 3f
lvds_writel(lvds, LVDS_CH0_REG_2, 0xfe);
lvds_writel(lvds, LVDS_CH0_REG_3, 0x46);//0x46
lvds_writel(lvds, LVDS_CH0_REG_4, 0x00);
//lvds_writel(lvds, LVDS_CH0_REG_9, 0x20);
//lvds_writel(lvds, LVDS_CH0_REG_d, 0x4b);
//lvds_writel(lvds, LVDS_CH0_REG_f, 0x0d);
lvds_writel(lvds, LVDS_CH0_REG_d, 0x0a);//0a
lvds_writel(lvds, LVDS_CH0_REG_20,0x44);/* 44:LSB 45:MSB*/
//lvds_writel(lvds, 0x24,0x20);
//writel_relaxed(0x23, lvds->regs + 0x88);
writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_c); /*eanble pll*/
writel_relaxed(0x92, lvds->regs + LVDS_CFG_REG_21); /*enable tx*/
//lvds_writel(lvds, 0x100, 0xbf);
//lvds_writel(lvds, 0x104, 0x3f);
//lvds_writel(lvds, 0x108, 0xfe);
//lvds_writel(lvds, 0x10c, 0x46); //0x46
//lvds_writel(lvds, 0x110, 0x00);
//lvds_writel(lvds, 0x114, 0x00);
//lvds_writel(lvds, 0x134, 0x0a);
#endif
#if 0
for(i=0;i<100;i++){
mdelay(1000);
mdelay(1000);
mdelay(1000);
mdelay(1000);
mdelay(1000);
printk("write LVDS_CH0_REG_20 :0x40\n");
//writel_relaxed(0x10, lvds->regs + LVDS_CFG_REG_c);
lvds_writel(lvds, LVDS_CH0_REG_20,0x40);/* 44:LSB 45:MSB*/
val = readl_relaxed(lvds->regs + LVDS_CH0_REG_20);
printk("read back LVDS_CH0_REG_20:0x%x\n",val);
mdelay(1000);
mdelay(1000);
mdelay(1000);
mdelay(1000);
mdelay(1000);
printk("write LVDS_CH0_REG_20 :0x44\n");
lvds_writel(lvds, LVDS_CH0_REG_20,0x44);/* 44:LSB 45:MSB*/
val = readl_relaxed(lvds->regs + LVDS_CH0_REG_20);
printk("read back LVDS_CH0_REG_20:0x%x\n",val);
}
#endif
//while(1)
#if 0
{
val = readl_relaxed(RK_GRF_VIRT + RK3288_GRF_SOC_CON6);
printk("RK3288_GRF_SOC_CON6:0x%x\n",val);
val = readl_relaxed(RK_GRF_VIRT + RK3288_GRF_SOC_CON7);
printk("RK3288_GRF_SOC_CON7:0x%x\n",val);
val = readl_relaxed(RK_GRF_VIRT + RK3288_GRF_SOC_CON15);
printk("RK3288_GRF_SOC_CON15:0x%x\n",val);
val = readl_relaxed(lvds->regs + LVDS_CH0_REG_0);
printk("LVDS_CH0_REG_0:0x%x\n",val);
val = readl_relaxed(lvds->regs + LVDS_CH0_REG_1);
printk("LVDS_CH0_REG_1:0x%x\n",val);
val = readl_relaxed(lvds->regs + LVDS_CH0_REG_2);
printk("LVDS_CH0_REG_2:0x%x\n",val);
val = readl_relaxed(lvds->regs + LVDS_CH0_REG_3);
printk("LVDS_CH0_REG_3:0x%x\n",val);
val = readl_relaxed(lvds->regs + LVDS_CH0_REG_4);
printk("LVDS_CH0_REG_4:0x%x\n",val);
val = readl_relaxed(lvds->regs + LVDS_CH0_REG_5);
printk("LVDS_CH0_REG_5:0x%x\n",val);
val = readl_relaxed(lvds->regs + LVDS_CH0_REG_d);
printk("LVDS_CH0_REG_d:0x%x\n",val);
val = readl_relaxed(lvds->regs + LVDS_CH0_REG_f);
printk("LVDS_CH0_REG_f:0x%x\n",val);
val = readl_relaxed(lvds->regs + LVDS_CFG_REG_c);
printk("LVDS_CFG_REG_c:0x%x\n",val);
val = readl_relaxed(lvds->regs + LVDS_CFG_REG_21);
printk("LVDS_CFG_REG_21:0x%x\n",val);
val = readl_relaxed(lvds->regs + 0x100);
printk("0x100:0x%x\n",val);
val = readl_relaxed(lvds->regs + 0x104);
printk("0x104:0x%x\n",val);
val = readl_relaxed(lvds->regs + 0x108);
printk("0x108:0x%x\n",val);
val = readl_relaxed(lvds->regs + 0x10c);
printk("0x10c:0x%x\n",val);
val = readl_relaxed(lvds->regs + 0x110);
printk("0x110:0x%x\n",val);
val = readl_relaxed(lvds->regs + 0x114);
printk("0x114:0x%x\n",val);
val = readl_relaxed(lvds->regs + 0x118);
printk("0x118:0x%x\n",val);
val = readl_relaxed(lvds->regs + 0x11c);
printk("0x11c:0x%x\n",val);
mdelay(1000);
}
for(i=0;i<0x200;){
val = readl_relaxed(lvds->regs + i);
printk("0x%08x:0x%08x ",i,val);
i += 4;
if(i % 16 == 0)
printk("\n");
}
#endif
return 0;
}
static struct rk_fb_trsm_ops trsm_lvds_ops = {
.enable = rk32_lvds_en,
.disable = rk32_lvds_disable,
};
static int rk32_lvds_probe(struct platform_device *pdev)
{
struct rk32_lvds *lvds;
struct resource *res;
struct device_node *np = pdev->dev.of_node;
if (!np) {
dev_err(&pdev->dev, "Missing device tree node.\n");
return -EINVAL;
}
lvds = devm_kzalloc(&pdev->dev, sizeof(struct rk32_lvds), GFP_KERNEL);
if (!lvds) {
dev_err(&pdev->dev, "no memory for state\n");
return -ENOMEM;
}
lvds->dev = &pdev->dev;
rk_fb_get_prmry_screen(&lvds->screen);
if ((lvds->screen.type != SCREEN_RGB) &&
(lvds->screen.type != SCREEN_LVDS) &&
(lvds->screen.type != SCREEN_DUAL_LVDS)) {
dev_err(&pdev->dev, "screen is not lvds/rgb!\n");
return -EINVAL;
}
platform_set_drvdata(pdev, lvds);
dev_set_name(lvds->dev, "rk32-lvds");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
lvds->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(lvds->regs)) {
dev_err(&pdev->dev, "ioremap reg failed\n");
return PTR_ERR(lvds->regs);
}
lvds->clk = devm_clk_get(&pdev->dev,NULL);
if (IS_ERR(lvds->clk)) {
dev_err(&pdev->dev, "get clk failed\n");
return PTR_ERR(lvds->clk);
}
rk32_lvds = lvds;
rk_fb_trsm_ops_register(&trsm_lvds_ops,SCREEN_LVDS);
dev_info(&pdev->dev, "rk32 lvds driver probe success\n");
return 0;
}
static void rk32_lvds_shutdown(struct platform_device *pdev)
{
}
#if defined(CONFIG_OF)
static const struct of_device_id rk32_lvds_dt_ids[] = {
{.compatible = "rockchip, rk32-lvds",},
{}
};
MODULE_DEVICE_TABLE(of, rk32_lvds_dt_ids);
#endif
static struct platform_driver rk32_lvds_driver = {
.probe = rk32_lvds_probe,
.driver = {
.name = "rk32-lvds",
.owner = THIS_MODULE,
#if defined(CONFIG_OF)
.of_match_table = of_match_ptr(rk32_lvds_dt_ids),
#endif
},
.shutdown = rk32_lvds_shutdown,
};
static int __init rk32_lvds_module_init(void)
{
return platform_driver_register(&rk32_lvds_driver);
}
static void __exit rk32_lvds_module_exit(void)
{
}
fs_initcall(rk32_lvds_module_init);
module_exit(rk32_lvds_module_exit);

View File

@ -0,0 +1,44 @@
#ifndef __RK32_LVDS__
#define __RK32_LVDS__
#define LVDS_CH0_REG_0 0x00
#define LVDS_CH0_REG_1 0x04
#define LVDS_CH0_REG_2 0x08
#define LVDS_CH0_REG_3 0x0c
#define LVDS_CH0_REG_4 0x10
#define LVDS_CH0_REG_5 0x14
#define LVDS_CH0_REG_9 0x24
#define LVDS_CFG_REG_c 0x30
#define LVDS_CH0_REG_d 0x34
#define LVDS_CH0_REG_f 0x3c
#define LVDS_CH0_REG_20 0x80
#define LVDS_CFG_REG_21 0x84
#define LVDS_SEL_VOP_LIT (1 << 3)
#define LVDS_FMT_MASK (0x07 << 16)
#define LVDS_MSB (0x01 << 3)
#define LVDS_DUAL (0x01 << 4)
#define LVDS_FMT_1 (0x01 << 5)
#define LVDS_TTL_EN (0x01 << 6)
#define LVDS_START_PHASE_RST_1 (0x01 << 7)
#define LVDS_DCLK_INV (0x01 << 8)
#define LVDS_CH0_EN (0x01 << 11)
#define LVDS_CH1_EN (0x01 << 12)
#define LVDS_PWRDN (0x01 << 15)
struct rk32_lvds {
struct device *dev;
void __iomem *regs;
struct clk *clk; /*phb clk*/
struct rk_screen screen;
};
static int inline lvds_writel(struct rk32_lvds *lvds, u32 offset, u32 val)
{
writel_relaxed(val, lvds->regs + offset);
//if (lvds->screen.type == SCREEN_DUAL_LVDS)
writel_relaxed(val, lvds->regs + offset + 0x100);
return 0;
}
#endif

View File

@ -204,7 +204,7 @@ int drm_err(const char *func, const char *format, ...);
* \param fmt printf() like format string.
* \param arg arguments
*/
#if DRM_DEBUG_CODE
#if 0// DRM_DEBUG_CODE
#define DRM_DEBUG(fmt, args...) \
do { \
drm_ut_debug_printk(DRM_UT_CORE, DRM_NAME, \

100
include/drm/rockchip_drm.h Normal file
View File

@ -0,0 +1,100 @@
/* rockchip_drm.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#ifndef _rockchip_DRM_H_
#define _rockchip_DRM_H_
#include <uapi/drm/rockchip_drm.h>
/**
* A structure for lcd panel information.
*
* @timing: default video mode for initializing
* @width_mm: physical size of lcd width.
* @height_mm: physical size of lcd height.
*/
struct rockchip_drm_panel_info {
struct fb_videomode timing;
u32 width_mm;
u32 height_mm;
};
/**
* Platform Specific Structure for DRM based FIMD.
*
* @panel: default panel info for initializing
* @default_win: default window layer number to be used for UI.
* @bpp: default bit per pixel.
*/
struct rockchip_drm_fimd_pdata {
struct rockchip_drm_panel_info panel;
u32 vidcon0;
u32 vidcon1;
unsigned int default_win;
unsigned int bpp;
};
/**
* Platform Specific Structure for DRM based HDMI.
*
* @hdmi_dev: device point to specific hdmi driver.
* @mixer_dev: device point to specific mixer driver.
*
* this structure is used for common hdmi driver and each device object
* would be used to access specific device driver(hdmi or mixer driver)
*/
struct rockchip_drm_common_hdmi_pd {
struct device *hdmi_dev;
struct device *mixer_dev;
};
/**
* Platform Specific Structure for DRM based HDMI core.
*
* @is_v13: set if hdmi version 13 is.
* @cfg_hpd: function pointer to configure hdmi hotplug detection pin
* @get_hpd: function pointer to get value of hdmi hotplug detection pin
*/
struct rockchip_drm_hdmi_pdata {
bool is_v13;
void (*cfg_hpd)(bool external);
int (*get_hpd)(void);
};
/**
* Platform Specific Structure for DRM based IPP.
*
* @inv_pclk: if set 1. invert pixel clock
* @inv_vsync: if set 1. invert vsync signal for wb
* @inv_href: if set 1. invert href signal
* @inv_hsync: if set 1. invert hsync signal for wb
*/
struct rockchip_drm_ipp_pol {
unsigned int inv_pclk;
unsigned int inv_vsync;
unsigned int inv_href;
unsigned int inv_hsync;
};
/**
* Platform Specific Structure for DRM based FIMC.
*
* @pol: current hardware block polarity settings.
* @clk_rate: current hardware clock rate.
*/
struct rockchip_drm_fimc_pdata {
struct rockchip_drm_ipp_pol pol;
int clk_rate;
};
#endif /* _rockchip_DRM_H_ */

View File

@ -0,0 +1,390 @@
/*
*
* Copyright (C) ROCKCHIP, Inc.
*Author:yzq<yzq@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.
*/
#ifndef _UAPI_ROCKCHIP_DRM_H_
#define _UAPI_ROCKCHIP_DRM_H_
#include <drm/drm.h>
/**
* User-desired buffer creation information structure.
*
* @size: user-desired memory allocation size.
* - this size value would be page-aligned internally.
* @flags: user request for setting memory type or cache attributes.
* @handle: returned a handle to created gem object.
* - this handle will be set by gem module of kernel side.
*/
struct drm_rockchip_gem_create {
uint64_t size;
unsigned int flags;
unsigned int handle;
};
/**
* A structure for getting buffer offset.
*
* @handle: a pointer to gem object created.
* @pad: just padding to be 64-bit aligned.
* @offset: relatived offset value of the memory region allocated.
* - this value should be set by user.
*/
struct drm_rockchip_gem_map_off {
unsigned int handle;
unsigned int pad;
uint64_t offset;
};
/**
* A structure for mapping buffer.
*
* @handle: a handle to gem object created.
* @pad: just padding to be 64-bit aligned.
* @size: memory size to be mapped.
* @mapped: having user virtual address mmaped.
* - this variable would be filled by exynos gem module
* of kernel side with user virtual address which is allocated
* by do_mmap().
*/
struct drm_rockchip_gem_mmap {
unsigned int handle;
unsigned int pad;
uint64_t size;
uint64_t mapped;
};
/**
* A structure to gem information.
*
* @handle: a handle to gem object created.
* @flags: flag value including memory type and cache attribute and
* this value would be set by driver.
* @size: size to memory region allocated by gem and this size would
* be set by driver.
*/
struct drm_rockchip_gem_info {
unsigned int handle;
unsigned int flags;
uint64_t size;
};
/**
* A structure for user connection request of virtual display.
*
* @connection: indicate whether doing connetion or not by user.
* @extensions: if this value is 1 then the vidi driver would need additional
* 128bytes edid data.
* @edid: the edid data pointer from user side.
*/
struct drm_rockchip_vidi_connection {
unsigned int connection;
unsigned int extensions;
uint64_t edid;
};
/* memory type definitions. */
enum e_drm_rockchip_gem_mem_type {
/* Physically Continuous memory and used as default. */
ROCKCHIP_BO_CONTIG = 0 << 0,
/* Physically Non-Continuous memory. */
ROCKCHIP_BO_NONCONTIG = 1 << 0,
/* non-cachable mapping and used as default. */
ROCKCHIP_BO_NONCACHABLE = 0 << 1,
/* cachable mapping. */
ROCKCHIP_BO_CACHABLE = 1 << 1,
/* write-combine mapping. */
ROCKCHIP_BO_WC = 1 << 2,
ROCKCHIP_BO_MASK = ROCKCHIP_BO_NONCONTIG | ROCKCHIP_BO_CACHABLE |
ROCKCHIP_BO_WC
};
struct drm_rockchip_g2d_get_ver {
__u32 major;
__u32 minor;
};
struct drm_rockchip_g2d_cmd {
__u32 offset;
__u32 data;
};
enum drm_rockchip_g2d_buf_type {
G2D_BUF_USERPTR = 1 << 31,
};
enum drm_rockchip_g2d_event_type {
G2D_EVENT_NOT,
G2D_EVENT_NONSTOP,
G2D_EVENT_STOP, /* not yet */
};
struct drm_rockchip_g2d_userptr {
unsigned long userptr;
unsigned long size;
};
struct drm_rockchip_g2d_set_cmdlist {
__u64 cmd;
__u64 cmd_buf;
__u32 cmd_nr;
__u32 cmd_buf_nr;
/* for g2d event */
__u64 event_type;
__u64 user_data;
};
struct drm_rockchip_g2d_exec {
__u64 async;
};
enum drm_rockchip_ops_id {
ROCKCHIP_DRM_OPS_SRC,
ROCKCHIP_DRM_OPS_DST,
ROCKCHIP_DRM_OPS_MAX,
};
struct drm_rockchip_sz {
__u32 hsize;
__u32 vsize;
};
struct drm_rockchip_pos {
__u32 x;
__u32 y;
__u32 w;
__u32 h;
};
enum drm_rockchip_flip {
ROCKCHIP_DRM_FLIP_NONE = (0 << 0),
ROCKCHIP_DRM_FLIP_VERTICAL = (1 << 0),
ROCKCHIP_DRM_FLIP_HORIZONTAL = (1 << 1),
ROCKCHIP_DRM_FLIP_BOTH = ROCKCHIP_DRM_FLIP_VERTICAL |
ROCKCHIP_DRM_FLIP_HORIZONTAL,
};
enum drm_rockchip_degree {
ROCKCHIP_DRM_DEGREE_0,
ROCKCHIP_DRM_DEGREE_90,
ROCKCHIP_DRM_DEGREE_180,
ROCKCHIP_DRM_DEGREE_270,
};
enum drm_rockchip_planer {
ROCKCHIP_DRM_PLANAR_Y,
ROCKCHIP_DRM_PLANAR_CB,
ROCKCHIP_DRM_PLANAR_CR,
ROCKCHIP_DRM_PLANAR_MAX,
};
/**
* A structure for ipp supported property list.
*
* @version: version of this structure.
* @ipp_id: id of ipp driver.
* @count: count of ipp driver.
* @writeback: flag of writeback supporting.
* @flip: flag of flip supporting.
* @degree: flag of degree information.
* @csc: flag of csc supporting.
* @crop: flag of crop supporting.
* @scale: flag of scale supporting.
* @refresh_min: min hz of refresh.
* @refresh_max: max hz of refresh.
* @crop_min: crop min resolution.
* @crop_max: crop max resolution.
* @scale_min: scale min resolution.
* @scale_max: scale max resolution.
*/
struct drm_rockchip_ipp_prop_list {
__u32 version;
__u32 ipp_id;
__u32 count;
__u32 writeback;
__u32 flip;
__u32 degree;
__u32 csc;
__u32 crop;
__u32 scale;
__u32 refresh_min;
__u32 refresh_max;
__u32 reserved;
struct drm_rockchip_sz crop_min;
struct drm_rockchip_sz crop_max;
struct drm_rockchip_sz scale_min;
struct drm_rockchip_sz scale_max;
};
/**
* A structure for ipp config.
*
* @ops_id: property of operation directions.
* @flip: property of mirror, flip.
* @degree: property of rotation degree.
* @fmt: property of image format.
* @sz: property of image size.
* @pos: property of image position(src-cropped,dst-scaler).
*/
struct drm_rockchip_ipp_config {
enum drm_rockchip_ops_id ops_id;
enum drm_rockchip_flip flip;
enum drm_rockchip_degree degree;
__u32 fmt;
struct drm_rockchip_sz sz;
struct drm_rockchip_pos pos;
};
enum drm_rockchip_ipp_cmd {
IPP_CMD_NONE,
IPP_CMD_M2M,
IPP_CMD_WB,
IPP_CMD_OUTPUT,
IPP_CMD_MAX,
};
/**
* A structure for ipp property.
*
* @config: source, destination config.
* @cmd: definition of command.
* @ipp_id: id of ipp driver.
* @prop_id: id of property.
* @refresh_rate: refresh rate.
*/
struct drm_rockchip_ipp_property {
struct drm_rockchip_ipp_config config[ROCKCHIP_DRM_OPS_MAX];
enum drm_rockchip_ipp_cmd cmd;
__u32 ipp_id;
__u32 prop_id;
__u32 refresh_rate;
};
enum drm_rockchip_ipp_buf_type {
IPP_BUF_ENQUEUE,
IPP_BUF_DEQUEUE,
};
/**
* A structure for ipp buffer operations.
*
* @ops_id: operation directions.
* @buf_type: definition of buffer.
* @prop_id: id of property.
* @buf_id: id of buffer.
* @handle: Y, Cb, Cr each planar handle.
* @user_data: user data.
*/
struct drm_rockchip_ipp_queue_buf {
enum drm_rockchip_ops_id ops_id;
enum drm_rockchip_ipp_buf_type buf_type;
__u32 prop_id;
__u32 buf_id;
__u32 handle[ROCKCHIP_DRM_PLANAR_MAX];
__u32 reserved;
__u64 user_data;
};
enum drm_rockchip_ipp_ctrl {
IPP_CTRL_PLAY,
IPP_CTRL_STOP,
IPP_CTRL_PAUSE,
IPP_CTRL_RESUME,
IPP_CTRL_MAX,
};
/**
* A structure for ipp start/stop operations.
*
* @prop_id: id of property.
* @ctrl: definition of control.
*/
struct drm_rockchip_ipp_cmd_ctrl {
__u32 prop_id;
enum drm_rockchip_ipp_ctrl ctrl;
};
#define DRM_ROCKCHIP_GEM_CREATE 0x00
#define DRM_ROCKCHIP_GEM_MAP_OFFSET 0x01
#define DRM_ROCKCHIP_GEM_MMAP 0x02
/* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */
#define DRM_ROCKCHIP_GEM_GET 0x04
#define DRM_ROCKCHIP_VIDI_CONNECTION 0x07
/* G2D */
#define DRM_ROCKCHIP_G2D_GET_VER 0x20
#define DRM_ROCKCHIP_G2D_SET_CMDLIST 0x21
#define DRM_ROCKCHIP_G2D_EXEC 0x22
/* IPP - Image Post Processing */
#define DRM_ROCKCHIP_IPP_GET_PROPERTY 0x30
#define DRM_ROCKCHIP_IPP_SET_PROPERTY 0x31
#define DRM_ROCKCHIP_IPP_QUEUE_BUF 0x32
#define DRM_ROCKCHIP_IPP_CMD_CTRL 0x33
#define DRM_IOCTL_ROCKCHIP_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create)
#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off)
#define DRM_IOCTL_ROCKCHIP_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_GEM_MMAP, struct drm_rockchip_gem_mmap)
#define DRM_IOCTL_ROCKCHIP_GEM_GET DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_GEM_GET, struct drm_rockchip_gem_info)
#define DRM_IOCTL_ROCKCHIP_VIDI_CONNECTION DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_VIDI_CONNECTION, struct drm_rockchip_vidi_connection)
#define DRM_IOCTL_ROCKCHIP_G2D_GET_VER DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_G2D_GET_VER, struct drm_rockchip_g2d_get_ver)
#define DRM_IOCTL_ROCKCHIP_G2D_SET_CMDLIST DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_G2D_SET_CMDLIST, struct drm_rockchip_g2d_set_cmdlist)
#define DRM_IOCTL_ROCKCHIP_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_G2D_EXEC, struct drm_rockchip_g2d_exec)
#define DRM_IOCTL_ROCKCHIP_IPP_GET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_IPP_GET_PROPERTY, struct drm_rockchip_ipp_prop_list)
#define DRM_IOCTL_ROCKCHIP_IPP_SET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_IPP_SET_PROPERTY, struct drm_rockchip_ipp_property)
#define DRM_IOCTL_ROCKCHIP_IPP_QUEUE_BUF DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_IPP_QUEUE_BUF, struct drm_rockchip_ipp_queue_buf)
#define DRM_IOCTL_ROCKCHIP_IPP_CMD_CTRL DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_IPP_CMD_CTRL, struct drm_rockchip_ipp_cmd_ctrl)
/* ROCKCHIP specific events */
#define DRM_ROCKCHIP_G2D_EVENT 0x80000000
#define DRM_ROCKCHIP_IPP_EVENT 0x80000001
struct drm_rockchip_g2d_event {
struct drm_event base;
__u64 user_data;
__u32 tv_sec;
__u32 tv_usec;
__u32 cmdlist_no;
__u32 reserved;
};
struct drm_rockchip_ipp_event {
struct drm_event base;
__u64 user_data;
__u32 tv_sec;
__u32 tv_usec;
__u32 prop_id;
__u32 reserved;
__u32 buf_id[ROCKCHIP_DRM_OPS_MAX];
};
#endif /* _UAPI_ROCKCHIP_DRM_H_ */

View File

@ -75,7 +75,7 @@ struct display_timing {
struct timing_entry vsync_len; /* ver. sync len */
enum display_flags flags; /* display flags */
#if defined(CONFIG_FB_ROCKCHIP)
#if defined(CONFIG_FB_ROCKCHIP) || defined(CONFIG_DRM_ROCKCHIP)
u16 screen_type; /*screen type*/
u16 lvds_format; /*lvds data format for lvds screen*/
u16 face; /*display output interface format:24bit 18bit 16bit*/