diff options
Diffstat (limited to 'drivers/gpu/drm/msm')
58 files changed, 7745 insertions, 1252 deletions
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index a06c19cc56f..f1238896785 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -2,8 +2,8 @@  config DRM_MSM  	tristate "MSM DRM"  	depends on DRM -	depends on ARCH_MSM -	depends on ARCH_MSM8960 +	depends on MSM_IOMMU +	depends on ARCH_QCOM || (ARM && COMPILE_TEST)  	select DRM_KMS_HELPER  	select SHMEM  	select TMPFS @@ -14,6 +14,7 @@ config DRM_MSM  config DRM_MSM_FBDEV  	bool "Enable legacy fbdev support for MSM modesetting driver"  	depends on DRM_MSM +	select DRM_KMS_FB_HELPER  	select FB_SYS_FILLRECT  	select FB_SYS_COPYAREA  	select FB_SYS_IMAGEBLIT diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index e17914889e5..93ca49c8df4 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -7,22 +7,35 @@ msm-y := \  	adreno/adreno_gpu.o \  	adreno/a3xx_gpu.o \  	hdmi/hdmi.o \ +	hdmi/hdmi_audio.o \  	hdmi/hdmi_bridge.o \  	hdmi/hdmi_connector.o \  	hdmi/hdmi_i2c.o \  	hdmi/hdmi_phy_8960.o \  	hdmi/hdmi_phy_8x60.o \ -	mdp4/mdp4_crtc.o \ -	mdp4/mdp4_dtv_encoder.o \ -	mdp4/mdp4_format.o \ -	mdp4/mdp4_irq.o \ -	mdp4/mdp4_kms.o \ -	mdp4/mdp4_plane.o \ +	hdmi/hdmi_phy_8x74.o \ +	mdp/mdp_format.o \ +	mdp/mdp_kms.o \ +	mdp/mdp4/mdp4_crtc.o \ +	mdp/mdp4/mdp4_dtv_encoder.o \ +	mdp/mdp4/mdp4_irq.o \ +	mdp/mdp4/mdp4_kms.o \ +	mdp/mdp4/mdp4_plane.o \ +	mdp/mdp5/mdp5_crtc.o \ +	mdp/mdp5/mdp5_encoder.o \ +	mdp/mdp5/mdp5_irq.o \ +	mdp/mdp5/mdp5_kms.o \ +	mdp/mdp5/mdp5_plane.o \ +	mdp/mdp5/mdp5_smp.o \  	msm_drv.o \  	msm_fb.o \  	msm_gem.o \ +	msm_gem_prime.o \  	msm_gem_submit.o \  	msm_gpu.o \ +	msm_iommu.o \ +	msm_perf.o \ +	msm_rd.o \  	msm_ringbuffer.o  msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o diff --git a/drivers/gpu/drm/msm/NOTES b/drivers/gpu/drm/msm/NOTES index e036f6c1db9..9c4255b9802 100644 --- a/drivers/gpu/drm/msm/NOTES +++ b/drivers/gpu/drm/msm/NOTES @@ -4,7 +4,7 @@ In the current snapdragon SoC's, we have (at least) 3 different  display controller blocks at play:   + MDP3 - ?? seems to be what is on geeksphone peak device   + MDP4 - S3 (APQ8060, touchpad), S4-pro (APQ8064, nexus4 & ifc6410) - + MDSS - snapdragon 800 + + MDP5 - snapdragon 800  (I don't have a completely clear picture on which display controller  maps to which part #) @@ -46,6 +46,24 @@ and treat the MDP4 block's irq as "the" irq.  Even though the connectors  may have their own irqs which they install themselves.  For this reason  the display controller is the "master" device. +For MDP5, the mapping is: + +  plane   -> PIPE{RGBn,VIGn}             \ +  crtc    -> LM (layer mixer)            |-> MDP "device" +  encoder -> INTF                        / +  connector -> HDMI/DSI/eDP/etc          --> other device(s) + +Unlike MDP4, it appears we can get by with a single encoder, rather +than needing a different implementation for DTV, DSI, etc.  (Ie. the +register interface is same, just different bases.) + +Also unlike MDP4, with MDP5 all the IRQs for other blocks (HDMI, DSI, +etc) are routed through MDP. + +And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from +which blocks need to be allocated to the active pipes based on fetch +stride. +  Each connector probably ends up being a separate device, just for the  logistics of finding/mapping io region, irq, etc.  Idealy we would  have a better way than just stashing the platform device in a global diff --git a/drivers/gpu/drm/msm/adreno/a2xx.xml.h b/drivers/gpu/drm/msm/adreno/a2xx.xml.h index 35463864b95..85d615e7d62 100644 --- a/drivers/gpu/drm/msm/adreno/a2xx.xml.h +++ b/drivers/gpu/drm/msm/adreno/a2xx.xml.h @@ -4,16 +4,17 @@  /* Autogenerated file, DO NOT EDIT manually!  This file was generated by the rules-ng-ng headergen tool in this git repository: -http://0x04.net/cgit/index.cgi/rules-ng-ng -git clone git://0x04.net/rules-ng-ng +http://github.com/freedreno/envytools/ +git clone https://github.com/freedreno/envytools.git  The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml              (    327 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml           (  30005 bytes, from 2013-07-19 21:30:48) -- /home/robclark/src/freedreno/envytools/rnndb/adreno_common.xml       (   8983 bytes, from 2013-07-24 01:38:36) -- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml          (   9712 bytes, from 2013-05-26 15:22:37) -- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml           (  51415 bytes, from 2013-08-03 14:26:05) +- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml               (    364 bytes, from 2013-11-30 14:47:15) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml  (   1453 bytes, from 2013-03-31 16:51:27) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml          (  32814 bytes, from 2013-11-30 15:07:33) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml (   8900 bytes, from 2013-10-22 23:57:49) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml    (  10574 bytes, from 2013-11-13 05:44:45) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml          (  53644 bytes, from 2013-11-30 15:07:33) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml          (   8344 bytes, from 2013-11-30 14:49:47)  Copyright (C) 2013 by the following authors:  - Rob Clark <robdclark@gmail.com> (robclark) @@ -202,6 +203,12 @@ enum a2xx_rb_copy_sample_select {  	SAMPLE_0123 = 6,  }; +enum adreno_mmu_clnt_beh { +	BEH_NEVR = 0, +	BEH_TRAN_RNG = 1, +	BEH_TRAN_FLT = 2, +}; +  enum sq_tex_clamp {  	SQ_TEX_WRAP = 0,  	SQ_TEX_MIRROR = 1, @@ -238,6 +245,92 @@ enum sq_tex_filter {  #define REG_A2XX_CP_PFP_UCODE_DATA				0x000000c1 +#define REG_A2XX_MH_MMU_CONFIG					0x00000040 +#define A2XX_MH_MMU_CONFIG_MMU_ENABLE				0x00000001 +#define A2XX_MH_MMU_CONFIG_SPLIT_MODE_ENABLE			0x00000002 +#define A2XX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR__MASK		0x00000030 +#define A2XX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR__SHIFT		4 +static inline uint32_t A2XX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) +{ +	return ((val) << A2XX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR__SHIFT) & A2XX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR__MASK; +} +#define A2XX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR__MASK		0x000000c0 +#define A2XX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR__SHIFT		6 +static inline uint32_t A2XX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) +{ +	return ((val) << A2XX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR__SHIFT) & A2XX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR__MASK; +} +#define A2XX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR__MASK		0x00000300 +#define A2XX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR__SHIFT		8 +static inline uint32_t A2XX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) +{ +	return ((val) << A2XX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR__SHIFT) & A2XX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR__MASK; +} +#define A2XX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR__MASK		0x00000c00 +#define A2XX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR__SHIFT		10 +static inline uint32_t A2XX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) +{ +	return ((val) << A2XX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR__SHIFT) & A2XX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR__MASK; +} +#define A2XX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR__MASK		0x00003000 +#define A2XX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR__SHIFT		12 +static inline uint32_t A2XX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) +{ +	return ((val) << A2XX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR__SHIFT) & A2XX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR__MASK; +} +#define A2XX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR__MASK		0x0000c000 +#define A2XX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR__SHIFT		14 +static inline uint32_t A2XX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) +{ +	return ((val) << A2XX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR__SHIFT) & A2XX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR__MASK; +} +#define A2XX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR__MASK		0x00030000 +#define A2XX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR__SHIFT		16 +static inline uint32_t A2XX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) +{ +	return ((val) << A2XX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR__SHIFT) & A2XX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR__MASK; +} +#define A2XX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR__MASK		0x000c0000 +#define A2XX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR__SHIFT		18 +static inline uint32_t A2XX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) +{ +	return ((val) << A2XX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR__SHIFT) & A2XX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR__MASK; +} +#define A2XX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR__MASK		0x00300000 +#define A2XX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR__SHIFT		20 +static inline uint32_t A2XX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) +{ +	return ((val) << A2XX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR__SHIFT) & A2XX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR__MASK; +} +#define A2XX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR__MASK		0x00c00000 +#define A2XX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR__SHIFT		22 +static inline uint32_t A2XX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) +{ +	return ((val) << A2XX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR__SHIFT) & A2XX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR__MASK; +} +#define A2XX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR__MASK		0x03000000 +#define A2XX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR__SHIFT		24 +static inline uint32_t A2XX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) +{ +	return ((val) << A2XX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR__SHIFT) & A2XX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR__MASK; +} + +#define REG_A2XX_MH_MMU_VA_RANGE				0x00000041 + +#define REG_A2XX_MH_MMU_PT_BASE					0x00000042 + +#define REG_A2XX_MH_MMU_PAGE_FAULT				0x00000043 + +#define REG_A2XX_MH_MMU_TRAN_ERROR				0x00000044 + +#define REG_A2XX_MH_MMU_INVALIDATE				0x00000045 + +#define REG_A2XX_MH_MMU_MPU_BASE				0x00000046 + +#define REG_A2XX_MH_MMU_MPU_END					0x00000047 + +#define REG_A2XX_NQWAIT_UNTIL					0x00000394 +  #define REG_A2XX_RBBM_PERFCOUNTER1_SELECT			0x00000395  #define REG_A2XX_RBBM_PERFCOUNTER1_LO				0x00000397 @@ -276,20 +369,6 @@ enum sq_tex_filter {  #define REG_A2XX_CP_PERFCOUNTER_HI				0x00000447 -#define REG_A2XX_CP_ST_BASE					0x0000044d - -#define REG_A2XX_CP_ST_BUFSZ					0x0000044e - -#define REG_A2XX_CP_IB1_BASE					0x00000458 - -#define REG_A2XX_CP_IB1_BUFSZ					0x00000459 - -#define REG_A2XX_CP_IB2_BASE					0x0000045a - -#define REG_A2XX_CP_IB2_BUFSZ					0x0000045b - -#define REG_A2XX_CP_STAT					0x0000047f -  #define REG_A2XX_RBBM_STATUS					0x000005d0  #define A2XX_RBBM_STATUS_CMDFIFO_AVAIL__MASK			0x0000001f  #define A2XX_RBBM_STATUS_CMDFIFO_AVAIL__SHIFT			0 @@ -317,6 +396,38 @@ static inline uint32_t A2XX_RBBM_STATUS_CMDFIFO_AVAIL(uint32_t val)  #define A2XX_RBBM_STATUS_RB_CNTX_BUSY				0x40000000  #define A2XX_RBBM_STATUS_GUI_ACTIVE				0x80000000 +#define REG_A2XX_MH_ARBITER_CONFIG				0x00000a40 +#define A2XX_MH_ARBITER_CONFIG_SAME_PAGE_LIMIT__MASK		0x0000003f +#define A2XX_MH_ARBITER_CONFIG_SAME_PAGE_LIMIT__SHIFT		0 +static inline uint32_t A2XX_MH_ARBITER_CONFIG_SAME_PAGE_LIMIT(uint32_t val) +{ +	return ((val) << A2XX_MH_ARBITER_CONFIG_SAME_PAGE_LIMIT__SHIFT) & A2XX_MH_ARBITER_CONFIG_SAME_PAGE_LIMIT__MASK; +} +#define A2XX_MH_ARBITER_CONFIG_SAME_PAGE_GRANULARITY		0x00000040 +#define A2XX_MH_ARBITER_CONFIG_L1_ARB_ENABLE			0x00000080 +#define A2XX_MH_ARBITER_CONFIG_L1_ARB_HOLD_ENABLE		0x00000100 +#define A2XX_MH_ARBITER_CONFIG_L2_ARB_CONTROL			0x00000200 +#define A2XX_MH_ARBITER_CONFIG_PAGE_SIZE__MASK			0x00001c00 +#define A2XX_MH_ARBITER_CONFIG_PAGE_SIZE__SHIFT			10 +static inline uint32_t A2XX_MH_ARBITER_CONFIG_PAGE_SIZE(uint32_t val) +{ +	return ((val) << A2XX_MH_ARBITER_CONFIG_PAGE_SIZE__SHIFT) & A2XX_MH_ARBITER_CONFIG_PAGE_SIZE__MASK; +} +#define A2XX_MH_ARBITER_CONFIG_TC_REORDER_ENABLE		0x00002000 +#define A2XX_MH_ARBITER_CONFIG_TC_ARB_HOLD_ENABLE		0x00004000 +#define A2XX_MH_ARBITER_CONFIG_IN_FLIGHT_LIMIT_ENABLE		0x00008000 +#define A2XX_MH_ARBITER_CONFIG_IN_FLIGHT_LIMIT__MASK		0x003f0000 +#define A2XX_MH_ARBITER_CONFIG_IN_FLIGHT_LIMIT__SHIFT		16 +static inline uint32_t A2XX_MH_ARBITER_CONFIG_IN_FLIGHT_LIMIT(uint32_t val) +{ +	return ((val) << A2XX_MH_ARBITER_CONFIG_IN_FLIGHT_LIMIT__SHIFT) & A2XX_MH_ARBITER_CONFIG_IN_FLIGHT_LIMIT__MASK; +} +#define A2XX_MH_ARBITER_CONFIG_CP_CLNT_ENABLE			0x00400000 +#define A2XX_MH_ARBITER_CONFIG_VGT_CLNT_ENABLE			0x00800000 +#define A2XX_MH_ARBITER_CONFIG_TC_CLNT_ENABLE			0x01000000 +#define A2XX_MH_ARBITER_CONFIG_RB_CLNT_ENABLE			0x02000000 +#define A2XX_MH_ARBITER_CONFIG_PA_CLNT_ENABLE			0x04000000 +  #define REG_A2XX_A220_VSC_BIN_SIZE				0x00000c01  #define A2XX_A220_VSC_BIN_SIZE_WIDTH__MASK			0x0000001f  #define A2XX_A220_VSC_BIN_SIZE_WIDTH__SHIFT			0 @@ -776,6 +887,12 @@ static inline uint32_t A2XX_SQ_CONTEXT_MISC_PARAM_GEN_POS(uint32_t val)  #define REG_A2XX_SQ_VS_PROGRAM					0x000021f7 +#define REG_A2XX_VGT_EVENT_INITIATOR				0x000021f9 + +#define REG_A2XX_VGT_DRAW_INITIATOR				0x000021fc + +#define REG_A2XX_VGT_IMMED_DATA					0x000021fd +  #define REG_A2XX_RB_DEPTHCONTROL				0x00002200  #define A2XX_RB_DEPTHCONTROL_STENCIL_ENABLE			0x00000001  #define A2XX_RB_DEPTHCONTROL_Z_ENABLE				0x00000002 diff --git a/drivers/gpu/drm/msm/adreno/a3xx.xml.h b/drivers/gpu/drm/msm/adreno/a3xx.xml.h index d183516067b..a7be56163d2 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx.xml.h +++ b/drivers/gpu/drm/msm/adreno/a3xx.xml.h @@ -4,16 +4,17 @@  /* Autogenerated file, DO NOT EDIT manually!  This file was generated by the rules-ng-ng headergen tool in this git repository: -http://0x04.net/cgit/index.cgi/rules-ng-ng -git clone git://0x04.net/rules-ng-ng +http://github.com/freedreno/envytools/ +git clone https://github.com/freedreno/envytools.git  The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml              (    327 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml           (  30005 bytes, from 2013-07-19 21:30:48) -- /home/robclark/src/freedreno/envytools/rnndb/adreno_common.xml       (   8983 bytes, from 2013-07-24 01:38:36) -- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml          (   9712 bytes, from 2013-05-26 15:22:37) -- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml           (  51415 bytes, from 2013-08-03 14:26:05) +- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml               (    364 bytes, from 2013-11-30 14:47:15) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml  (   1453 bytes, from 2013-03-31 16:51:27) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml          (  32814 bytes, from 2013-11-30 15:07:33) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml (   8900 bytes, from 2013-10-22 23:57:49) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml    (  10574 bytes, from 2013-11-13 05:44:45) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml          (  53644 bytes, from 2013-11-30 15:07:33) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml          (   8344 bytes, from 2013-11-30 14:49:47)  Copyright (C) 2013 by the following authors:  - Rob Clark <robdclark@gmail.com> (robclark) @@ -292,6 +293,8 @@ enum a3xx_tex_type {  #define A3XX_RBBM_STATUS_GPU_BUSY_NOHC				0x40000000  #define A3XX_RBBM_STATUS_GPU_BUSY				0x80000000 +#define REG_A3XX_RBBM_NQWAIT_UNTIL				0x00000040 +  #define REG_A3XX_RBBM_WAIT_IDLE_CLOCKS_CTL			0x00000033  #define REG_A3XX_RBBM_INTERFACE_HANG_INT_CTL			0x00000050 @@ -304,6 +307,8 @@ enum a3xx_tex_type {  #define REG_A3XX_RBBM_INTERFACE_HANG_MASK_CTL3			0x0000005a +#define REG_A3XX_RBBM_INT_SET_CMD				0x00000060 +  #define REG_A3XX_RBBM_INT_CLEAR_CMD				0x00000061  #define REG_A3XX_RBBM_INT_0_MASK				0x00000063 @@ -637,11 +642,12 @@ static inline uint32_t A3XX_GRAS_SU_POLY_OFFSET_OFFSET(float val)  #define REG_A3XX_GRAS_SU_MODE_CONTROL				0x00002070  #define A3XX_GRAS_SU_MODE_CONTROL_CULL_FRONT			0x00000001  #define A3XX_GRAS_SU_MODE_CONTROL_CULL_BACK			0x00000002 -#define A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__MASK		0x000007fc -#define A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__SHIFT		2 -static inline uint32_t A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH(uint32_t val) +#define A3XX_GRAS_SU_MODE_CONTROL_FRONT_CW			0x00000004 +#define A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__MASK		0x000007f8 +#define A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__SHIFT		3 +static inline uint32_t A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH(float val)  { -	return ((val) << A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__SHIFT) & A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__MASK; +	return ((((uint32_t)(val * 4.0))) << A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__SHIFT) & A3XX_GRAS_SU_MODE_CONTROL_LINEHALFWIDTH__MASK;  }  #define A3XX_GRAS_SU_MODE_CONTROL_POLY_OFFSET			0x00000800 @@ -745,6 +751,7 @@ static inline uint32_t A3XX_RB_RENDER_CONTROL_BIN_WIDTH(uint32_t val)  }  #define A3XX_RB_RENDER_CONTROL_DISABLE_COLOR_PIPE		0x00001000  #define A3XX_RB_RENDER_CONTROL_ENABLE_GMEM			0x00002000 +#define A3XX_RB_RENDER_CONTROL_ALPHA_TEST			0x00400000  #define A3XX_RB_RENDER_CONTROL_ALPHA_TEST_FUNC__MASK		0x07000000  #define A3XX_RB_RENDER_CONTROL_ALPHA_TEST_FUNC__SHIFT		24  static inline uint32_t A3XX_RB_RENDER_CONTROL_ALPHA_TEST_FUNC(enum adreno_compare_func val) @@ -767,7 +774,19 @@ static inline uint32_t A3XX_RB_MSAA_CONTROL_SAMPLE_MASK(uint32_t val)  	return ((val) << A3XX_RB_MSAA_CONTROL_SAMPLE_MASK__SHIFT) & A3XX_RB_MSAA_CONTROL_SAMPLE_MASK__MASK;  } -#define REG_A3XX_UNKNOWN_20C3					0x000020c3 +#define REG_A3XX_RB_ALPHA_REF					0x000020c3 +#define A3XX_RB_ALPHA_REF_UINT__MASK				0x0000ff00 +#define A3XX_RB_ALPHA_REF_UINT__SHIFT				8 +static inline uint32_t A3XX_RB_ALPHA_REF_UINT(uint32_t val) +{ +	return ((val) << A3XX_RB_ALPHA_REF_UINT__SHIFT) & A3XX_RB_ALPHA_REF_UINT__MASK; +} +#define A3XX_RB_ALPHA_REF_FLOAT__MASK				0xffff0000 +#define A3XX_RB_ALPHA_REF_FLOAT__SHIFT				16 +static inline uint32_t A3XX_RB_ALPHA_REF_FLOAT(float val) +{ +	return ((util_float_to_half(val)) << A3XX_RB_ALPHA_REF_FLOAT__SHIFT) & A3XX_RB_ALPHA_REF_FLOAT__MASK; +}  static inline uint32_t REG_A3XX_RB_MRT(uint32_t i0) { return 0x000020c4 + 0x4*i0; } @@ -923,13 +942,13 @@ static inline uint32_t A3XX_RB_BLEND_ALPHA_FLOAT(float val)  	return ((util_float_to_half(val)) << A3XX_RB_BLEND_ALPHA_FLOAT__SHIFT) & A3XX_RB_BLEND_ALPHA_FLOAT__MASK;  } -#define REG_A3XX_UNKNOWN_20E8					0x000020e8 +#define REG_A3XX_RB_CLEAR_COLOR_DW0				0x000020e8 -#define REG_A3XX_UNKNOWN_20E9					0x000020e9 +#define REG_A3XX_RB_CLEAR_COLOR_DW1				0x000020e9 -#define REG_A3XX_UNKNOWN_20EA					0x000020ea +#define REG_A3XX_RB_CLEAR_COLOR_DW2				0x000020ea -#define REG_A3XX_UNKNOWN_20EB					0x000020eb +#define REG_A3XX_RB_CLEAR_COLOR_DW3				0x000020eb  #define REG_A3XX_RB_COPY_CONTROL				0x000020ec  #define A3XX_RB_COPY_CONTROL_MSAA_RESOLVE__MASK			0x00000003 @@ -1002,7 +1021,7 @@ static inline uint32_t A3XX_RB_COPY_DEST_INFO_ENDIAN(enum adreno_rb_surface_endi  #define REG_A3XX_RB_DEPTH_CONTROL				0x00002100  #define A3XX_RB_DEPTH_CONTROL_Z_ENABLE				0x00000002  #define A3XX_RB_DEPTH_CONTROL_Z_WRITE_ENABLE			0x00000004 -#define A3XX_RB_DEPTH_CONTROL_EARLY_Z_ENABLE			0x00000008 +#define A3XX_RB_DEPTH_CONTROL_EARLY_Z_DISABLE			0x00000008  #define A3XX_RB_DEPTH_CONTROL_ZFUNC__MASK			0x00000070  #define A3XX_RB_DEPTH_CONTROL_ZFUNC__SHIFT			4  static inline uint32_t A3XX_RB_DEPTH_CONTROL_ZFUNC(enum adreno_compare_func val) @@ -1012,7 +1031,7 @@ static inline uint32_t A3XX_RB_DEPTH_CONTROL_ZFUNC(enum adreno_compare_func val)  #define A3XX_RB_DEPTH_CONTROL_BF_ENABLE				0x00000080  #define A3XX_RB_DEPTH_CONTROL_Z_TEST_ENABLE			0x80000000 -#define REG_A3XX_UNKNOWN_2101					0x00002101 +#define REG_A3XX_RB_DEPTH_CLEAR					0x00002101  #define REG_A3XX_RB_DEPTH_INFO					0x00002102  #define A3XX_RB_DEPTH_INFO_DEPTH_FORMAT__MASK			0x00000001 @@ -1038,7 +1057,8 @@ static inline uint32_t A3XX_RB_DEPTH_PITCH(uint32_t val)  #define REG_A3XX_RB_STENCIL_CONTROL				0x00002104  #define A3XX_RB_STENCIL_CONTROL_STENCIL_ENABLE			0x00000001 -#define A3XX_RB_STENCIL_CONTROL_STENCIL_ENABLE_BF		0x00000004 +#define A3XX_RB_STENCIL_CONTROL_STENCIL_ENABLE_BF		0x00000002 +#define A3XX_RB_STENCIL_CONTROL_STENCIL_READ			0x00000004  #define A3XX_RB_STENCIL_CONTROL_FUNC__MASK			0x00000700  #define A3XX_RB_STENCIL_CONTROL_FUNC__SHIFT			8  static inline uint32_t A3XX_RB_STENCIL_CONTROL_FUNC(enum adreno_compare_func val) @@ -1088,11 +1108,11 @@ static inline uint32_t A3XX_RB_STENCIL_CONTROL_ZFAIL_BF(enum adreno_stencil_op v  	return ((val) << A3XX_RB_STENCIL_CONTROL_ZFAIL_BF__SHIFT) & A3XX_RB_STENCIL_CONTROL_ZFAIL_BF__MASK;  } -#define REG_A3XX_UNKNOWN_2105					0x00002105 +#define REG_A3XX_RB_STENCIL_CLEAR				0x00002105 -#define REG_A3XX_UNKNOWN_2106					0x00002106 +#define REG_A3XX_RB_STENCIL_BUF_INFO				0x00002106 -#define REG_A3XX_UNKNOWN_2107					0x00002107 +#define REG_A3XX_RB_STENCIL_BUF_PITCH				0x00002107  #define REG_A3XX_RB_STENCILREFMASK				0x00002108  #define A3XX_RB_STENCILREFMASK_STENCILREF__MASK			0x000000ff @@ -1134,20 +1154,31 @@ static inline uint32_t A3XX_RB_STENCILREFMASK_BF_STENCILWRITEMASK(uint32_t val)  	return ((val) << A3XX_RB_STENCILREFMASK_BF_STENCILWRITEMASK__SHIFT) & A3XX_RB_STENCILREFMASK_BF_STENCILWRITEMASK__MASK;  } -#define REG_A3XX_PA_SC_WINDOW_OFFSET				0x0000210e -#define A3XX_PA_SC_WINDOW_OFFSET_X__MASK			0x0000ffff -#define A3XX_PA_SC_WINDOW_OFFSET_X__SHIFT			0 -static inline uint32_t A3XX_PA_SC_WINDOW_OFFSET_X(uint32_t val) +#define REG_A3XX_RB_LRZ_VSC_CONTROL				0x0000210c +#define A3XX_RB_LRZ_VSC_CONTROL_BINNING_ENABLE			0x00000002 + +#define REG_A3XX_RB_WINDOW_OFFSET				0x0000210e +#define A3XX_RB_WINDOW_OFFSET_X__MASK				0x0000ffff +#define A3XX_RB_WINDOW_OFFSET_X__SHIFT				0 +static inline uint32_t A3XX_RB_WINDOW_OFFSET_X(uint32_t val)  { -	return ((val) << A3XX_PA_SC_WINDOW_OFFSET_X__SHIFT) & A3XX_PA_SC_WINDOW_OFFSET_X__MASK; +	return ((val) << A3XX_RB_WINDOW_OFFSET_X__SHIFT) & A3XX_RB_WINDOW_OFFSET_X__MASK;  } -#define A3XX_PA_SC_WINDOW_OFFSET_Y__MASK			0xffff0000 -#define A3XX_PA_SC_WINDOW_OFFSET_Y__SHIFT			16 -static inline uint32_t A3XX_PA_SC_WINDOW_OFFSET_Y(uint32_t val) +#define A3XX_RB_WINDOW_OFFSET_Y__MASK				0xffff0000 +#define A3XX_RB_WINDOW_OFFSET_Y__SHIFT				16 +static inline uint32_t A3XX_RB_WINDOW_OFFSET_Y(uint32_t val)  { -	return ((val) << A3XX_PA_SC_WINDOW_OFFSET_Y__SHIFT) & A3XX_PA_SC_WINDOW_OFFSET_Y__MASK; +	return ((val) << A3XX_RB_WINDOW_OFFSET_Y__SHIFT) & A3XX_RB_WINDOW_OFFSET_Y__MASK;  } +#define REG_A3XX_RB_SAMPLE_COUNT_CONTROL			0x00002110 + +#define REG_A3XX_RB_SAMPLE_COUNT_ADDR				0x00002111 + +#define REG_A3XX_RB_Z_CLAMP_MIN					0x00002114 + +#define REG_A3XX_RB_Z_CLAMP_MAX					0x00002115 +  #define REG_A3XX_PC_VSTREAM_CONTROL				0x000021e4  #define REG_A3XX_PC_VERTEX_REUSE_BLOCK_CNTL			0x000021ea @@ -1294,6 +1325,8 @@ static inline uint32_t A3XX_HLSQ_CONST_FSPRESV_RANGE_REG_ENDENTRY(uint32_t val)  #define REG_A3XX_HLSQ_CL_KERNEL_GROUP_X_REG			0x00002215 +#define REG_A3XX_HLSQ_CL_KERNEL_GROUP_Y_REG			0x00002216 +  #define REG_A3XX_HLSQ_CL_KERNEL_GROUP_Z_REG			0x00002217  #define REG_A3XX_HLSQ_CL_WG_OFFSET_REG				0x0000221a @@ -1476,12 +1509,13 @@ static inline uint32_t REG_A3XX_VPC_VARYING_PS_REPL_MODE(uint32_t i0) { return 0  #define REG_A3XX_SP_SP_CTRL_REG					0x000022c0  #define A3XX_SP_SP_CTRL_REG_RESOLVE				0x00010000 -#define A3XX_SP_SP_CTRL_REG_CONSTMODE__MASK			0x000c0000 +#define A3XX_SP_SP_CTRL_REG_CONSTMODE__MASK			0x00040000  #define A3XX_SP_SP_CTRL_REG_CONSTMODE__SHIFT			18  static inline uint32_t A3XX_SP_SP_CTRL_REG_CONSTMODE(uint32_t val)  {  	return ((val) << A3XX_SP_SP_CTRL_REG_CONSTMODE__SHIFT) & A3XX_SP_SP_CTRL_REG_CONSTMODE__MASK;  } +#define A3XX_SP_SP_CTRL_REG_BINNING				0x00080000  #define A3XX_SP_SP_CTRL_REG_SLEEPMODE__MASK			0x00300000  #define A3XX_SP_SP_CTRL_REG_SLEEPMODE__SHIFT			20  static inline uint32_t A3XX_SP_SP_CTRL_REG_SLEEPMODE(uint32_t val) @@ -1654,7 +1688,7 @@ static inline uint32_t A3XX_SP_VS_OBJ_OFFSET_REG_SHADEROBJOFFSET(uint32_t val)  #define REG_A3XX_SP_VS_OBJ_START_REG				0x000022d5 -#define REG_A3XX_SP_VS_PVT_MEM_CTRL_REG				0x000022d6 +#define REG_A3XX_SP_VS_PVT_MEM_PARAM_REG			0x000022d6  #define REG_A3XX_SP_VS_PVT_MEM_ADDR_REG				0x000022d7 @@ -1757,7 +1791,7 @@ static inline uint32_t A3XX_SP_FS_OBJ_OFFSET_REG_SHADEROBJOFFSET(uint32_t val)  #define REG_A3XX_SP_FS_OBJ_START_REG				0x000022e3 -#define REG_A3XX_SP_FS_PVT_MEM_CTRL_REG				0x000022e4 +#define REG_A3XX_SP_FS_PVT_MEM_PARAM_REG			0x000022e4  #define REG_A3XX_SP_FS_PVT_MEM_ADDR_REG				0x000022e5 @@ -1928,6 +1962,9 @@ static inline uint32_t REG_A3XX_VSC_PIPE_DATA_ADDRESS(uint32_t i0) { return 0x00  static inline uint32_t REG_A3XX_VSC_PIPE_DATA_LENGTH(uint32_t i0) { return 0x00000c08 + 0x3*i0; } +#define REG_A3XX_VSC_BIN_CONTROL				0x00000c3c +#define A3XX_VSC_BIN_CONTROL_BINNING_ENABLE			0x00000001 +  #define REG_A3XX_UNKNOWN_0C3D					0x00000c3d  #define REG_A3XX_PC_PERFCOUNTER0_SELECT				0x00000c48 @@ -1938,7 +1975,7 @@ static inline uint32_t REG_A3XX_VSC_PIPE_DATA_LENGTH(uint32_t i0) { return 0x000  #define REG_A3XX_PC_PERFCOUNTER3_SELECT				0x00000c4b -#define REG_A3XX_UNKNOWN_0C81					0x00000c81 +#define REG_A3XX_GRAS_TSE_DEBUG_ECO				0x00000c81  #define REG_A3XX_GRAS_PERFCOUNTER0_SELECT			0x00000c88 @@ -1960,22 +1997,24 @@ static inline uint32_t REG_A3XX_GRAS_CL_USER_PLANE_W(uint32_t i0) { return 0x000  #define REG_A3XX_RB_GMEM_BASE_ADDR				0x00000cc0 +#define REG_A3XX_RB_DEBUG_ECO_CONTROLS_ADDR			0x00000cc1 +  #define REG_A3XX_RB_PERFCOUNTER0_SELECT				0x00000cc6  #define REG_A3XX_RB_PERFCOUNTER1_SELECT				0x00000cc7 -#define REG_A3XX_RB_WINDOW_SIZE					0x00000ce0 -#define A3XX_RB_WINDOW_SIZE_WIDTH__MASK				0x00003fff -#define A3XX_RB_WINDOW_SIZE_WIDTH__SHIFT			0 -static inline uint32_t A3XX_RB_WINDOW_SIZE_WIDTH(uint32_t val) +#define REG_A3XX_RB_FRAME_BUFFER_DIMENSION			0x00000ce0 +#define A3XX_RB_FRAME_BUFFER_DIMENSION_WIDTH__MASK		0x00003fff +#define A3XX_RB_FRAME_BUFFER_DIMENSION_WIDTH__SHIFT		0 +static inline uint32_t A3XX_RB_FRAME_BUFFER_DIMENSION_WIDTH(uint32_t val)  { -	return ((val) << A3XX_RB_WINDOW_SIZE_WIDTH__SHIFT) & A3XX_RB_WINDOW_SIZE_WIDTH__MASK; +	return ((val) << A3XX_RB_FRAME_BUFFER_DIMENSION_WIDTH__SHIFT) & A3XX_RB_FRAME_BUFFER_DIMENSION_WIDTH__MASK;  } -#define A3XX_RB_WINDOW_SIZE_HEIGHT__MASK			0x0fffc000 -#define A3XX_RB_WINDOW_SIZE_HEIGHT__SHIFT			14 -static inline uint32_t A3XX_RB_WINDOW_SIZE_HEIGHT(uint32_t val) +#define A3XX_RB_FRAME_BUFFER_DIMENSION_HEIGHT__MASK		0x0fffc000 +#define A3XX_RB_FRAME_BUFFER_DIMENSION_HEIGHT__SHIFT		14 +static inline uint32_t A3XX_RB_FRAME_BUFFER_DIMENSION_HEIGHT(uint32_t val)  { -	return ((val) << A3XX_RB_WINDOW_SIZE_HEIGHT__SHIFT) & A3XX_RB_WINDOW_SIZE_HEIGHT__MASK; +	return ((val) << A3XX_RB_FRAME_BUFFER_DIMENSION_HEIGHT__SHIFT) & A3XX_RB_FRAME_BUFFER_DIMENSION_HEIGHT__MASK;  }  #define REG_A3XX_HLSQ_PERFCOUNTER0_SELECT			0x00000e00 @@ -2073,7 +2112,16 @@ static inline uint32_t A3XX_UCHE_CACHE_INVALIDATE1_REG_OPCODE(enum a3xx_cache_op  #define REG_A3XX_TP_PERFCOUNTER5_SELECT				0x00000f09 +#define REG_A3XX_VGT_CL_INITIATOR				0x000021f0 + +#define REG_A3XX_VGT_EVENT_INITIATOR				0x000021f9 + +#define REG_A3XX_VGT_DRAW_INITIATOR				0x000021fc + +#define REG_A3XX_VGT_IMMED_DATA					0x000021fd +  #define REG_A3XX_TEX_SAMP_0					0x00000000 +#define A3XX_TEX_SAMP_0_MIPFILTER_LINEAR			0x00000002  #define A3XX_TEX_SAMP_0_XY_MAG__MASK				0x0000000c  #define A3XX_TEX_SAMP_0_XY_MAG__SHIFT				2  static inline uint32_t A3XX_TEX_SAMP_0_XY_MAG(enum a3xx_tex_filter val) @@ -2107,6 +2155,18 @@ static inline uint32_t A3XX_TEX_SAMP_0_WRAP_R(enum a3xx_tex_clamp val)  #define A3XX_TEX_SAMP_0_UNNORM_COORDS				0x80000000  #define REG_A3XX_TEX_SAMP_1					0x00000001 +#define A3XX_TEX_SAMP_1_MAX_LOD__MASK				0x003ff000 +#define A3XX_TEX_SAMP_1_MAX_LOD__SHIFT				12 +static inline uint32_t A3XX_TEX_SAMP_1_MAX_LOD(float val) +{ +	return ((((uint32_t)(val * 12.0))) << A3XX_TEX_SAMP_1_MAX_LOD__SHIFT) & A3XX_TEX_SAMP_1_MAX_LOD__MASK; +} +#define A3XX_TEX_SAMP_1_MIN_LOD__MASK				0xffc00000 +#define A3XX_TEX_SAMP_1_MIN_LOD__SHIFT				22 +static inline uint32_t A3XX_TEX_SAMP_1_MIN_LOD(float val) +{ +	return ((((uint32_t)(val * 12.0))) << A3XX_TEX_SAMP_1_MIN_LOD__SHIFT) & A3XX_TEX_SAMP_1_MIN_LOD__MASK; +}  #define REG_A3XX_TEX_CONST_0					0x00000000  #define A3XX_TEX_CONST_0_TILED					0x00000001 @@ -2134,6 +2194,12 @@ static inline uint32_t A3XX_TEX_CONST_0_SWIZ_W(enum a3xx_tex_swiz val)  {  	return ((val) << A3XX_TEX_CONST_0_SWIZ_W__SHIFT) & A3XX_TEX_CONST_0_SWIZ_W__MASK;  } +#define A3XX_TEX_CONST_0_MIPLVLS__MASK				0x000f0000 +#define A3XX_TEX_CONST_0_MIPLVLS__SHIFT				16 +static inline uint32_t A3XX_TEX_CONST_0_MIPLVLS(uint32_t val) +{ +	return ((val) << A3XX_TEX_CONST_0_MIPLVLS__SHIFT) & A3XX_TEX_CONST_0_MIPLVLS__MASK; +}  #define A3XX_TEX_CONST_0_FMT__MASK				0x1fc00000  #define A3XX_TEX_CONST_0_FMT__SHIFT				22  static inline uint32_t A3XX_TEX_CONST_0_FMT(enum a3xx_tex_fmt val) diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index 035bd13dc8b..942e09d898a 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -15,6 +15,10 @@   * this program.  If not, see <http://www.gnu.org/licenses/>.   */ +#ifdef CONFIG_MSM_OCMEM +#  include <mach/ocmem.h> +#endif +  #include "a3xx_gpu.h"  #define A3XX_INT0_MASK \ @@ -31,7 +35,11 @@  	 A3XX_INT0_CP_AHB_ERROR_HALT |     \  	 A3XX_INT0_UCHE_OOB_ACCESS) -static struct platform_device *a3xx_pdev; + +static bool hang_debug = false; +MODULE_PARM_DESC(hang_debug, "Dump registers when hang is detected (can be slow!)"); +module_param_named(hang_debug, hang_debug, bool, 0600); +static void a3xx_dump(struct msm_gpu *gpu);  static void a3xx_me_init(struct msm_gpu *gpu)  { @@ -63,6 +71,7 @@ static void a3xx_me_init(struct msm_gpu *gpu)  static int a3xx_hw_init(struct msm_gpu *gpu)  {  	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); +	struct a3xx_gpu *a3xx_gpu = to_a3xx_gpu(adreno_gpu);  	uint32_t *ptr, len;  	int i, ret; @@ -105,6 +114,21 @@ static int a3xx_hw_init(struct msm_gpu *gpu)  		gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT, 0x000000ff);  		gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT_CONF, 0x000000a4); +	} else if (adreno_is_a330v2(adreno_gpu)) { +		/* +		 * Most of the VBIF registers on 8974v2 have the correct +		 * values at power on, so we won't modify those if we don't +		 * need to +		 */ +		/* Enable 1k sort: */ +		gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT, 0x0001003f); +		gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT_CONF, 0x000000a4); +		/* Enable WR-REQ: */ +		gpu_write(gpu, REG_A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x00003f); +		gpu_write(gpu, REG_A3XX_VBIF_DDR_OUT_MAX_BURST, 0x0000303); +		/* Set up VBIF_ROUND_ROBIN_QOS_ARB: */ +		gpu_write(gpu, REG_A3XX_VBIF_ROUND_ROBIN_QOS_ARB, 0x0003); +  	} else if (adreno_is_a330(adreno_gpu)) {  		/* Set up 16 deep read/write request queues: */  		gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF0, 0x18181818); @@ -121,10 +145,10 @@ static int a3xx_hw_init(struct msm_gpu *gpu)  		/* Set up VBIF_ROUND_ROBIN_QOS_ARB: */  		gpu_write(gpu, REG_A3XX_VBIF_ROUND_ROBIN_QOS_ARB, 0x0001);  		/* Set up AOOO: */ -		gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000ffff); -		gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO, 0xffffffff); +		gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000003f); +		gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO, 0x003f003f);  		/* Enable 1K sort: */ -		gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT, 0x0001ffff); +		gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT, 0x0001003f);  		gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT_CONF, 0x000000a4);  		/* Disable VBIF clock gating. This is to enable AXI running  		 * higher frequency than GPU: @@ -162,23 +186,32 @@ static int a3xx_hw_init(struct msm_gpu *gpu)  	gpu_write(gpu, REG_A3XX_UCHE_CACHE_MODE_CONTROL_REG, 0x00000001);  	/* Enable Clock gating: */ -	gpu_write(gpu, REG_A3XX_RBBM_CLOCK_CTL, 0xbfffffff); - -	/* Set the OCMEM base address for A330 */ -//TODO: -//	if (adreno_is_a330(adreno_gpu)) { -//		gpu_write(gpu, REG_A3XX_RB_GMEM_BASE_ADDR, -//			(unsigned int)(a3xx_gpu->ocmem_base >> 14)); -//	} +	if (adreno_is_a320(adreno_gpu)) +		gpu_write(gpu, REG_A3XX_RBBM_CLOCK_CTL, 0xbfffffff); +	else if (adreno_is_a330v2(adreno_gpu)) +		gpu_write(gpu, REG_A3XX_RBBM_CLOCK_CTL, 0xaaaaaaaa); +	else if (adreno_is_a330(adreno_gpu)) +		gpu_write(gpu, REG_A3XX_RBBM_CLOCK_CTL, 0xbffcffff); + +	if (adreno_is_a330v2(adreno_gpu)) +		gpu_write(gpu, REG_A3XX_RBBM_GPR0_CTL, 0x05515455); +	else if (adreno_is_a330(adreno_gpu)) +		gpu_write(gpu, REG_A3XX_RBBM_GPR0_CTL, 0x00000000); + +	/* Set the OCMEM base address for A330, etc */ +	if (a3xx_gpu->ocmem_hdl) { +		gpu_write(gpu, REG_A3XX_RB_GMEM_BASE_ADDR, +			(unsigned int)(a3xx_gpu->ocmem_base >> 14)); +	}  	/* Turn on performance counters: */  	gpu_write(gpu, REG_A3XX_RBBM_PERFCTR_CTL, 0x01); -	/* Set SP perfcounter 7 to count SP_FS_FULL_ALU_INSTRUCTIONS -	 * we will use this to augment our hang detection: -	 */ -	gpu_write(gpu, REG_A3XX_SP_PERFCOUNTER7_SELECT, -			SP_FS_FULL_ALU_INSTRUCTIONS); +	/* Enable the perfcntrs that we use.. */ +	for (i = 0; i < gpu->num_perfcntrs; i++) { +		const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i]; +		gpu_write(gpu, perfcntr->select_reg, perfcntr->select_val); +	}  	gpu_write(gpu, REG_A3XX_RBBM_INT_0_MASK, A3XX_INT0_MASK); @@ -219,7 +252,7 @@ static int a3xx_hw_init(struct msm_gpu *gpu)  	/* Load PM4: */  	ptr = (uint32_t *)(adreno_gpu->pm4->data);  	len = adreno_gpu->pm4->size / 4; -	DBG("loading PM4 ucode version: %u", ptr[0]); +	DBG("loading PM4 ucode version: %x", ptr[1]);  	gpu_write(gpu, REG_AXXX_CP_DEBUG,  			AXXX_CP_DEBUG_DYNAMIC_CLK_DISABLE | @@ -231,19 +264,26 @@ static int a3xx_hw_init(struct msm_gpu *gpu)  	/* Load PFP: */  	ptr = (uint32_t *)(adreno_gpu->pfp->data);  	len = adreno_gpu->pfp->size / 4; -	DBG("loading PFP ucode version: %u", ptr[0]); +	DBG("loading PFP ucode version: %x", ptr[5]);  	gpu_write(gpu, REG_A3XX_CP_PFP_UCODE_ADDR, 0);  	for (i = 1; i < len; i++)  		gpu_write(gpu, REG_A3XX_CP_PFP_UCODE_DATA, ptr[i]);  	/* CP ROQ queue sizes (bytes) - RB:16, ST:16, IB1:32, IB2:64 */ -	if (adreno_is_a305(adreno_gpu) || adreno_is_a320(adreno_gpu)) +	if (adreno_is_a305(adreno_gpu) || adreno_is_a320(adreno_gpu)) {  		gpu_write(gpu, REG_AXXX_CP_QUEUE_THRESHOLDS,  				AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB1_START(2) |  				AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB2_START(6) |  				AXXX_CP_QUEUE_THRESHOLDS_CSQ_ST_START(14)); - +	} else if (adreno_is_a330(adreno_gpu)) { +		/* NOTE: this (value take from downstream android driver) +		 * includes some bits outside of the known bitfields.  But +		 * A330 has this "MERCIU queue" thing too, which might +		 * explain a new bitfield or reshuffling: +		 */ +		gpu_write(gpu, REG_AXXX_CP_QUEUE_THRESHOLDS, 0x003e2008); +	}  	/* clear ME_HALT to start micro engine */  	gpu_write(gpu, REG_AXXX_CP_ME_CNTL, 0); @@ -253,6 +293,17 @@ static int a3xx_hw_init(struct msm_gpu *gpu)  	return 0;  } +static void a3xx_recover(struct msm_gpu *gpu) +{ +	/* dump registers before resetting gpu, if enabled: */ +	if (hang_debug) +		a3xx_dump(gpu); +	gpu_write(gpu, REG_A3XX_RBBM_SW_RESET_CMD, 1); +	gpu_read(gpu, REG_A3XX_RBBM_SW_RESET_CMD); +	gpu_write(gpu, REG_A3XX_RBBM_SW_RESET_CMD, 0); +	adreno_recover(gpu); +} +  static void a3xx_destroy(struct msm_gpu *gpu)  {  	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); @@ -261,27 +312,24 @@ static void a3xx_destroy(struct msm_gpu *gpu)  	DBG("%s", gpu->name);  	adreno_gpu_cleanup(adreno_gpu); -	put_device(&a3xx_gpu->pdev->dev); + +#ifdef CONFIG_MSM_OCMEM +	if (a3xx_gpu->ocmem_base) +		ocmem_free(OCMEM_GRAPHICS, a3xx_gpu->ocmem_hdl); +#endif +  	kfree(a3xx_gpu);  }  static void a3xx_idle(struct msm_gpu *gpu)  { -	unsigned long t; -  	/* wait for ringbuffer to drain: */  	adreno_idle(gpu); -	t = jiffies + ADRENO_IDLE_TIMEOUT; -  	/* then wait for GPU to finish: */ -	do { -		uint32_t rbbm_status = gpu_read(gpu, REG_A3XX_RBBM_STATUS); -		if (!(rbbm_status & A3XX_RBBM_STATUS_GPU_BUSY)) -			return; -	} while(time_before(jiffies, t)); - -	DRM_ERROR("timeout waiting for %s to idle!\n", gpu->name); +	if (spin_until(!(gpu_read(gpu, REG_A3XX_RBBM_STATUS) & +			A3XX_RBBM_STATUS_GPU_BUSY))) +		DRM_ERROR("%s: timeout waiting for GPU to idle!\n", gpu->name);  	/* TODO maybe we need to reset GPU here to recover from hang? */  } @@ -302,7 +350,6 @@ static irqreturn_t a3xx_irq(struct msm_gpu *gpu)  	return IRQ_HANDLED;  } -#ifdef CONFIG_DEBUG_FS  static const unsigned int a3xx_registers[] = {  	0x0000, 0x0002, 0x0010, 0x0012, 0x0018, 0x0018, 0x0020, 0x0027,  	0x0029, 0x002b, 0x002e, 0x0033, 0x0040, 0x0042, 0x0050, 0x005c, @@ -342,11 +389,18 @@ static const unsigned int a3xx_registers[] = {  	0x303c, 0x303c, 0x305e, 0x305f,  }; +#ifdef CONFIG_DEBUG_FS  static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)  { +	struct drm_device *dev = gpu->dev;  	int i;  	adreno_show(gpu, m); + +	mutex_lock(&dev->struct_mutex); + +	gpu->funcs->pm_resume(gpu); +  	seq_printf(m, "status:   %08x\n",  			gpu_read(gpu, REG_A3XX_RBBM_STATUS)); @@ -362,16 +416,43 @@ static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)  			seq_printf(m, "IO:R %08x %08x\n", addr<<2, val);  		}  	} + +	gpu->funcs->pm_suspend(gpu); + +	mutex_unlock(&dev->struct_mutex);  }  #endif +/* would be nice to not have to duplicate the _show() stuff with printk(): */ +static void a3xx_dump(struct msm_gpu *gpu) +{ +	int i; + +	adreno_dump(gpu); +	printk("status:   %08x\n", +			gpu_read(gpu, REG_A3XX_RBBM_STATUS)); + +	/* dump these out in a form that can be parsed by demsm: */ +	printk("IO:region %s 00000000 00020000\n", gpu->name); +	for (i = 0; i < ARRAY_SIZE(a3xx_registers); i += 2) { +		uint32_t start = a3xx_registers[i]; +		uint32_t end   = a3xx_registers[i+1]; +		uint32_t addr; + +		for (addr = start; addr <= end; addr++) { +			uint32_t val = gpu_read(gpu, addr); +			printk("IO:R %08x %08x\n", addr<<2, val); +		} +	} +} +  static const struct adreno_gpu_funcs funcs = {  	.base = {  		.get_param = adreno_get_param,  		.hw_init = a3xx_hw_init,  		.pm_suspend = msm_gpu_pm_suspend,  		.pm_resume = msm_gpu_pm_resume, -		.recover = adreno_recover, +		.recover = a3xx_recover,  		.last_fence = adreno_last_fence,  		.submit = adreno_submit,  		.flush = adreno_flush, @@ -384,11 +465,20 @@ static const struct adreno_gpu_funcs funcs = {  	},  }; +static const struct msm_gpu_perfcntr perfcntrs[] = { +	{ REG_A3XX_SP_PERFCOUNTER6_SELECT, REG_A3XX_RBBM_PERFCTR_SP_6_LO, +			SP_ALU_ACTIVE_CYCLES, "ALUACTIVE" }, +	{ REG_A3XX_SP_PERFCOUNTER7_SELECT, REG_A3XX_RBBM_PERFCTR_SP_7_LO, +			SP_FS_FULL_ALU_INSTRUCTIONS, "ALUFULL" }, +}; +  struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)  {  	struct a3xx_gpu *a3xx_gpu = NULL; +	struct adreno_gpu *adreno_gpu;  	struct msm_gpu *gpu; -	struct platform_device *pdev = a3xx_pdev; +	struct msm_drm_private *priv = dev->dev_private; +	struct platform_device *pdev = priv->gpu_pdev;  	struct adreno_platform_config *config;  	int ret; @@ -406,24 +496,57 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)  		goto fail;  	} -	gpu = &a3xx_gpu->base.base; +	adreno_gpu = &a3xx_gpu->base; +	gpu = &adreno_gpu->base; -	get_device(&pdev->dev);  	a3xx_gpu->pdev = pdev;  	gpu->fast_rate = config->fast_rate;  	gpu->slow_rate = config->slow_rate;  	gpu->bus_freq  = config->bus_freq; +#ifdef CONFIG_MSM_BUS_SCALING +	gpu->bus_scale_table = config->bus_scale_table; +#endif  	DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u",  			gpu->fast_rate, gpu->slow_rate, gpu->bus_freq); -	ret = adreno_gpu_init(dev, pdev, &a3xx_gpu->base, -			&funcs, config->rev); +	gpu->perfcntrs = perfcntrs; +	gpu->num_perfcntrs = ARRAY_SIZE(perfcntrs); + +	ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, config->rev);  	if (ret)  		goto fail; -	return &a3xx_gpu->base.base; +	/* if needed, allocate gmem: */ +	if (adreno_is_a330(adreno_gpu)) { +#ifdef CONFIG_MSM_OCMEM +		/* TODO this is different/missing upstream: */ +		struct ocmem_buf *ocmem_hdl = +				ocmem_allocate(OCMEM_GRAPHICS, adreno_gpu->gmem); + +		a3xx_gpu->ocmem_hdl = ocmem_hdl; +		a3xx_gpu->ocmem_base = ocmem_hdl->addr; +		adreno_gpu->gmem = ocmem_hdl->len; +		DBG("using %dK of OCMEM at 0x%08x", adreno_gpu->gmem / 1024, +				a3xx_gpu->ocmem_base); +#endif +	} + +	if (!gpu->mmu) { +		/* TODO we think it is possible to configure the GPU to +		 * restrict access to VRAM carveout.  But the required +		 * registers are unknown.  For now just bail out and +		 * limp along with just modesetting.  If it turns out +		 * to not be possible to restrict access, then we must +		 * implement a cmdstream validator. +		 */ +		dev_err(dev->dev, "No memory protection without IOMMU\n"); +		ret = -ENXIO; +		goto fail; +	} + +	return gpu;  fail:  	if (a3xx_gpu) @@ -436,19 +559,66 @@ fail:   * The a3xx device:   */ -static int a3xx_probe(struct platform_device *pdev) +#if defined(CONFIG_MSM_BUS_SCALING) && !defined(CONFIG_OF) +#  include <mach/kgsl.h> +#endif + +static void set_gpu_pdev(struct drm_device *dev, +		struct platform_device *pdev) +{ +	struct msm_drm_private *priv = dev->dev_private; +	priv->gpu_pdev = pdev; +} + +static int a3xx_bind(struct device *dev, struct device *master, void *data)  {  	static struct adreno_platform_config config = {};  #ifdef CONFIG_OF -	/* TODO */ +	struct device_node *child, *node = dev->of_node; +	u32 val; +	int ret; + +	ret = of_property_read_u32(node, "qcom,chipid", &val); +	if (ret) { +		dev_err(dev, "could not find chipid: %d\n", ret); +		return ret; +	} + +	config.rev = ADRENO_REV((val >> 24) & 0xff, +			(val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff); + +	/* find clock rates: */ +	config.fast_rate = 0; +	config.slow_rate = ~0; +	for_each_child_of_node(node, child) { +		if (of_device_is_compatible(child, "qcom,gpu-pwrlevels")) { +			struct device_node *pwrlvl; +			for_each_child_of_node(child, pwrlvl) { +				ret = of_property_read_u32(pwrlvl, "qcom,gpu-freq", &val); +				if (ret) { +					dev_err(dev, "could not find gpu-freq: %d\n", ret); +					return ret; +				} +				config.fast_rate = max(config.fast_rate, val); +				config.slow_rate = min(config.slow_rate, val); +			} +		} +	} + +	if (!config.fast_rate) { +		dev_err(dev, "could not find clk rates\n"); +		return -ENXIO; +	} +  #else +	struct kgsl_device_platform_data *pdata = dev->platform_data;  	uint32_t version = socinfo_get_version();  	if (cpu_is_apq8064ab()) {  		config.fast_rate = 450000000;  		config.slow_rate = 27000000;  		config.bus_freq  = 4;  		config.rev = ADRENO_REV(3, 2, 1, 0); -	} else if (cpu_is_apq8064() || cpu_is_msm8960ab()) { +	} else if (cpu_is_apq8064()) {  		config.fast_rate = 400000000;  		config.slow_rate = 27000000;  		config.bus_freq  = 4; @@ -461,6 +631,16 @@ static int a3xx_probe(struct platform_device *pdev)  		else  			config.rev = ADRENO_REV(3, 2, 0, 0); +	} else if (cpu_is_msm8960ab()) { +		config.fast_rate = 400000000; +		config.slow_rate = 320000000; +		config.bus_freq  = 4; + +		if (SOCINFO_VERSION_MINOR(version) == 0) +			config.rev = ADRENO_REV(3, 2, 1, 0); +		else +			config.rev = ADRENO_REV(3, 2, 1, 1); +  	} else if (cpu_is_msm8930()) {  		config.fast_rate = 400000000;  		config.slow_rate = 27000000; @@ -473,22 +653,49 @@ static int a3xx_probe(struct platform_device *pdev)  			config.rev = ADRENO_REV(3, 0, 5, 0);  	} +#  ifdef CONFIG_MSM_BUS_SCALING +	config.bus_scale_table = pdata->bus_scale_table; +#  endif  #endif -	pdev->dev.platform_data = &config; -	a3xx_pdev = pdev; +	dev->platform_data = &config; +	set_gpu_pdev(dev_get_drvdata(master), to_platform_device(dev));  	return 0;  } +static void a3xx_unbind(struct device *dev, struct device *master, +		void *data) +{ +	set_gpu_pdev(dev_get_drvdata(master), NULL); +} + +static const struct component_ops a3xx_ops = { +		.bind   = a3xx_bind, +		.unbind = a3xx_unbind, +}; + +static int a3xx_probe(struct platform_device *pdev) +{ +	return component_add(&pdev->dev, &a3xx_ops); +} +  static int a3xx_remove(struct platform_device *pdev)  { -	a3xx_pdev = NULL; +	component_del(&pdev->dev, &a3xx_ops);  	return 0;  } +static const struct of_device_id dt_match[] = { +	{ .compatible = "qcom,kgsl-3d0" }, +	{} +}; +  static struct platform_driver a3xx_driver = {  	.probe = a3xx_probe,  	.remove = a3xx_remove, -	.driver.name = "kgsl-3d0", +	.driver = { +		.name = "kgsl-3d0", +		.of_match_table = dt_match, +	},  };  void __init a3xx_register(void) diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.h b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h index 32c398c2d00..bb9a8ca0507 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h @@ -24,6 +24,10 @@  struct a3xx_gpu {  	struct adreno_gpu base;  	struct platform_device *pdev; + +	/* if OCMEM is used for GMEM: */ +	uint32_t ocmem_base; +	void *ocmem_hdl;  };  #define to_a3xx_gpu(x) container_of(x, struct a3xx_gpu, base) diff --git a/drivers/gpu/drm/msm/adreno/adreno_common.xml.h b/drivers/gpu/drm/msm/adreno/adreno_common.xml.h index 61979d458ac..d6e6ce2d1ab 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_common.xml.h +++ b/drivers/gpu/drm/msm/adreno/adreno_common.xml.h @@ -4,16 +4,17 @@  /* Autogenerated file, DO NOT EDIT manually!  This file was generated by the rules-ng-ng headergen tool in this git repository: -http://0x04.net/cgit/index.cgi/rules-ng-ng -git clone git://0x04.net/rules-ng-ng +http://github.com/freedreno/envytools/ +git clone https://github.com/freedreno/envytools.git  The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml              (    327 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml           (  30005 bytes, from 2013-07-19 21:30:48) -- /home/robclark/src/freedreno/envytools/rnndb/adreno_common.xml       (   8983 bytes, from 2013-07-24 01:38:36) -- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml          (   9712 bytes, from 2013-05-26 15:22:37) -- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml           (  51415 bytes, from 2013-08-03 14:26:05) +- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml               (    364 bytes, from 2013-11-30 14:47:15) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml  (   1453 bytes, from 2013-03-31 16:51:27) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml          (  32814 bytes, from 2013-11-30 15:07:33) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml (   8900 bytes, from 2013-10-22 23:57:49) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml    (  10574 bytes, from 2013-11-13 05:44:45) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml          (  53644 bytes, from 2013-11-30 15:07:33) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml          (   8344 bytes, from 2013-11-30 14:49:47)  Copyright (C) 2013 by the following authors:  - Rob Clark <robdclark@gmail.com> (robclark) @@ -115,96 +116,6 @@ enum adreno_rb_depth_format {  	DEPTHX_24_8 = 1,  }; -enum adreno_mmu_clnt_beh { -	BEH_NEVR = 0, -	BEH_TRAN_RNG = 1, -	BEH_TRAN_FLT = 2, -}; - -#define REG_AXXX_MH_MMU_CONFIG					0x00000040 -#define AXXX_MH_MMU_CONFIG_MMU_ENABLE				0x00000001 -#define AXXX_MH_MMU_CONFIG_SPLIT_MODE_ENABLE			0x00000002 -#define AXXX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR__MASK		0x00000030 -#define AXXX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR__SHIFT		4 -static inline uint32_t AXXX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) -{ -	return ((val) << AXXX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR__MASK; -} -#define AXXX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR__MASK		0x000000c0 -#define AXXX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR__SHIFT		6 -static inline uint32_t AXXX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) -{ -	return ((val) << AXXX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR__MASK; -} -#define AXXX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR__MASK		0x00000300 -#define AXXX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR__SHIFT		8 -static inline uint32_t AXXX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) -{ -	return ((val) << AXXX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR__MASK; -} -#define AXXX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR__MASK		0x00000c00 -#define AXXX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR__SHIFT		10 -static inline uint32_t AXXX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) -{ -	return ((val) << AXXX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR__MASK; -} -#define AXXX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR__MASK		0x00003000 -#define AXXX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR__SHIFT		12 -static inline uint32_t AXXX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) -{ -	return ((val) << AXXX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR__MASK; -} -#define AXXX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR__MASK		0x0000c000 -#define AXXX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR__SHIFT		14 -static inline uint32_t AXXX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) -{ -	return ((val) << AXXX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR__MASK; -} -#define AXXX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR__MASK		0x00030000 -#define AXXX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR__SHIFT		16 -static inline uint32_t AXXX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) -{ -	return ((val) << AXXX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR__MASK; -} -#define AXXX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR__MASK		0x000c0000 -#define AXXX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR__SHIFT		18 -static inline uint32_t AXXX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) -{ -	return ((val) << AXXX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR__MASK; -} -#define AXXX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR__MASK		0x00300000 -#define AXXX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR__SHIFT		20 -static inline uint32_t AXXX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) -{ -	return ((val) << AXXX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR__MASK; -} -#define AXXX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR__MASK		0x00c00000 -#define AXXX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR__SHIFT		22 -static inline uint32_t AXXX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) -{ -	return ((val) << AXXX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR__MASK; -} -#define AXXX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR__MASK		0x03000000 -#define AXXX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR__SHIFT		24 -static inline uint32_t AXXX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val) -{ -	return ((val) << AXXX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR__MASK; -} - -#define REG_AXXX_MH_MMU_VA_RANGE				0x00000041 - -#define REG_AXXX_MH_MMU_PT_BASE					0x00000042 - -#define REG_AXXX_MH_MMU_PAGE_FAULT				0x00000043 - -#define REG_AXXX_MH_MMU_TRAN_ERROR				0x00000044 - -#define REG_AXXX_MH_MMU_INVALIDATE				0x00000045 - -#define REG_AXXX_MH_MMU_MPU_BASE				0x00000046 - -#define REG_AXXX_MH_MMU_MPU_END					0x00000047 -  #define REG_AXXX_CP_RB_BASE					0x000001c0  #define REG_AXXX_CP_RB_CNTL					0x000001c1 @@ -275,6 +186,18 @@ static inline uint32_t AXXX_CP_QUEUE_THRESHOLDS_CSQ_ST_START(uint32_t val)  }  #define REG_AXXX_CP_MEQ_THRESHOLDS				0x000001d6 +#define AXXX_CP_MEQ_THRESHOLDS_MEQ_END__MASK			0x001f0000 +#define AXXX_CP_MEQ_THRESHOLDS_MEQ_END__SHIFT			16 +static inline uint32_t AXXX_CP_MEQ_THRESHOLDS_MEQ_END(uint32_t val) +{ +	return ((val) << AXXX_CP_MEQ_THRESHOLDS_MEQ_END__SHIFT) & AXXX_CP_MEQ_THRESHOLDS_MEQ_END__MASK; +} +#define AXXX_CP_MEQ_THRESHOLDS_ROQ_END__MASK			0x1f000000 +#define AXXX_CP_MEQ_THRESHOLDS_ROQ_END__SHIFT			24 +static inline uint32_t AXXX_CP_MEQ_THRESHOLDS_ROQ_END(uint32_t val) +{ +	return ((val) << AXXX_CP_MEQ_THRESHOLDS_ROQ_END__SHIFT) & AXXX_CP_MEQ_THRESHOLDS_ROQ_END__MASK; +}  #define REG_AXXX_CP_CSQ_AVAIL					0x000001d7  #define AXXX_CP_CSQ_AVAIL_RING__MASK				0x0000007f @@ -402,6 +325,36 @@ static inline uint32_t AXXX_CP_CSQ_IB2_STAT_WPTR(uint32_t val)  	return ((val) << AXXX_CP_CSQ_IB2_STAT_WPTR__SHIFT) & AXXX_CP_CSQ_IB2_STAT_WPTR__MASK;  } +#define REG_AXXX_CP_NON_PREFETCH_CNTRS				0x00000440 + +#define REG_AXXX_CP_STQ_ST_STAT					0x00000443 + +#define REG_AXXX_CP_ST_BASE					0x0000044d + +#define REG_AXXX_CP_ST_BUFSZ					0x0000044e + +#define REG_AXXX_CP_MEQ_STAT					0x0000044f + +#define REG_AXXX_CP_MIU_TAG_STAT				0x00000452 + +#define REG_AXXX_CP_BIN_MASK_LO					0x00000454 + +#define REG_AXXX_CP_BIN_MASK_HI					0x00000455 + +#define REG_AXXX_CP_BIN_SELECT_LO				0x00000456 + +#define REG_AXXX_CP_BIN_SELECT_HI				0x00000457 + +#define REG_AXXX_CP_IB1_BASE					0x00000458 + +#define REG_AXXX_CP_IB1_BUFSZ					0x00000459 + +#define REG_AXXX_CP_IB2_BASE					0x0000045a + +#define REG_AXXX_CP_IB2_BUFSZ					0x0000045b + +#define REG_AXXX_CP_STAT					0x0000047f +  #define REG_AXXX_CP_SCRATCH_REG0				0x00000578  #define REG_AXXX_CP_SCRATCH_REG1				0x00000579 @@ -418,6 +371,26 @@ static inline uint32_t AXXX_CP_CSQ_IB2_STAT_WPTR(uint32_t val)  #define REG_AXXX_CP_SCRATCH_REG7				0x0000057f +#define REG_AXXX_CP_ME_VS_EVENT_SRC				0x00000600 + +#define REG_AXXX_CP_ME_VS_EVENT_ADDR				0x00000601 + +#define REG_AXXX_CP_ME_VS_EVENT_DATA				0x00000602 + +#define REG_AXXX_CP_ME_VS_EVENT_ADDR_SWM			0x00000603 + +#define REG_AXXX_CP_ME_VS_EVENT_DATA_SWM			0x00000604 + +#define REG_AXXX_CP_ME_PS_EVENT_SRC				0x00000605 + +#define REG_AXXX_CP_ME_PS_EVENT_ADDR				0x00000606 + +#define REG_AXXX_CP_ME_PS_EVENT_DATA				0x00000607 + +#define REG_AXXX_CP_ME_PS_EVENT_ADDR_SWM			0x00000608 + +#define REG_AXXX_CP_ME_PS_EVENT_DATA_SWM			0x00000609 +  #define REG_AXXX_CP_ME_CF_EVENT_SRC				0x0000060a  #define REG_AXXX_CP_ME_CF_EVENT_ADDR				0x0000060b @@ -428,5 +401,11 @@ static inline uint32_t AXXX_CP_CSQ_IB2_STAT_WPTR(uint32_t val)  #define REG_AXXX_CP_ME_NRT_DATA					0x0000060e +#define REG_AXXX_CP_ME_VS_FETCH_DONE_SRC			0x00000612 + +#define REG_AXXX_CP_ME_VS_FETCH_DONE_ADDR			0x00000613 + +#define REG_AXXX_CP_ME_VS_FETCH_DONE_DATA			0x00000614 +  #endif /* ADRENO_COMMON_XML */ diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index a60584763b6..28ca8cd8b09 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -17,6 +17,7 @@  #include "adreno_gpu.h"  #include "msm_gem.h" +#include "msm_mmu.h"  struct adreno_info {  	struct adreno_rev rev; @@ -44,7 +45,7 @@ static const struct adreno_info gpulist[] = {  		.pfpfw = "a300_pfp.fw",  		.gmem  = SZ_512K,  	}, { -		.rev   = ADRENO_REV(3, 3, 0, 0), +		.rev   = ADRENO_REV(3, 3, 0, ANY_ID),  		.revn  = 330,  		.name  = "A330",  		.pm4fw = "a330_pm4.fw", @@ -53,6 +54,11 @@ static const struct adreno_info gpulist[] = {  	},  }; +MODULE_FIRMWARE("a300_pm4.fw"); +MODULE_FIRMWARE("a300_pfp.fw"); +MODULE_FIRMWARE("a330_pm4.fw"); +MODULE_FIRMWARE("a330_pfp.fw"); +  #define RB_SIZE    SZ_32K  #define RB_BLKSIZE 16 @@ -65,7 +71,13 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value)  		*value = adreno_gpu->info->revn;  		return 0;  	case MSM_PARAM_GMEM_SIZE: -		*value = adreno_gpu->info->gmem; +		*value = adreno_gpu->gmem; +		return 0; +	case MSM_PARAM_CHIP_ID: +		*value = adreno_gpu->rev.patchid | +				(adreno_gpu->rev.minor << 8) | +				(adreno_gpu->rev.major << 16) | +				(adreno_gpu->rev.core << 24);  		return 0;  	default:  		DBG("%s: invalid param: %u", gpu->name, param); @@ -86,7 +98,7 @@ int adreno_hw_init(struct msm_gpu *gpu)  	gpu_write(gpu, REG_AXXX_CP_RB_CNTL,  			/* size is log2(quad-words): */  			AXXX_CP_RB_CNTL_BUFSZ(ilog2(gpu->rb->size / 8)) | -			AXXX_CP_RB_CNTL_BLKSZ(RB_BLKSIZE)); +			AXXX_CP_RB_CNTL_BLKSZ(ilog2(RB_BLKSIZE / 8)));  	/* Setup ringbuffer address: */  	gpu_write(gpu, REG_AXXX_CP_RB_BASE, gpu->rb_iova); @@ -124,6 +136,8 @@ void adreno_recover(struct msm_gpu *gpu)  	/* reset completed fence seqno, just discard anything pending: */  	adreno_gpu->memptrs->fence = gpu->submitted_fence; +	adreno_gpu->memptrs->rptr  = 0; +	adreno_gpu->memptrs->wptr  = 0;  	gpu->funcs->pm_resume(gpu);  	ret = gpu->funcs->hw_init(gpu); @@ -217,19 +231,11 @@ void adreno_flush(struct msm_gpu *gpu)  void adreno_idle(struct msm_gpu *gpu)  {  	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); -	uint32_t rptr, wptr = get_wptr(gpu->rb); -	unsigned long t; - -	t = jiffies + ADRENO_IDLE_TIMEOUT; - -	/* then wait for CP to drain ringbuffer: */ -	do { -		rptr = adreno_gpu->memptrs->rptr; -		if (rptr == wptr) -			return; -	} while(time_before(jiffies, t)); +	uint32_t wptr = get_wptr(gpu->rb); -	DRM_ERROR("timeout waiting for %s to drain ringbuffer!\n", gpu->name); +	/* wait for CP to drain ringbuffer: */ +	if (spin_until(adreno_gpu->memptrs->rptr == wptr)) +		DRM_ERROR("%s: timeout waiting to drain ringbuffer!\n", gpu->name);  	/* TODO maybe we need to reset GPU here to recover from hang? */  } @@ -252,16 +258,37 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m)  }  #endif -void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords) +/* would be nice to not have to duplicate the _show() stuff with printk(): */ +void adreno_dump(struct msm_gpu *gpu)  {  	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); -	uint32_t freedwords; -	do { -		uint32_t size = gpu->rb->size / 4; -		uint32_t wptr = get_wptr(gpu->rb); -		uint32_t rptr = adreno_gpu->memptrs->rptr; -		freedwords = (rptr + (size - 1) - wptr) % size; -	} while(freedwords < ndwords); + +	printk("revision: %d (%d.%d.%d.%d)\n", +			adreno_gpu->info->revn, adreno_gpu->rev.core, +			adreno_gpu->rev.major, adreno_gpu->rev.minor, +			adreno_gpu->rev.patchid); + +	printk("fence:    %d/%d\n", adreno_gpu->memptrs->fence, +			gpu->submitted_fence); +	printk("rptr:     %d\n", adreno_gpu->memptrs->rptr); +	printk("wptr:     %d\n", adreno_gpu->memptrs->wptr); +	printk("rb wptr:  %d\n", get_wptr(gpu->rb)); + +} + +static uint32_t ring_freewords(struct msm_gpu *gpu) +{ +	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); +	uint32_t size = gpu->rb->size / 4; +	uint32_t wptr = get_wptr(gpu->rb); +	uint32_t rptr = adreno_gpu->memptrs->rptr; +	return (rptr + (size - 1) - wptr) % size; +} + +void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords) +{ +	if (spin_until(ring_freewords(gpu) >= ndwords)) +		DRM_ERROR("%s: timeout waiting for ringbuffer space\n", gpu->name);  }  static const char *iommu_ports[] = { @@ -278,6 +305,7 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,  		struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs,  		struct adreno_rev rev)  { +	struct msm_mmu *mmu;  	int i, ret;  	/* identify gpu: */ @@ -303,6 +331,7 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,  			rev.core, rev.major, rev.minor, rev.patchid);  	gpu->funcs = funcs; +	gpu->gmem = gpu->info->gmem;  	gpu->rev = rev;  	ret = request_firmware(&gpu->pm4, gpu->info->pm4fw, drm->dev); @@ -325,10 +354,13 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,  	if (ret)  		return ret; -	ret = msm_iommu_attach(drm, gpu->base.iommu, -			iommu_ports, ARRAY_SIZE(iommu_ports)); -	if (ret) -		return ret; +	mmu = gpu->base.mmu; +	if (mmu) { +		ret = mmu->funcs->attach(mmu, iommu_ports, +				ARRAY_SIZE(iommu_ports)); +		if (ret) +			return ret; +	}  	gpu->memptrs_bo = msm_gem_new(drm, sizeof(*gpu->memptrs),  			MSM_BO_UNCACHED); diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index f73abfba7c2..63c36ce3302 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -51,6 +51,7 @@ struct adreno_gpu {  	struct msm_gpu base;  	struct adreno_rev rev;  	const struct adreno_info *info; +	uint32_t gmem;  /* actual gmem size */  	uint32_t revn;  /* numeric revision name */  	const struct adreno_gpu_funcs *funcs; @@ -70,9 +71,25 @@ struct adreno_gpu {  struct adreno_platform_config {  	struct adreno_rev rev;  	uint32_t fast_rate, slow_rate, bus_freq; +#ifdef CONFIG_MSM_BUS_SCALING +	struct msm_bus_scale_pdata *bus_scale_table; +#endif  }; -#define ADRENO_IDLE_TIMEOUT (20 * 1000) +#define ADRENO_IDLE_TIMEOUT msecs_to_jiffies(1000) + +#define spin_until(X) ({                                   \ +	int __ret = -ETIMEDOUT;                            \ +	unsigned long __t = jiffies + ADRENO_IDLE_TIMEOUT; \ +	do {                                               \ +		if (X) {                                   \ +			__ret = 0;                         \ +			break;                             \ +		}                                          \ +	} while (time_before(jiffies, __t));               \ +	__ret;                                             \ +}) +  static inline bool adreno_is_a3xx(struct adreno_gpu *gpu)  { @@ -94,6 +111,11 @@ static inline bool adreno_is_a330(struct adreno_gpu *gpu)  	return gpu->revn == 330;  } +static inline bool adreno_is_a330v2(struct adreno_gpu *gpu) +{ +	return adreno_is_a330(gpu) && (gpu->rev.patchid > 0); +} +  int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value);  int adreno_hw_init(struct msm_gpu *gpu);  uint32_t adreno_last_fence(struct msm_gpu *gpu); @@ -105,6 +127,7 @@ void adreno_idle(struct msm_gpu *gpu);  #ifdef CONFIG_DEBUG_FS  void adreno_show(struct msm_gpu *gpu, struct seq_file *m);  #endif +void adreno_dump(struct msm_gpu *gpu);  void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords);  int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, diff --git a/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h b/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h index 94c13f418e7..ae992c71703 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h +++ b/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h @@ -4,16 +4,17 @@  /* Autogenerated file, DO NOT EDIT manually!  This file was generated by the rules-ng-ng headergen tool in this git repository: -http://0x04.net/cgit/index.cgi/rules-ng-ng -git clone git://0x04.net/rules-ng-ng +http://github.com/freedreno/envytools/ +git clone https://github.com/freedreno/envytools.git  The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml              (    327 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml           (  30005 bytes, from 2013-07-19 21:30:48) -- /home/robclark/src/freedreno/envytools/rnndb/adreno_common.xml       (   8983 bytes, from 2013-07-24 01:38:36) -- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml          (   9712 bytes, from 2013-05-26 15:22:37) -- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml           (  51415 bytes, from 2013-08-03 14:26:05) +- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml               (    364 bytes, from 2013-11-30 14:47:15) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml  (   1453 bytes, from 2013-03-31 16:51:27) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml          (  32814 bytes, from 2013-11-30 15:07:33) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml (   8900 bytes, from 2013-10-22 23:57:49) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml    (  10574 bytes, from 2013-11-13 05:44:45) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml          (  53644 bytes, from 2013-11-30 15:07:33) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml          (   8344 bytes, from 2013-11-30 14:49:47)  Copyright (C) 2013 by the following authors:  - Rob Clark <robdclark@gmail.com> (robclark) @@ -66,13 +67,15 @@ enum vgt_event_type {  enum pc_di_primtype {  	DI_PT_NONE = 0, -	DI_PT_POINTLIST = 1, +	DI_PT_POINTLIST_A2XX = 1,  	DI_PT_LINELIST = 2,  	DI_PT_LINESTRIP = 3,  	DI_PT_TRILIST = 4,  	DI_PT_TRIFAN = 5,  	DI_PT_TRISTRIP = 6, +	DI_PT_LINELOOP = 7,  	DI_PT_RECTLIST = 8, +	DI_PT_POINTLIST_A3XX = 9,  	DI_PT_QUADLIST = 13,  	DI_PT_QUADSTRIP = 14,  	DI_PT_POLYGON = 15, @@ -119,7 +122,7 @@ enum adreno_pm4_type3_packets {  	CP_WAIT_FOR_IDLE = 38,  	CP_WAIT_REG_MEM = 60,  	CP_WAIT_REG_EQ = 82, -	CP_WAT_REG_GTE = 83, +	CP_WAIT_REG_GTE = 83,  	CP_WAIT_UNTIL_READ = 92,  	CP_WAIT_IB_PFD_COMPLETE = 93,  	CP_REG_RMW = 33, @@ -151,7 +154,6 @@ enum adreno_pm4_type3_packets {  	CP_CONTEXT_UPDATE = 94,  	CP_INTERRUPT = 64,  	CP_IM_STORE = 44, -	CP_SET_BIN_BASE_OFFSET = 75,  	CP_SET_DRAW_INIT_FLAGS = 75,  	CP_SET_PROTECTED_MODE = 95,  	CP_LOAD_STATE = 48, @@ -159,6 +161,16 @@ enum adreno_pm4_type3_packets {  	CP_COND_INDIRECT_BUFFER_PFD = 50,  	CP_INDIRECT_BUFFER_PFE = 63,  	CP_SET_BIN = 76, +	CP_TEST_TWO_MEMS = 113, +	CP_WAIT_FOR_ME = 19, +	IN_IB_PREFETCH_END = 23, +	IN_SUBBLK_PREFETCH = 31, +	IN_INSTR_PREFETCH = 32, +	IN_INSTR_MATCH = 71, +	IN_CONST_PREFETCH = 73, +	IN_INCR_UPDT_STATE = 85, +	IN_INCR_UPDT_CONST = 86, +	IN_INCR_UPDT_INSTR = 87,  };  enum adreno_state_block { diff --git a/drivers/gpu/drm/msm/dsi/dsi.xml.h b/drivers/gpu/drm/msm/dsi/dsi.xml.h index 6f8396be431..87be647e382 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.xml.h +++ b/drivers/gpu/drm/msm/dsi/dsi.xml.h @@ -4,18 +4,20 @@  /* Autogenerated file, DO NOT EDIT manually!  This file was generated by the rules-ng-ng headergen tool in this git repository: -http://0x04.net/cgit/index.cgi/rules-ng-ng -git clone git://0x04.net/rules-ng-ng +http://github.com/freedreno/envytools/ +git clone https://github.com/freedreno/envytools.git  The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    595 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    647 bytes, from 2013-11-30 14:45:35)  - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-08-16 22:16:36) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml            (  17996 bytes, from 2013-12-01 19:10:31) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml      (   1615 bytes, from 2013-11-30 15:00:52) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml            (  22517 bytes, from 2013-12-03 20:59:13)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05)  - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml         (    600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  19288 bytes, from 2013-08-11 18:14:15) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  20932 bytes, from 2013-12-01 15:13:04)  Copyright (C) 2013 by the following authors:  - Rob Clark <robdclark@gmail.com> (robclark) diff --git a/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h b/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h index aefc1b8feae..747a6ef4211 100644 --- a/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h +++ b/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h @@ -4,18 +4,20 @@  /* Autogenerated file, DO NOT EDIT manually!  This file was generated by the rules-ng-ng headergen tool in this git repository: -http://0x04.net/cgit/index.cgi/rules-ng-ng -git clone git://0x04.net/rules-ng-ng +http://github.com/freedreno/envytools/ +git clone https://github.com/freedreno/envytools.git  The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    595 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    647 bytes, from 2013-11-30 14:45:35)  - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-08-16 22:16:36) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml            (  17996 bytes, from 2013-12-01 19:10:31) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml      (   1615 bytes, from 2013-11-30 15:00:52) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml            (  22517 bytes, from 2013-12-03 20:59:13)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05)  - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml         (    600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  19288 bytes, from 2013-08-11 18:14:15) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  20932 bytes, from 2013-12-01 15:13:04)  Copyright (C) 2013 by the following authors:  - Rob Clark <robdclark@gmail.com> (robclark) diff --git a/drivers/gpu/drm/msm/dsi/sfpb.xml.h b/drivers/gpu/drm/msm/dsi/sfpb.xml.h index a225e8170b2..48e03acf19b 100644 --- a/drivers/gpu/drm/msm/dsi/sfpb.xml.h +++ b/drivers/gpu/drm/msm/dsi/sfpb.xml.h @@ -4,18 +4,20 @@  /* Autogenerated file, DO NOT EDIT manually!  This file was generated by the rules-ng-ng headergen tool in this git repository: -http://0x04.net/cgit/index.cgi/rules-ng-ng -git clone git://0x04.net/rules-ng-ng +http://github.com/freedreno/envytools/ +git clone https://github.com/freedreno/envytools.git  The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    595 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    647 bytes, from 2013-11-30 14:45:35)  - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-08-16 22:16:36) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml            (  17996 bytes, from 2013-12-01 19:10:31) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml      (   1615 bytes, from 2013-11-30 15:00:52) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml            (  22517 bytes, from 2013-12-03 20:59:13)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05)  - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml         (    600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  19288 bytes, from 2013-08-11 18:14:15) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  20932 bytes, from 2013-12-01 15:13:04)  Copyright (C) 2013 by the following authors:  - Rob Clark <robdclark@gmail.com> (robclark) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 50d11df35b2..7f7aadef8a8 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -17,8 +17,6 @@  #include "hdmi.h" -static struct platform_device *hdmi_pdev; -  void hdmi_set_mode(struct hdmi *hdmi, bool power_on)  {  	uint32_t ctrl = 0; @@ -41,7 +39,7 @@ void hdmi_set_mode(struct hdmi *hdmi, bool power_on)  			power_on ? "Enable" : "Disable", ctrl);  } -static irqreturn_t hdmi_irq(int irq, void *dev_id) +irqreturn_t hdmi_irq(int irq, void *dev_id)  {  	struct hdmi *hdmi = dev_id; @@ -67,17 +65,17 @@ void hdmi_destroy(struct kref *kref)  	if (hdmi->i2c)  		hdmi_i2c_destroy(hdmi->i2c); -	put_device(&hdmi->pdev->dev); +	platform_set_drvdata(hdmi->pdev, NULL);  }  /* initialize connector */ -int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder) +struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)  {  	struct hdmi *hdmi = NULL;  	struct msm_drm_private *priv = dev->dev_private; -	struct platform_device *pdev = hdmi_pdev; +	struct platform_device *pdev = priv->hdmi_pdev;  	struct hdmi_platform_config *config; -	int ret; +	int i, ret;  	if (!pdev) {  		dev_err(dev->dev, "no hdmi device\n"); @@ -95,12 +93,13 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)  	kref_init(&hdmi->refcount); -	get_device(&pdev->dev); -  	hdmi->dev = dev;  	hdmi->pdev = pdev; +	hdmi->config = config;  	hdmi->encoder = encoder; +	hdmi_audio_infoframe_init(&hdmi->audio.infoframe); +  	/* not sure about which phy maps to which msm.. probably I miss some */  	if (config->phy_init)  		hdmi->phy = config->phy_init(hdmi); @@ -114,44 +113,70 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)  		goto fail;  	} -	hdmi->mmio = msm_ioremap(pdev, "hdmi_msm_hdmi_addr", "HDMI"); +	hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI");  	if (IS_ERR(hdmi->mmio)) {  		ret = PTR_ERR(hdmi->mmio);  		goto fail;  	} -	hdmi->mvs = devm_regulator_get(&pdev->dev, "8901_hdmi_mvs"); -	if (IS_ERR(hdmi->mvs)) -		hdmi->mvs = devm_regulator_get(&pdev->dev, "hdmi_mvs"); -	if (IS_ERR(hdmi->mvs)) { -		ret = PTR_ERR(hdmi->mvs); -		dev_err(dev->dev, "failed to get mvs regulator: %d\n", ret); -		goto fail; +	BUG_ON(config->hpd_reg_cnt > ARRAY_SIZE(hdmi->hpd_regs)); +	for (i = 0; i < config->hpd_reg_cnt; i++) { +		struct regulator *reg; + +		reg = devm_regulator_get(&pdev->dev, config->hpd_reg_names[i]); +		if (IS_ERR(reg)) { +			ret = PTR_ERR(reg); +			dev_err(dev->dev, "failed to get hpd regulator: %s (%d)\n", +					config->hpd_reg_names[i], ret); +			goto fail; +		} + +		hdmi->hpd_regs[i] = reg;  	} -	hdmi->mpp0 = devm_regulator_get(&pdev->dev, "8901_mpp0"); -	if (IS_ERR(hdmi->mpp0)) -		hdmi->mpp0 = NULL; +	BUG_ON(config->pwr_reg_cnt > ARRAY_SIZE(hdmi->pwr_regs)); +	for (i = 0; i < config->pwr_reg_cnt; i++) { +		struct regulator *reg; -	hdmi->clk = devm_clk_get(&pdev->dev, "core_clk"); -	if (IS_ERR(hdmi->clk)) { -		ret = PTR_ERR(hdmi->clk); -		dev_err(dev->dev, "failed to get 'clk': %d\n", ret); -		goto fail; +		reg = devm_regulator_get(&pdev->dev, config->pwr_reg_names[i]); +		if (IS_ERR(reg)) { +			ret = PTR_ERR(reg); +			dev_err(dev->dev, "failed to get pwr regulator: %s (%d)\n", +					config->pwr_reg_names[i], ret); +			goto fail; +		} + +		hdmi->pwr_regs[i] = reg;  	} -	hdmi->m_pclk = devm_clk_get(&pdev->dev, "master_iface_clk"); -	if (IS_ERR(hdmi->m_pclk)) { -		ret = PTR_ERR(hdmi->m_pclk); -		dev_err(dev->dev, "failed to get 'm_pclk': %d\n", ret); -		goto fail; +	BUG_ON(config->hpd_clk_cnt > ARRAY_SIZE(hdmi->hpd_clks)); +	for (i = 0; i < config->hpd_clk_cnt; i++) { +		struct clk *clk; + +		clk = devm_clk_get(&pdev->dev, config->hpd_clk_names[i]); +		if (IS_ERR(clk)) { +			ret = PTR_ERR(clk); +			dev_err(dev->dev, "failed to get hpd clk: %s (%d)\n", +					config->hpd_clk_names[i], ret); +			goto fail; +		} + +		hdmi->hpd_clks[i] = clk;  	} -	hdmi->s_pclk = devm_clk_get(&pdev->dev, "slave_iface_clk"); -	if (IS_ERR(hdmi->s_pclk)) { -		ret = PTR_ERR(hdmi->s_pclk); -		dev_err(dev->dev, "failed to get 's_pclk': %d\n", ret); -		goto fail; +	BUG_ON(config->pwr_clk_cnt > ARRAY_SIZE(hdmi->pwr_clks)); +	for (i = 0; i < config->pwr_clk_cnt; i++) { +		struct clk *clk; + +		clk = devm_clk_get(&pdev->dev, config->pwr_clk_names[i]); +		if (IS_ERR(clk)) { +			ret = PTR_ERR(clk); +			dev_err(dev->dev, "failed to get pwr clk: %s (%d)\n", +					config->pwr_clk_names[i], ret); +			goto fail; +		} + +		hdmi->pwr_clks[i] = clk;  	}  	hdmi->i2c = hdmi_i2c_init(hdmi); @@ -178,20 +203,22 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)  		goto fail;  	} -	hdmi->irq = platform_get_irq(pdev, 0); -	if (hdmi->irq < 0) { -		ret = hdmi->irq; -		dev_err(dev->dev, "failed to get irq: %d\n", ret); -		goto fail; -	} +	if (!config->shared_irq) { +		hdmi->irq = platform_get_irq(pdev, 0); +		if (hdmi->irq < 0) { +			ret = hdmi->irq; +			dev_err(dev->dev, "failed to get irq: %d\n", ret); +			goto fail; +		} -	ret = devm_request_threaded_irq(&pdev->dev, hdmi->irq, -			NULL, hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, -			"hdmi_isr", hdmi); -	if (ret < 0) { -		dev_err(dev->dev, "failed to request IRQ%u: %d\n", -				hdmi->irq, ret); -		goto fail; +		ret = devm_request_threaded_irq(&pdev->dev, hdmi->irq, +				NULL, hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, +				"hdmi_isr", hdmi); +		if (ret < 0) { +			dev_err(dev->dev, "failed to request IRQ%u: %d\n", +					hdmi->irq, ret); +			goto fail; +		}  	}  	encoder->bridge = hdmi->bridge; @@ -199,7 +226,9 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)  	priv->bridges[priv->num_bridges++]       = hdmi->bridge;  	priv->connectors[priv->num_connectors++] = hdmi->connector; -	return 0; +	platform_set_drvdata(pdev, hdmi); + +	return hdmi;  fail:  	if (hdmi) { @@ -211,54 +240,150 @@ fail:  		hdmi_destroy(&hdmi->refcount);  	} -	return ret; +	return ERR_PTR(ret);  }  /*   * The hdmi device:   */ -static int hdmi_dev_probe(struct platform_device *pdev) +#include <linux/of_gpio.h> + +static void set_hdmi_pdev(struct drm_device *dev, +		struct platform_device *pdev) +{ +	struct msm_drm_private *priv = dev->dev_private; +	priv->hdmi_pdev = pdev; +} + +static int hdmi_bind(struct device *dev, struct device *master, void *data)  {  	static struct hdmi_platform_config config = {};  #ifdef CONFIG_OF -	/* TODO */ +	struct device_node *of_node = dev->of_node; + +	int get_gpio(const char *name) +	{ +		int gpio = of_get_named_gpio(of_node, name, 0); +		if (gpio < 0) { +			dev_err(dev, "failed to get gpio: %s (%d)\n", +					name, gpio); +			gpio = -1; +		} +		return gpio; +	} + +	/* TODO actually use DT.. */ +	static const char *hpd_reg_names[] = {"hpd-gdsc", "hpd-5v"}; +	static const char *pwr_reg_names[] = {"core-vdda", "core-vcc"}; +	static const char *hpd_clk_names[] = {"iface_clk", "core_clk", "mdp_core_clk"}; +	static unsigned long hpd_clk_freq[] = {0, 19200000, 0}; +	static const char *pwr_clk_names[] = {"extp_clk", "alt_iface_clk"}; + +	config.phy_init      = hdmi_phy_8x74_init; +	config.mmio_name     = "core_physical"; +	config.hpd_reg_names = hpd_reg_names; +	config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names); +	config.pwr_reg_names = pwr_reg_names; +	config.pwr_reg_cnt   = ARRAY_SIZE(pwr_reg_names); +	config.hpd_clk_names = hpd_clk_names; +	config.hpd_freq      = hpd_clk_freq; +	config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names); +	config.pwr_clk_names = pwr_clk_names; +	config.pwr_clk_cnt   = ARRAY_SIZE(pwr_clk_names); +	config.ddc_clk_gpio  = get_gpio("qcom,hdmi-tx-ddc-clk"); +	config.ddc_data_gpio = get_gpio("qcom,hdmi-tx-ddc-data"); +	config.hpd_gpio      = get_gpio("qcom,hdmi-tx-hpd"); +	config.mux_en_gpio   = get_gpio("qcom,hdmi-tx-mux-en"); +	config.mux_sel_gpio  = get_gpio("qcom,hdmi-tx-mux-sel"); +	config.shared_irq    = true; +  #else +	static const char *hpd_clk_names[] = { +			"core_clk", "master_iface_clk", "slave_iface_clk", +	};  	if (cpu_is_apq8064()) { +		static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};  		config.phy_init      = hdmi_phy_8960_init; +		config.mmio_name     = "hdmi_msm_hdmi_addr"; +		config.hpd_reg_names = hpd_reg_names; +		config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names); +		config.hpd_clk_names = hpd_clk_names; +		config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);  		config.ddc_clk_gpio  = 70;  		config.ddc_data_gpio = 71;  		config.hpd_gpio      = 72; -		config.pmic_gpio     = 13 + NR_GPIO_IRQS; -	} else if (cpu_is_msm8960()) { +		config.mux_en_gpio   = -1; +		config.mux_sel_gpio  = -1; +	} else if (cpu_is_msm8960() || cpu_is_msm8960ab()) { +		static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};  		config.phy_init      = hdmi_phy_8960_init; +		config.mmio_name     = "hdmi_msm_hdmi_addr"; +		config.hpd_reg_names = hpd_reg_names; +		config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names); +		config.hpd_clk_names = hpd_clk_names; +		config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);  		config.ddc_clk_gpio  = 100;  		config.ddc_data_gpio = 101;  		config.hpd_gpio      = 102; -		config.pmic_gpio     = -1; +		config.mux_en_gpio   = -1; +		config.mux_sel_gpio  = -1;  	} else if (cpu_is_msm8x60()) { +		static const char *hpd_reg_names[] = { +				"8901_hdmi_mvs", "8901_mpp0" +		};  		config.phy_init      = hdmi_phy_8x60_init; +		config.mmio_name     = "hdmi_msm_hdmi_addr"; +		config.hpd_reg_names = hpd_reg_names; +		config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names); +		config.hpd_clk_names = hpd_clk_names; +		config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);  		config.ddc_clk_gpio  = 170;  		config.ddc_data_gpio = 171;  		config.hpd_gpio      = 172; -		config.pmic_gpio     = -1; +		config.mux_en_gpio   = -1; +		config.mux_sel_gpio  = -1;  	}  #endif -	pdev->dev.platform_data = &config; -	hdmi_pdev = pdev; +	dev->platform_data = &config; +	set_hdmi_pdev(dev_get_drvdata(master), to_platform_device(dev));  	return 0;  } +static void hdmi_unbind(struct device *dev, struct device *master, +		void *data) +{ +	set_hdmi_pdev(dev_get_drvdata(master), NULL); +} + +static const struct component_ops hdmi_ops = { +		.bind   = hdmi_bind, +		.unbind = hdmi_unbind, +}; + +static int hdmi_dev_probe(struct platform_device *pdev) +{ +	return component_add(&pdev->dev, &hdmi_ops); +} +  static int hdmi_dev_remove(struct platform_device *pdev)  { -	hdmi_pdev = NULL; +	component_del(&pdev->dev, &hdmi_ops);  	return 0;  } +static const struct of_device_id dt_match[] = { +	{ .compatible = "qcom,hdmi-tx" }, +	{} +}; +  static struct platform_driver hdmi_driver = {  	.probe = hdmi_dev_probe,  	.remove = hdmi_dev_remove, -	.driver.name = "hdmi_msm", +	.driver = { +		.name = "hdmi_msm", +		.of_match_table = dt_match, +	},  };  void __init hdmi_register(void) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 2c2ec566394..9d7723c6528 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -22,12 +22,20 @@  #include <linux/clk.h>  #include <linux/platform_device.h>  #include <linux/regulator/consumer.h> +#include <linux/hdmi.h>  #include "msm_drv.h"  #include "hdmi.xml.h"  struct hdmi_phy; +struct hdmi_platform_config; + +struct hdmi_audio { +	bool enabled; +	struct hdmi_audio_infoframe infoframe; +	int rate; +};  struct hdmi {  	struct kref refcount; @@ -35,14 +43,21 @@ struct hdmi {  	struct drm_device *dev;  	struct platform_device *pdev; -	void __iomem *mmio; +	const struct hdmi_platform_config *config; -	struct regulator *mvs;        /* HDMI_5V */ -	struct regulator *mpp0;       /* External 5V */ +	/* audio state: */ +	struct hdmi_audio audio; -	struct clk *clk; -	struct clk *m_pclk; -	struct clk *s_pclk; +	/* video state: */ +	bool power_on; +	unsigned long int pixclock; + +	void __iomem *mmio; + +	struct regulator *hpd_regs[2]; +	struct regulator *pwr_regs[2]; +	struct clk *hpd_clks[3]; +	struct clk *pwr_clks[2];  	struct hdmi_phy *phy;  	struct i2c_adapter *i2c; @@ -60,7 +75,30 @@ struct hdmi {  /* platform config data (ie. from DT, or pdata) */  struct hdmi_platform_config {  	struct hdmi_phy *(*phy_init)(struct hdmi *hdmi); -	int ddc_clk_gpio, ddc_data_gpio, hpd_gpio, pmic_gpio; +	const char *mmio_name; + +	/* regulators that need to be on for hpd: */ +	const char **hpd_reg_names; +	int hpd_reg_cnt; + +	/* regulators that need to be on for screen pwr: */ +	const char **pwr_reg_names; +	int pwr_reg_cnt; + +	/* clks that need to be on for hpd: */ +	const char **hpd_clk_names; +	const long unsigned *hpd_freq; +	int hpd_clk_cnt; + +	/* clks that need to be on for screen pwr (ie pixel clk): */ +	const char **pwr_clk_names; +	int pwr_clk_cnt; + +	/* gpio's: */ +	int ddc_clk_gpio, ddc_data_gpio, hpd_gpio, mux_en_gpio, mux_sel_gpio; + +	/* older devices had their own irq, mdp5+ it is shared w/ mdp: */ +	bool shared_irq;  };  void hdmi_set_mode(struct hdmi *hdmi, bool power_on); @@ -106,6 +144,18 @@ struct hdmi_phy {  struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi);  struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi); +struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi); + +/* + * audio: + */ + +int hdmi_audio_update(struct hdmi *hdmi); +int hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled, +	uint32_t num_of_channels, uint32_t channel_allocation, +	uint32_t level_shift, bool down_mix); +void hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate); +  /*   * hdmi bridge: diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h index f5fa4865e05..e2636582cfd 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h @@ -4,18 +4,20 @@  /* Autogenerated file, DO NOT EDIT manually!  This file was generated by the rules-ng-ng headergen tool in this git repository: -http://0x04.net/cgit/index.cgi/rules-ng-ng -git clone git://0x04.net/rules-ng-ng +http://github.com/freedreno/envytools/ +git clone https://github.com/freedreno/envytools.git  The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    595 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    647 bytes, from 2013-11-30 14:45:35)  - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-08-16 22:16:36) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml            (  17996 bytes, from 2013-12-01 19:10:31) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml      (   1615 bytes, from 2013-11-30 15:00:52) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml            (  22517 bytes, from 2013-12-03 20:59:13)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05)  - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml         (    600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  19288 bytes, from 2013-08-11 18:14:15) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  20932 bytes, from 2013-12-01 15:13:04)  Copyright (C) 2013 by the following authors:  - Rob Clark <robdclark@gmail.com> (robclark) @@ -212,6 +214,20 @@ static inline uint32_t HDMI_HDCP_LINK0_STATUS_KEY_STATE(enum hdmi_hdcp_key_state  #define REG_HDMI_HDCP_RESET					0x00000130  #define HDMI_HDCP_RESET_LINK0_DEAUTHENTICATE			0x00000001 +#define REG_HDMI_VENSPEC_INFO0					0x0000016c + +#define REG_HDMI_VENSPEC_INFO1					0x00000170 + +#define REG_HDMI_VENSPEC_INFO2					0x00000174 + +#define REG_HDMI_VENSPEC_INFO3					0x00000178 + +#define REG_HDMI_VENSPEC_INFO4					0x0000017c + +#define REG_HDMI_VENSPEC_INFO5					0x00000180 + +#define REG_HDMI_VENSPEC_INFO6					0x00000184 +  #define REG_HDMI_AUDIO_CFG					0x000001d0  #define HDMI_AUDIO_CFG_ENGINE_ENABLE				0x00000001  #define HDMI_AUDIO_CFG_FIFO_WATERMARK__MASK			0x000000f0 @@ -235,6 +251,9 @@ static inline uint32_t HDMI_DDC_CTRL_TRANSACTION_CNT(uint32_t val)  	return ((val) << HDMI_DDC_CTRL_TRANSACTION_CNT__SHIFT) & HDMI_DDC_CTRL_TRANSACTION_CNT__MASK;  } +#define REG_HDMI_DDC_ARBITRATION				0x00000210 +#define HDMI_DDC_ARBITRATION_HW_ARBITRATION			0x00000010 +  #define REG_HDMI_DDC_INT_CTRL					0x00000214  #define HDMI_DDC_INT_CTRL_SW_DONE_INT				0x00000001  #define HDMI_DDC_INT_CTRL_SW_DONE_ACK				0x00000002 @@ -340,6 +359,20 @@ static inline uint32_t HDMI_DDC_REF_REFTIMER(uint32_t val)  	return ((val) << HDMI_DDC_REF_REFTIMER__SHIFT) & HDMI_DDC_REF_REFTIMER__MASK;  } +#define REG_HDMI_CEC_STATUS					0x00000298 + +#define REG_HDMI_CEC_INT					0x0000029c + +#define REG_HDMI_CEC_ADDR					0x000002a0 + +#define REG_HDMI_CEC_TIME					0x000002a4 + +#define REG_HDMI_CEC_REFTIMER					0x000002a8 + +#define REG_HDMI_CEC_RD_DATA					0x000002ac + +#define REG_HDMI_CEC_RD_FILTER					0x000002b0 +  #define REG_HDMI_ACTIVE_HSYNC					0x000002b4  #define HDMI_ACTIVE_HSYNC_START__MASK				0x00000fff  #define HDMI_ACTIVE_HSYNC_START__SHIFT				0 @@ -410,17 +443,33 @@ static inline uint32_t HDMI_VSYNC_TOTAL_F2_V_TOTAL(uint32_t val)  #define HDMI_FRAME_CTRL_HSYNC_LOW				0x20000000  #define HDMI_FRAME_CTRL_INTERLACED_EN				0x80000000 +#define REG_HDMI_AUD_INT					0x000002cc +#define HDMI_AUD_INT_AUD_FIFO_URUN_INT				0x00000001 +#define HDMI_AUD_INT_AUD_FIFO_URAN_MASK				0x00000002 +#define HDMI_AUD_INT_AUD_SAM_DROP_INT				0x00000004 +#define HDMI_AUD_INT_AUD_SAM_DROP_MASK				0x00000008 +  #define REG_HDMI_PHY_CTRL					0x000002d4  #define HDMI_PHY_CTRL_SW_RESET_PLL				0x00000001  #define HDMI_PHY_CTRL_SW_RESET_PLL_LOW				0x00000002  #define HDMI_PHY_CTRL_SW_RESET					0x00000004  #define HDMI_PHY_CTRL_SW_RESET_LOW				0x00000008 -#define REG_HDMI_AUD_INT					0x000002cc -#define HDMI_AUD_INT_AUD_FIFO_URUN_INT				0x00000001 -#define HDMI_AUD_INT_AUD_FIFO_URAN_MASK				0x00000002 -#define HDMI_AUD_INT_AUD_SAM_DROP_INT				0x00000004 -#define HDMI_AUD_INT_AUD_SAM_DROP_MASK				0x00000008 +#define REG_HDMI_CEC_WR_RANGE					0x000002dc + +#define REG_HDMI_CEC_RD_RANGE					0x000002e0 + +#define REG_HDMI_VERSION					0x000002e4 + +#define REG_HDMI_CEC_COMPL_CTL					0x00000360 + +#define REG_HDMI_CEC_RD_START_RANGE				0x00000364 + +#define REG_HDMI_CEC_RD_TOTAL_RANGE				0x00000368 + +#define REG_HDMI_CEC_RD_ERR_RESP_LO				0x0000036c + +#define REG_HDMI_CEC_WR_CHECK_CONFIG				0x00000370  #define REG_HDMI_8x60_PHY_REG0					0x00000300  #define HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__MASK			0x0000001c @@ -504,5 +553,23 @@ static inline uint32_t HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(uint32_t val)  #define REG_HDMI_8960_PHY_REG12					0x00000430 +#define REG_HDMI_8x74_ANA_CFG0					0x00000000 + +#define REG_HDMI_8x74_ANA_CFG1					0x00000004 + +#define REG_HDMI_8x74_PD_CTRL0					0x00000010 + +#define REG_HDMI_8x74_PD_CTRL1					0x00000014 + +#define REG_HDMI_8x74_BIST_CFG0					0x00000034 + +#define REG_HDMI_8x74_BIST_PATN0				0x0000003c + +#define REG_HDMI_8x74_BIST_PATN1				0x00000040 + +#define REG_HDMI_8x74_BIST_PATN2				0x00000044 + +#define REG_HDMI_8x74_BIST_PATN3				0x00000048 +  #endif /* HDMI_XML */ diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c new file mode 100644 index 00000000000..872485f6013 --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/hdmi.h> +#include "hdmi.h" + + +/* Supported HDMI Audio channels */ +#define MSM_HDMI_AUDIO_CHANNEL_2		0 +#define MSM_HDMI_AUDIO_CHANNEL_4		1 +#define MSM_HDMI_AUDIO_CHANNEL_6		2 +#define MSM_HDMI_AUDIO_CHANNEL_8		3 + +/* maps MSM_HDMI_AUDIO_CHANNEL_n consts used by audio driver to # of channels: */ +static int nchannels[] = { 2, 4, 6, 8 }; + +/* Supported HDMI Audio sample rates */ +#define MSM_HDMI_SAMPLE_RATE_32KHZ		0 +#define MSM_HDMI_SAMPLE_RATE_44_1KHZ		1 +#define MSM_HDMI_SAMPLE_RATE_48KHZ		2 +#define MSM_HDMI_SAMPLE_RATE_88_2KHZ		3 +#define MSM_HDMI_SAMPLE_RATE_96KHZ		4 +#define MSM_HDMI_SAMPLE_RATE_176_4KHZ		5 +#define MSM_HDMI_SAMPLE_RATE_192KHZ		6 +#define MSM_HDMI_SAMPLE_RATE_MAX		7 + + +struct hdmi_msm_audio_acr { +	uint32_t n;	/* N parameter for clock regeneration */ +	uint32_t cts;	/* CTS parameter for clock regeneration */ +}; + +struct hdmi_msm_audio_arcs { +	unsigned long int pixclock; +	struct hdmi_msm_audio_acr lut[MSM_HDMI_SAMPLE_RATE_MAX]; +}; + +#define HDMI_MSM_AUDIO_ARCS(pclk, ...) { (1000 * (pclk)), __VA_ARGS__ } + +/* Audio constants lookup table for hdmi_msm_audio_acr_setup */ +/* Valid Pixel-Clock rates: 25.2MHz, 27MHz, 27.03MHz, 74.25MHz, 148.5MHz */ +static const struct hdmi_msm_audio_arcs acr_lut[] = { +	/*  25.200MHz  */ +	HDMI_MSM_AUDIO_ARCS(25200, { +		{4096, 25200}, {6272, 28000}, {6144, 25200}, {12544, 28000}, +		{12288, 25200}, {25088, 28000}, {24576, 25200} }), +	/*  27.000MHz  */ +	HDMI_MSM_AUDIO_ARCS(27000, { +		{4096, 27000}, {6272, 30000}, {6144, 27000}, {12544, 30000}, +		{12288, 27000}, {25088, 30000}, {24576, 27000} }), +	/*  27.027MHz */ +	HDMI_MSM_AUDIO_ARCS(27030, { +		{4096, 27027}, {6272, 30030}, {6144, 27027}, {12544, 30030}, +		{12288, 27027}, {25088, 30030}, {24576, 27027} }), +	/*  74.250MHz */ +	HDMI_MSM_AUDIO_ARCS(74250, { +		{4096, 74250}, {6272, 82500}, {6144, 74250}, {12544, 82500}, +		{12288, 74250}, {25088, 82500}, {24576, 74250} }), +	/* 148.500MHz */ +	HDMI_MSM_AUDIO_ARCS(148500, { +		{4096, 148500}, {6272, 165000}, {6144, 148500}, {12544, 165000}, +		{12288, 148500}, {25088, 165000}, {24576, 148500} }), +}; + +static const struct hdmi_msm_audio_arcs *get_arcs(unsigned long int pixclock) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(acr_lut); i++) { +		const struct hdmi_msm_audio_arcs *arcs = &acr_lut[i]; +		if (arcs->pixclock == pixclock) +			return arcs; +	} + +	return NULL; +} + +int hdmi_audio_update(struct hdmi *hdmi) +{ +	struct hdmi_audio *audio = &hdmi->audio; +	struct hdmi_audio_infoframe *info = &audio->infoframe; +	const struct hdmi_msm_audio_arcs *arcs = NULL; +	bool enabled = audio->enabled; +	uint32_t acr_pkt_ctrl, vbi_pkt_ctrl, aud_pkt_ctrl; +	uint32_t infofrm_ctrl, audio_config; + +	DBG("audio: enabled=%d, channels=%d, channel_allocation=0x%x, " +		"level_shift_value=%d, downmix_inhibit=%d, rate=%d", +		audio->enabled, info->channels,  info->channel_allocation, +		info->level_shift_value, info->downmix_inhibit, audio->rate); +	DBG("video: power_on=%d, pixclock=%lu", hdmi->power_on, hdmi->pixclock); + +	if (enabled && !(hdmi->power_on && hdmi->pixclock)) { +		DBG("disabling audio: no video"); +		enabled = false; +	} + +	if (enabled) { +		arcs = get_arcs(hdmi->pixclock); +		if (!arcs) { +			DBG("disabling audio: unsupported pixclock: %lu", +					hdmi->pixclock); +			enabled = false; +		} +	} + +	/* Read first before writing */ +	acr_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_ACR_PKT_CTRL); +	vbi_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_VBI_PKT_CTRL); +	aud_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_AUDIO_PKT_CTRL1); +	infofrm_ctrl = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0); +	audio_config = hdmi_read(hdmi, REG_HDMI_AUDIO_CFG); + +	/* Clear N/CTS selection bits */ +	acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_SELECT__MASK; + +	if (enabled) { +		uint32_t n, cts, multiplier; +		enum hdmi_acr_cts select; +		uint8_t buf[14]; + +		n   = arcs->lut[audio->rate].n; +		cts = arcs->lut[audio->rate].cts; + +		if ((MSM_HDMI_SAMPLE_RATE_192KHZ == audio->rate) || +				(MSM_HDMI_SAMPLE_RATE_176_4KHZ == audio->rate)) { +			multiplier = 4; +			n >>= 2; /* divide N by 4 and use multiplier */ +		} else if ((MSM_HDMI_SAMPLE_RATE_96KHZ == audio->rate) || +				(MSM_HDMI_SAMPLE_RATE_88_2KHZ == audio->rate)) { +			multiplier = 2; +			n >>= 1; /* divide N by 2 and use multiplier */ +		} else { +			multiplier = 1; +		} + +		DBG("n=%u, cts=%u, multiplier=%u", n, cts, multiplier); + +		acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SOURCE; +		acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_AUDIO_PRIORITY; +		acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_N_MULTIPLIER(multiplier); + +		if ((MSM_HDMI_SAMPLE_RATE_48KHZ == audio->rate) || +				(MSM_HDMI_SAMPLE_RATE_96KHZ == audio->rate) || +				(MSM_HDMI_SAMPLE_RATE_192KHZ == audio->rate)) +			select = ACR_48; +		else if ((MSM_HDMI_SAMPLE_RATE_44_1KHZ == audio->rate) || +				(MSM_HDMI_SAMPLE_RATE_88_2KHZ == audio->rate) || +				(MSM_HDMI_SAMPLE_RATE_176_4KHZ == audio->rate)) +			select = ACR_44; +		else /* default to 32k */ +			select = ACR_32; + +		acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SELECT(select); + +		hdmi_write(hdmi, REG_HDMI_ACR_0(select - 1), +				HDMI_ACR_0_CTS(cts)); +		hdmi_write(hdmi, REG_HDMI_ACR_1(select - 1), +				HDMI_ACR_1_N(n)); + +		hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL2, +				COND(info->channels != 2, HDMI_AUDIO_PKT_CTRL2_LAYOUT) | +				HDMI_AUDIO_PKT_CTRL2_OVERRIDE); + +		acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_CONT; +		acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SEND; + +		/* configure infoframe: */ +		hdmi_audio_infoframe_pack(info, buf, sizeof(buf)); +		hdmi_write(hdmi, REG_HDMI_AUDIO_INFO0, +				(buf[3] <<  0) || (buf[4] <<  8) || +				(buf[5] << 16) || (buf[6] << 24)); +		hdmi_write(hdmi, REG_HDMI_AUDIO_INFO1, +				(buf[7] <<  0) || (buf[8] << 8)); + +		hdmi_write(hdmi, REG_HDMI_GC, 0); + +		vbi_pkt_ctrl |= HDMI_VBI_PKT_CTRL_GC_ENABLE; +		vbi_pkt_ctrl |= HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME; + +		aud_pkt_ctrl |= HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND; + +		infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND; +		infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT; +		infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE; +		infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE; + +		audio_config &= ~HDMI_AUDIO_CFG_FIFO_WATERMARK__MASK; +		audio_config |= HDMI_AUDIO_CFG_FIFO_WATERMARK(4); +		audio_config |= HDMI_AUDIO_CFG_ENGINE_ENABLE; +	} else { +		hdmi_write(hdmi, REG_HDMI_GC, HDMI_GC_MUTE); +		acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_CONT; +		acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_SEND; +		vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_ENABLE; +		vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME; +		aud_pkt_ctrl &= ~HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND; +		infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND; +		infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT; +		infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE; +		infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE; +		audio_config &= ~HDMI_AUDIO_CFG_ENGINE_ENABLE; +	} + +	hdmi_write(hdmi, REG_HDMI_ACR_PKT_CTRL, acr_pkt_ctrl); +	hdmi_write(hdmi, REG_HDMI_VBI_PKT_CTRL, vbi_pkt_ctrl); +	hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL1, aud_pkt_ctrl); +	hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, infofrm_ctrl); + +	hdmi_write(hdmi, REG_HDMI_AUD_INT, +			COND(enabled, HDMI_AUD_INT_AUD_FIFO_URUN_INT) | +			COND(enabled, HDMI_AUD_INT_AUD_SAM_DROP_INT)); + +	hdmi_write(hdmi, REG_HDMI_AUDIO_CFG, audio_config); + + +	DBG("audio %sabled", enabled ? "en" : "dis"); + +	return 0; +} + +int hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled, +	uint32_t num_of_channels, uint32_t channel_allocation, +	uint32_t level_shift, bool down_mix) +{ +	struct hdmi_audio *audio; + +	if (!hdmi) +		return -ENXIO; + +	audio = &hdmi->audio; + +	if (num_of_channels >= ARRAY_SIZE(nchannels)) +		return -EINVAL; + +	audio->enabled = enabled; +	audio->infoframe.channels = nchannels[num_of_channels]; +	audio->infoframe.channel_allocation = channel_allocation; +	audio->infoframe.level_shift_value = level_shift; +	audio->infoframe.downmix_inhibit = down_mix; + +	return hdmi_audio_update(hdmi); +} + +void hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate) +{ +	struct hdmi_audio *audio; + +	if (!hdmi) +		return; + +	audio = &hdmi->audio; + +	if ((rate < 0) || (rate >= MSM_HDMI_SAMPLE_RATE_MAX)) +		return; + +	audio->rate = rate; +	hdmi_audio_update(hdmi); +} diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 5a8ee3473cf..f6cf745c249 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -19,10 +19,7 @@  struct hdmi_bridge {  	struct drm_bridge base; -  	struct hdmi *hdmi; - -	unsigned long int pixclock;  };  #define to_hdmi_bridge(x) container_of(x, struct hdmi_bridge, base) @@ -34,6 +31,65 @@ static void hdmi_bridge_destroy(struct drm_bridge *bridge)  	kfree(hdmi_bridge);  } +static void power_on(struct drm_bridge *bridge) +{ +	struct drm_device *dev = bridge->dev; +	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); +	struct hdmi *hdmi = hdmi_bridge->hdmi; +	const struct hdmi_platform_config *config = hdmi->config; +	int i, ret; + +	for (i = 0; i < config->pwr_reg_cnt; i++) { +		ret = regulator_enable(hdmi->pwr_regs[i]); +		if (ret) { +			dev_err(dev->dev, "failed to enable pwr regulator: %s (%d)\n", +					config->pwr_reg_names[i], ret); +		} +	} + +	if (config->pwr_clk_cnt > 0) { +		DBG("pixclock: %lu", hdmi->pixclock); +		ret = clk_set_rate(hdmi->pwr_clks[0], hdmi->pixclock); +		if (ret) { +			dev_err(dev->dev, "failed to set pixel clk: %s (%d)\n", +					config->pwr_clk_names[0], ret); +		} +	} + +	for (i = 0; i < config->pwr_clk_cnt; i++) { +		ret = clk_prepare_enable(hdmi->pwr_clks[i]); +		if (ret) { +			dev_err(dev->dev, "failed to enable pwr clk: %s (%d)\n", +					config->pwr_clk_names[i], ret); +		} +	} +} + +static void power_off(struct drm_bridge *bridge) +{ +	struct drm_device *dev = bridge->dev; +	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); +	struct hdmi *hdmi = hdmi_bridge->hdmi; +	const struct hdmi_platform_config *config = hdmi->config; +	int i, ret; + +	/* TODO do we need to wait for final vblank somewhere before +	 * cutting the clocks? +	 */ +	mdelay(16 + 4); + +	for (i = 0; i < config->pwr_clk_cnt; i++) +		clk_disable_unprepare(hdmi->pwr_clks[i]); + +	for (i = 0; i < config->pwr_reg_cnt; i++) { +		ret = regulator_disable(hdmi->pwr_regs[i]); +		if (ret) { +			dev_err(dev->dev, "failed to disable pwr regulator: %s (%d)\n", +					config->pwr_reg_names[i], ret); +		} +	} +} +  static void hdmi_bridge_pre_enable(struct drm_bridge *bridge)  {  	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); @@ -41,7 +97,14 @@ static void hdmi_bridge_pre_enable(struct drm_bridge *bridge)  	struct hdmi_phy *phy = hdmi->phy;  	DBG("power up"); -	phy->funcs->powerup(phy, hdmi_bridge->pixclock); + +	if (!hdmi->power_on) { +		power_on(bridge); +		hdmi->power_on = true; +		hdmi_audio_update(hdmi); +	} + +	phy->funcs->powerup(phy, hdmi->pixclock);  	hdmi_set_mode(hdmi, true);  } @@ -62,6 +125,12 @@ static void hdmi_bridge_post_disable(struct drm_bridge *bridge)  	DBG("power down");  	hdmi_set_mode(hdmi, false);  	phy->funcs->powerdown(phy); + +	if (hdmi->power_on) { +		power_off(bridge); +		hdmi->power_on = false; +		hdmi_audio_update(hdmi); +	}  }  static void hdmi_bridge_mode_set(struct drm_bridge *bridge, @@ -75,7 +144,7 @@ static void hdmi_bridge_mode_set(struct drm_bridge *bridge,  	mode = adjusted_mode; -	hdmi_bridge->pixclock = mode->clock * 1000; +	hdmi->pixclock = mode->clock * 1000;  	hdmi->hdmi_mode = drm_match_cea_mode(mode) > 1; @@ -123,9 +192,7 @@ static void hdmi_bridge_mode_set(struct drm_bridge *bridge,  	DBG("frame_ctrl=%08x", frame_ctrl);  	hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl); -	// TODO until we have audio, this might be safest: -	if (hdmi->hdmi_mode) -		hdmi_write(hdmi, REG_HDMI_GC, HDMI_GC_MUTE); +	hdmi_audio_update(hdmi);  }  static const struct drm_bridge_funcs hdmi_bridge_funcs = { diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c index 823eee521a3..28f7e3ec6c2 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c @@ -17,19 +17,20 @@  #include <linux/gpio.h> +#include "msm_kms.h"  #include "hdmi.h"  struct hdmi_connector {  	struct drm_connector base;  	struct hdmi *hdmi; +	struct work_struct hpd_work;  };  #define to_hdmi_connector(x) container_of(x, struct hdmi_connector, base)  static int gpio_config(struct hdmi *hdmi, bool on)  {  	struct drm_device *dev = hdmi->dev; -	struct hdmi_platform_config *config = -			hdmi->pdev->dev.platform_data; +	const struct hdmi_platform_config *config = hdmi->config;  	int ret;  	if (on) { @@ -39,26 +40,43 @@ static int gpio_config(struct hdmi *hdmi, bool on)  				"HDMI_DDC_CLK", config->ddc_clk_gpio, ret);  			goto error1;  		} +		gpio_set_value_cansleep(config->ddc_clk_gpio, 1); +  		ret = gpio_request(config->ddc_data_gpio, "HDMI_DDC_DATA");  		if (ret) {  			dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",  				"HDMI_DDC_DATA", config->ddc_data_gpio, ret);  			goto error2;  		} +		gpio_set_value_cansleep(config->ddc_data_gpio, 1); +  		ret = gpio_request(config->hpd_gpio, "HDMI_HPD");  		if (ret) {  			dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",  				"HDMI_HPD", config->hpd_gpio, ret);  			goto error3;  		} -		if (config->pmic_gpio != -1) { -			ret = gpio_request(config->pmic_gpio, "PMIC_HDMI_MUX_SEL"); +		gpio_direction_input(config->hpd_gpio); +		gpio_set_value_cansleep(config->hpd_gpio, 1); + +		if (config->mux_en_gpio != -1) { +			ret = gpio_request(config->mux_en_gpio, "HDMI_MUX_EN");  			if (ret) {  				dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n", -					"PMIC_HDMI_MUX_SEL", config->pmic_gpio, ret); +					"HDMI_MUX_SEL", config->mux_en_gpio, ret);  				goto error4;  			} -			gpio_set_value_cansleep(config->pmic_gpio, 0); +			gpio_set_value_cansleep(config->mux_en_gpio, 1); +		} + +		if (config->mux_sel_gpio != -1) { +			ret = gpio_request(config->mux_sel_gpio, "HDMI_MUX_SEL"); +			if (ret) { +				dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n", +					"HDMI_MUX_SEL", config->mux_sel_gpio, ret); +				goto error5; +			} +			gpio_set_value_cansleep(config->mux_sel_gpio, 0);  		}  		DBG("gpio on");  	} else { @@ -66,15 +84,23 @@ static int gpio_config(struct hdmi *hdmi, bool on)  		gpio_free(config->ddc_data_gpio);  		gpio_free(config->hpd_gpio); -		if (config->pmic_gpio != -1) { -			gpio_set_value_cansleep(config->pmic_gpio, 1); -			gpio_free(config->pmic_gpio); +		if (config->mux_en_gpio != -1) { +			gpio_set_value_cansleep(config->mux_en_gpio, 0); +			gpio_free(config->mux_en_gpio); +		} + +		if (config->mux_sel_gpio != -1) { +			gpio_set_value_cansleep(config->mux_sel_gpio, 1); +			gpio_free(config->mux_sel_gpio);  		}  		DBG("gpio off");  	}  	return 0; +error5: +	if (config->mux_en_gpio != -1) +		gpio_free(config->mux_en_gpio);  error4:  	gpio_free(config->hpd_gpio);  error3: @@ -88,10 +114,11 @@ error1:  static int hpd_enable(struct hdmi_connector *hdmi_connector)  {  	struct hdmi *hdmi = hdmi_connector->hdmi; +	const struct hdmi_platform_config *config = hdmi->config;  	struct drm_device *dev = hdmi_connector->base.dev;  	struct hdmi_phy *phy = hdmi->phy;  	uint32_t hpd_ctrl; -	int ret; +	int i, ret;  	ret = gpio_config(hdmi, true);  	if (ret) { @@ -99,31 +126,30 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector)  		goto fail;  	} -	ret = clk_prepare_enable(hdmi->clk); -	if (ret) { -		dev_err(dev->dev, "failed to enable 'clk': %d\n", ret); -		goto fail; -	} - -	ret = clk_prepare_enable(hdmi->m_pclk); -	if (ret) { -		dev_err(dev->dev, "failed to enable 'm_pclk': %d\n", ret); -		goto fail; -	} +	for (i = 0; i < config->hpd_clk_cnt; i++) { +		if (config->hpd_freq && config->hpd_freq[i]) { +			ret = clk_set_rate(hdmi->hpd_clks[i], +					config->hpd_freq[i]); +			if (ret) +				dev_warn(dev->dev, "failed to set clk %s (%d)\n", +						config->hpd_clk_names[i], ret); +		} -	ret = clk_prepare_enable(hdmi->s_pclk); -	if (ret) { -		dev_err(dev->dev, "failed to enable 's_pclk': %d\n", ret); -		goto fail; +		ret = clk_prepare_enable(hdmi->hpd_clks[i]); +		if (ret) { +			dev_err(dev->dev, "failed to enable hpd clk: %s (%d)\n", +					config->hpd_clk_names[i], ret); +			goto fail; +		}  	} -	if (hdmi->mpp0) -		ret = regulator_enable(hdmi->mpp0); -	if (!ret) -		ret = regulator_enable(hdmi->mvs); -	if (ret) { -		dev_err(dev->dev, "failed to enable regulators: %d\n", ret); -		goto fail; +	for (i = 0; i < config->hpd_reg_cnt; i++) { +		ret = regulator_enable(hdmi->hpd_regs[i]); +		if (ret) { +			dev_err(dev->dev, "failed to enable hpd regulator: %s (%d)\n", +					config->hpd_reg_names[i], ret); +			goto fail; +		}  	}  	hdmi_set_mode(hdmi, false); @@ -156,26 +182,26 @@ fail:  static int hdp_disable(struct hdmi_connector *hdmi_connector)  {  	struct hdmi *hdmi = hdmi_connector->hdmi; +	const struct hdmi_platform_config *config = hdmi->config;  	struct drm_device *dev = hdmi_connector->base.dev; -	int ret = 0; +	int i, ret = 0;  	/* Disable HPD interrupt */  	hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0);  	hdmi_set_mode(hdmi, false); -	if (hdmi->mpp0) -		ret = regulator_disable(hdmi->mpp0); -	if (!ret) -		ret = regulator_disable(hdmi->mvs); -	if (ret) { -		dev_err(dev->dev, "failed to enable regulators: %d\n", ret); -		goto fail; +	for (i = 0; i < config->hpd_reg_cnt; i++) { +		ret = regulator_disable(hdmi->hpd_regs[i]); +		if (ret) { +			dev_err(dev->dev, "failed to disable hpd regulator: %s (%d)\n", +					config->hpd_reg_names[i], ret); +			goto fail; +		}  	} -	clk_disable_unprepare(hdmi->clk); -	clk_disable_unprepare(hdmi->m_pclk); -	clk_disable_unprepare(hdmi->s_pclk); +	for (i = 0; i < config->hpd_clk_cnt; i++) +		clk_disable_unprepare(hdmi->hpd_clks[i]);  	ret = gpio_config(hdmi, false);  	if (ret) { @@ -189,9 +215,19 @@ fail:  	return ret;  } +static void +hotplug_work(struct work_struct *work) +{ +	struct hdmi_connector *hdmi_connector = +		container_of(work, struct hdmi_connector, hpd_work); +	struct drm_connector *connector = &hdmi_connector->base; +	drm_helper_hpd_irq_event(connector->dev); +} +  void hdmi_connector_irq(struct drm_connector *connector)  {  	struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector); +	struct msm_drm_private *priv = connector->dev->dev_private;  	struct hdmi *hdmi = hdmi_connector->hdmi;  	uint32_t hpd_int_status, hpd_int_ctrl; @@ -209,37 +245,59 @@ void hdmi_connector_irq(struct drm_connector *connector)  		hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,  				hpd_int_ctrl | HDMI_HPD_INT_CTRL_INT_ACK); -		drm_helper_hpd_irq_event(connector->dev); -  		/* detect disconnect if we are connected or visa versa: */  		hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN;  		if (!detected)  			hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;  		hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl); + +		queue_work(priv->wq, &hdmi_connector->hpd_work);  	}  } +static enum drm_connector_status detect_reg(struct hdmi *hdmi) +{ +	uint32_t hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS); +	return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ? +			connector_status_connected : connector_status_disconnected; +} + +static enum drm_connector_status detect_gpio(struct hdmi *hdmi) +{ +	const struct hdmi_platform_config *config = hdmi->config; +	return gpio_get_value(config->hpd_gpio) ? +			connector_status_connected : +			connector_status_disconnected; +} +  static enum drm_connector_status hdmi_connector_detect(  		struct drm_connector *connector, bool force)  {  	struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);  	struct hdmi *hdmi = hdmi_connector->hdmi; -	uint32_t hpd_int_status; +	enum drm_connector_status stat_gpio, stat_reg;  	int retry = 20; -	hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS); +	do { +		stat_gpio = detect_gpio(hdmi); +		stat_reg  = detect_reg(hdmi); + +		if (stat_gpio == stat_reg) +			break; -	/* sense seems to in some cases be momentarily de-asserted, don't -	 * let that trick us into thinking the monitor is gone: -	 */ -	while (retry-- && !(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED)) {  		mdelay(10); -		hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS); -		DBG("status=%08x", hpd_int_status); +	} while (--retry); + +	/* the status we get from reading gpio seems to be more reliable, +	 * so trust that one the most if we didn't manage to get hdmi and +	 * gpio status to agree: +	 */ +	if (stat_gpio != stat_reg) { +		DBG("HDMI_HPD_INT_STATUS tells us: %d", stat_reg); +		DBG("hpd gpio tells us: %d", stat_gpio);  	} -	return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ? -			connector_status_connected : connector_status_disconnected; +	return stat_gpio;  }  static void hdmi_connector_destroy(struct drm_connector *connector) @@ -285,6 +343,8 @@ static int hdmi_connector_mode_valid(struct drm_connector *connector,  				 struct drm_display_mode *mode)  {  	struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector); +	struct hdmi *hdmi = hdmi_connector->hdmi; +	const struct hdmi_platform_config *config = hdmi->config;  	struct msm_drm_private *priv = connector->dev->dev_private;  	struct msm_kms *kms = priv->kms;  	long actual, requested; @@ -293,6 +353,13 @@ static int hdmi_connector_mode_valid(struct drm_connector *connector,  	actual = kms->funcs->round_pixclk(kms,  			requested, hdmi_connector->hdmi->encoder); +	/* for mdp5/apq8074, we manage our own pixel clk (as opposed to +	 * mdp4/dtv stuff where pixel clk is assigned to mdp/encoder +	 * instead): +	 */ +	if (config->pwr_clk_cnt > 0) +		actual = clk_round_rate(hdmi->pwr_clks[0], actual); +  	DBG("requested=%ld, actual=%ld", requested, actual);  	if (actual != requested) @@ -335,6 +402,7 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi)  	}  	hdmi_connector->hdmi = hdmi_reference(hdmi); +	INIT_WORK(&hdmi_connector->hpd_work, hotplug_work);  	connector = &hdmi_connector->base; @@ -342,7 +410,8 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi)  			DRM_MODE_CONNECTOR_HDMIA);  	drm_connector_helper_add(connector, &hdmi_connector_helper_funcs); -	connector->polled = DRM_CONNECTOR_POLL_HPD; +	connector->polled = DRM_CONNECTOR_POLL_CONNECT | +			DRM_CONNECTOR_POLL_DISCONNECT;  	connector->interlace_allowed = 1;  	connector->doublescan_allowed = 0; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c new file mode 100644 index 00000000000..59fa6cdacb2 --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "hdmi.h" + +struct hdmi_phy_8x74 { +	struct hdmi_phy base; +	struct hdmi *hdmi; +	void __iomem *mmio; +}; +#define to_hdmi_phy_8x74(x) container_of(x, struct hdmi_phy_8x74, base) + + +static void phy_write(struct hdmi_phy_8x74 *phy, u32 reg, u32 data) +{ +	msm_writel(data, phy->mmio + reg); +} + +//static u32 phy_read(struct hdmi_phy_8x74 *phy, u32 reg) +//{ +//	return msm_readl(phy->mmio + reg); +//} + +static void hdmi_phy_8x74_destroy(struct hdmi_phy *phy) +{ +	struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy); +	kfree(phy_8x74); +} + +static void hdmi_phy_8x74_reset(struct hdmi_phy *phy) +{ +	struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy); +	struct hdmi *hdmi = phy_8x74->hdmi; +	unsigned int val; + +	/* NOTE that HDMI_PHY_CTL is in core mmio, not phy mmio: */ + +	val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL); + +	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) { +		/* pull low */ +		hdmi_write(hdmi, REG_HDMI_PHY_CTRL, +				val & ~HDMI_PHY_CTRL_SW_RESET); +	} else { +		/* pull high */ +		hdmi_write(hdmi, REG_HDMI_PHY_CTRL, +				val | HDMI_PHY_CTRL_SW_RESET); +	} + +	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) { +		/* pull low */ +		hdmi_write(hdmi, REG_HDMI_PHY_CTRL, +				val & ~HDMI_PHY_CTRL_SW_RESET_PLL); +	} else { +		/* pull high */ +		hdmi_write(hdmi, REG_HDMI_PHY_CTRL, +				val | HDMI_PHY_CTRL_SW_RESET_PLL); +	} + +	msleep(100); + +	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) { +		/* pull high */ +		hdmi_write(hdmi, REG_HDMI_PHY_CTRL, +				val | HDMI_PHY_CTRL_SW_RESET); +	} else { +		/* pull low */ +		hdmi_write(hdmi, REG_HDMI_PHY_CTRL, +				val & ~HDMI_PHY_CTRL_SW_RESET); +	} + +	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) { +		/* pull high */ +		hdmi_write(hdmi, REG_HDMI_PHY_CTRL, +				val | HDMI_PHY_CTRL_SW_RESET_PLL); +	} else { +		/* pull low */ +		hdmi_write(hdmi, REG_HDMI_PHY_CTRL, +				val & ~HDMI_PHY_CTRL_SW_RESET_PLL); +	} +} + +static void hdmi_phy_8x74_powerup(struct hdmi_phy *phy, +		unsigned long int pixclock) +{ +	struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy); + +	phy_write(phy_8x74, REG_HDMI_8x74_ANA_CFG0,   0x1b); +	phy_write(phy_8x74, REG_HDMI_8x74_ANA_CFG1,   0xf2); +	phy_write(phy_8x74, REG_HDMI_8x74_BIST_CFG0,  0x0); +	phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN0, 0x0); +	phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN1, 0x0); +	phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN2, 0x0); +	phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN3, 0x0); +	phy_write(phy_8x74, REG_HDMI_8x74_PD_CTRL1,   0x20); +} + +static void hdmi_phy_8x74_powerdown(struct hdmi_phy *phy) +{ +	struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy); +	phy_write(phy_8x74, REG_HDMI_8x74_PD_CTRL0, 0x7f); +} + +static const struct hdmi_phy_funcs hdmi_phy_8x74_funcs = { +		.destroy = hdmi_phy_8x74_destroy, +		.reset = hdmi_phy_8x74_reset, +		.powerup = hdmi_phy_8x74_powerup, +		.powerdown = hdmi_phy_8x74_powerdown, +}; + +struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi) +{ +	struct hdmi_phy_8x74 *phy_8x74; +	struct hdmi_phy *phy = NULL; +	int ret; + +	phy_8x74 = kzalloc(sizeof(*phy_8x74), GFP_KERNEL); +	if (!phy_8x74) { +		ret = -ENOMEM; +		goto fail; +	} + +	phy = &phy_8x74->base; + +	phy->funcs = &hdmi_phy_8x74_funcs; + +	phy_8x74->hdmi = hdmi; + +	/* for 8x74, the phy mmio is mapped separately: */ +	phy_8x74->mmio = msm_ioremap(hdmi->pdev, +			"phy_physical", "HDMI_8x74"); +	if (IS_ERR(phy_8x74->mmio)) { +		ret = PTR_ERR(phy_8x74->mmio); +		goto fail; +	} + +	return phy; + +fail: +	if (phy) +		hdmi_phy_8x74_destroy(phy); +	return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/msm/hdmi/qfprom.xml.h b/drivers/gpu/drm/msm/hdmi/qfprom.xml.h index bee36363bcd..d591567173c 100644 --- a/drivers/gpu/drm/msm/hdmi/qfprom.xml.h +++ b/drivers/gpu/drm/msm/hdmi/qfprom.xml.h @@ -4,18 +4,20 @@  /* Autogenerated file, DO NOT EDIT manually!  This file was generated by the rules-ng-ng headergen tool in this git repository: -http://0x04.net/cgit/index.cgi/rules-ng-ng -git clone git://0x04.net/rules-ng-ng +http://github.com/freedreno/envytools/ +git clone https://github.com/freedreno/envytools.git  The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    595 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    647 bytes, from 2013-11-30 14:45:35)  - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-08-16 22:16:36) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml            (  17996 bytes, from 2013-12-01 19:10:31) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml      (   1615 bytes, from 2013-11-30 15:00:52) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml            (  22517 bytes, from 2013-12-03 20:59:13)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05)  - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml         (    600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  19288 bytes, from 2013-08-11 18:14:15) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  20932 bytes, from 2013-12-01 15:13:04)  Copyright (C) 2013 by the following authors:  - Rob Clark <robdclark@gmail.com> (robclark) diff --git a/drivers/gpu/drm/msm/mdp4/mdp4.xml.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h index bbeeebe2db5..416a26e1e58 100644 --- a/drivers/gpu/drm/msm/mdp4/mdp4.xml.h +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h @@ -4,18 +4,20 @@  /* Autogenerated file, DO NOT EDIT manually!  This file was generated by the rules-ng-ng headergen tool in this git repository: -http://0x04.net/cgit/index.cgi/rules-ng-ng -git clone git://0x04.net/rules-ng-ng +http://github.com/freedreno/envytools/ +git clone https://github.com/freedreno/envytools.git  The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    595 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    647 bytes, from 2013-11-30 14:45:35)  - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml           (  19332 bytes, from 2013-08-16 22:16:36) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml            (  17996 bytes, from 2013-12-01 19:10:31) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml      (   1615 bytes, from 2013-11-30 15:00:52) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml            (  22517 bytes, from 2013-12-03 20:59:13)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)  - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05)  - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml         (    600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  19288 bytes, from 2013-08-11 18:14:15) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  20932 bytes, from 2013-12-01 15:13:04)  Copyright (C) 2013 by the following authors:  - Rob Clark <robdclark@gmail.com> (robclark) @@ -42,28 +44,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */ -enum mpd4_bpc { -	BPC1 = 0, -	BPC5 = 1, -	BPC6 = 2, -	BPC8 = 3, -}; - -enum mpd4_bpc_alpha { -	BPC1A = 0, -	BPC4A = 1, -	BPC6A = 2, -	BPC8A = 3, -}; - -enum mpd4_alpha_type { -	FG_CONST = 0, -	BG_CONST = 1, -	FG_PIXEL = 2, -	BG_PIXEL = 3, -}; - -enum mpd4_pipe { +enum mdp4_pipe {  	VG1 = 0,  	VG2 = 1,  	RGB1 = 2, @@ -73,21 +54,12 @@ enum mpd4_pipe {  	VG4 = 6,  }; -enum mpd4_mixer { +enum mdp4_mixer {  	MIXER0 = 0,  	MIXER1 = 1,  	MIXER2 = 2,  }; -enum mpd4_mixer_stage_id { -	STAGE_UNUSED = 0, -	STAGE_BASE = 1, -	STAGE0 = 2, -	STAGE1 = 3, -	STAGE2 = 4, -	STAGE3 = 5, -}; -  enum mdp4_intf {  	INTF_LCDC_DTV = 0,  	INTF_DSI_VIDEO = 1, @@ -194,56 +166,56 @@ static inline uint32_t MDP4_DISP_INTF_SEL_EXT(enum mdp4_intf val)  #define REG_MDP4_LAYERMIXER2_IN_CFG				0x000100f0  #define MDP4_LAYERMIXER2_IN_CFG_PIPE0__MASK			0x00000007  #define MDP4_LAYERMIXER2_IN_CFG_PIPE0__SHIFT			0 -static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE0(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE0(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE0__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE0__MASK;  }  #define MDP4_LAYERMIXER2_IN_CFG_PIPE0_MIXER1			0x00000008  #define MDP4_LAYERMIXER2_IN_CFG_PIPE1__MASK			0x00000070  #define MDP4_LAYERMIXER2_IN_CFG_PIPE1__SHIFT			4 -static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE1(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE1(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE1__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE1__MASK;  }  #define MDP4_LAYERMIXER2_IN_CFG_PIPE1_MIXER1			0x00000080  #define MDP4_LAYERMIXER2_IN_CFG_PIPE2__MASK			0x00000700  #define MDP4_LAYERMIXER2_IN_CFG_PIPE2__SHIFT			8 -static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE2(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE2(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE2__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE2__MASK;  }  #define MDP4_LAYERMIXER2_IN_CFG_PIPE2_MIXER1			0x00000800  #define MDP4_LAYERMIXER2_IN_CFG_PIPE3__MASK			0x00007000  #define MDP4_LAYERMIXER2_IN_CFG_PIPE3__SHIFT			12 -static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE3(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE3(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE3__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE3__MASK;  }  #define MDP4_LAYERMIXER2_IN_CFG_PIPE3_MIXER1			0x00008000  #define MDP4_LAYERMIXER2_IN_CFG_PIPE4__MASK			0x00070000  #define MDP4_LAYERMIXER2_IN_CFG_PIPE4__SHIFT			16 -static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE4(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE4(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE4__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE4__MASK;  }  #define MDP4_LAYERMIXER2_IN_CFG_PIPE4_MIXER1			0x00080000  #define MDP4_LAYERMIXER2_IN_CFG_PIPE5__MASK			0x00700000  #define MDP4_LAYERMIXER2_IN_CFG_PIPE5__SHIFT			20 -static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE5(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE5(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE5__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE5__MASK;  }  #define MDP4_LAYERMIXER2_IN_CFG_PIPE5_MIXER1			0x00800000  #define MDP4_LAYERMIXER2_IN_CFG_PIPE6__MASK			0x07000000  #define MDP4_LAYERMIXER2_IN_CFG_PIPE6__SHIFT			24 -static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE6(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE6(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE6__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE6__MASK;  }  #define MDP4_LAYERMIXER2_IN_CFG_PIPE6_MIXER1			0x08000000  #define MDP4_LAYERMIXER2_IN_CFG_PIPE7__MASK			0x70000000  #define MDP4_LAYERMIXER2_IN_CFG_PIPE7__SHIFT			28 -static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE7(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE7(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE7__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE7__MASK;  } @@ -254,56 +226,56 @@ static inline uint32_t MDP4_LAYERMIXER2_IN_CFG_PIPE7(enum mpd4_mixer_stage_id va  #define REG_MDP4_LAYERMIXER_IN_CFG				0x00010100  #define MDP4_LAYERMIXER_IN_CFG_PIPE0__MASK			0x00000007  #define MDP4_LAYERMIXER_IN_CFG_PIPE0__SHIFT			0 -static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE0(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE0(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE0__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE0__MASK;  }  #define MDP4_LAYERMIXER_IN_CFG_PIPE0_MIXER1			0x00000008  #define MDP4_LAYERMIXER_IN_CFG_PIPE1__MASK			0x00000070  #define MDP4_LAYERMIXER_IN_CFG_PIPE1__SHIFT			4 -static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE1(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE1(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE1__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE1__MASK;  }  #define MDP4_LAYERMIXER_IN_CFG_PIPE1_MIXER1			0x00000080  #define MDP4_LAYERMIXER_IN_CFG_PIPE2__MASK			0x00000700  #define MDP4_LAYERMIXER_IN_CFG_PIPE2__SHIFT			8 -static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE2(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE2(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE2__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE2__MASK;  }  #define MDP4_LAYERMIXER_IN_CFG_PIPE2_MIXER1			0x00000800  #define MDP4_LAYERMIXER_IN_CFG_PIPE3__MASK			0x00007000  #define MDP4_LAYERMIXER_IN_CFG_PIPE3__SHIFT			12 -static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE3(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE3(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE3__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE3__MASK;  }  #define MDP4_LAYERMIXER_IN_CFG_PIPE3_MIXER1			0x00008000  #define MDP4_LAYERMIXER_IN_CFG_PIPE4__MASK			0x00070000  #define MDP4_LAYERMIXER_IN_CFG_PIPE4__SHIFT			16 -static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE4(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE4(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE4__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE4__MASK;  }  #define MDP4_LAYERMIXER_IN_CFG_PIPE4_MIXER1			0x00080000  #define MDP4_LAYERMIXER_IN_CFG_PIPE5__MASK			0x00700000  #define MDP4_LAYERMIXER_IN_CFG_PIPE5__SHIFT			20 -static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE5(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE5(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE5__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE5__MASK;  }  #define MDP4_LAYERMIXER_IN_CFG_PIPE5_MIXER1			0x00800000  #define MDP4_LAYERMIXER_IN_CFG_PIPE6__MASK			0x07000000  #define MDP4_LAYERMIXER_IN_CFG_PIPE6__SHIFT			24 -static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE6(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE6(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE6__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE6__MASK;  }  #define MDP4_LAYERMIXER_IN_CFG_PIPE6_MIXER1			0x08000000  #define MDP4_LAYERMIXER_IN_CFG_PIPE7__MASK			0x70000000  #define MDP4_LAYERMIXER_IN_CFG_PIPE7__SHIFT			28 -static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE7(enum mpd4_mixer_stage_id val) +static inline uint32_t MDP4_LAYERMIXER_IN_CFG_PIPE7(enum mdp_mixer_stage_id val)  {  	return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE7__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE7__MASK;  } @@ -369,7 +341,7 @@ static inline uint32_t REG_MDP4_OVLP_STAGE(uint32_t i0, uint32_t i1) { return 0x  static inline uint32_t REG_MDP4_OVLP_STAGE_OP(uint32_t i0, uint32_t i1) { return 0x00000000 + __offset_OVLP(i0) + __offset_STAGE(i1); }  #define MDP4_OVLP_STAGE_OP_FG_ALPHA__MASK			0x00000003  #define MDP4_OVLP_STAGE_OP_FG_ALPHA__SHIFT			0 -static inline uint32_t MDP4_OVLP_STAGE_OP_FG_ALPHA(enum mpd4_alpha_type val) +static inline uint32_t MDP4_OVLP_STAGE_OP_FG_ALPHA(enum mdp_alpha_type val)  {  	return ((val) << MDP4_OVLP_STAGE_OP_FG_ALPHA__SHIFT) & MDP4_OVLP_STAGE_OP_FG_ALPHA__MASK;  } @@ -377,7 +349,7 @@ static inline uint32_t MDP4_OVLP_STAGE_OP_FG_ALPHA(enum mpd4_alpha_type val)  #define MDP4_OVLP_STAGE_OP_FG_MOD_ALPHA				0x00000008  #define MDP4_OVLP_STAGE_OP_BG_ALPHA__MASK			0x00000030  #define MDP4_OVLP_STAGE_OP_BG_ALPHA__SHIFT			4 -static inline uint32_t MDP4_OVLP_STAGE_OP_BG_ALPHA(enum mpd4_alpha_type val) +static inline uint32_t MDP4_OVLP_STAGE_OP_BG_ALPHA(enum mdp_alpha_type val)  {  	return ((val) << MDP4_OVLP_STAGE_OP_BG_ALPHA__SHIFT) & MDP4_OVLP_STAGE_OP_BG_ALPHA__MASK;  } @@ -472,19 +444,19 @@ static inline uint32_t REG_MDP4_DMA(enum mdp4_dma i0) { return 0x00000000 + __of  static inline uint32_t REG_MDP4_DMA_CONFIG(enum mdp4_dma i0) { return 0x00000000 + __offset_DMA(i0); }  #define MDP4_DMA_CONFIG_G_BPC__MASK				0x00000003  #define MDP4_DMA_CONFIG_G_BPC__SHIFT				0 -static inline uint32_t MDP4_DMA_CONFIG_G_BPC(enum mpd4_bpc val) +static inline uint32_t MDP4_DMA_CONFIG_G_BPC(enum mdp_bpc val)  {  	return ((val) << MDP4_DMA_CONFIG_G_BPC__SHIFT) & MDP4_DMA_CONFIG_G_BPC__MASK;  }  #define MDP4_DMA_CONFIG_B_BPC__MASK				0x0000000c  #define MDP4_DMA_CONFIG_B_BPC__SHIFT				2 -static inline uint32_t MDP4_DMA_CONFIG_B_BPC(enum mpd4_bpc val) +static inline uint32_t MDP4_DMA_CONFIG_B_BPC(enum mdp_bpc val)  {  	return ((val) << MDP4_DMA_CONFIG_B_BPC__SHIFT) & MDP4_DMA_CONFIG_B_BPC__MASK;  }  #define MDP4_DMA_CONFIG_R_BPC__MASK				0x00000030  #define MDP4_DMA_CONFIG_R_BPC__SHIFT				4 -static inline uint32_t MDP4_DMA_CONFIG_R_BPC(enum mpd4_bpc val) +static inline uint32_t MDP4_DMA_CONFIG_R_BPC(enum mdp_bpc val)  {  	return ((val) << MDP4_DMA_CONFIG_R_BPC__SHIFT) & MDP4_DMA_CONFIG_R_BPC__MASK;  } @@ -601,9 +573,9 @@ static inline uint32_t REG_MDP4_DMA_CSC_POST_LV(enum mdp4_dma i0, uint32_t i1) {  static inline uint32_t REG_MDP4_DMA_CSC_POST_LV_VAL(enum mdp4_dma i0, uint32_t i1) { return 0x00003680 + __offset_DMA(i0) + 0x4*i1; } -static inline uint32_t REG_MDP4_PIPE(enum mpd4_pipe i0) { return 0x00020000 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE(enum mdp4_pipe i0) { return 0x00020000 + 0x10000*i0; } -static inline uint32_t REG_MDP4_PIPE_SRC_SIZE(enum mpd4_pipe i0) { return 0x00020000 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_SRC_SIZE(enum mdp4_pipe i0) { return 0x00020000 + 0x10000*i0; }  #define MDP4_PIPE_SRC_SIZE_HEIGHT__MASK				0xffff0000  #define MDP4_PIPE_SRC_SIZE_HEIGHT__SHIFT			16  static inline uint32_t MDP4_PIPE_SRC_SIZE_HEIGHT(uint32_t val) @@ -617,7 +589,7 @@ static inline uint32_t MDP4_PIPE_SRC_SIZE_WIDTH(uint32_t val)  	return ((val) << MDP4_PIPE_SRC_SIZE_WIDTH__SHIFT) & MDP4_PIPE_SRC_SIZE_WIDTH__MASK;  } -static inline uint32_t REG_MDP4_PIPE_SRC_XY(enum mpd4_pipe i0) { return 0x00020004 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_SRC_XY(enum mdp4_pipe i0) { return 0x00020004 + 0x10000*i0; }  #define MDP4_PIPE_SRC_XY_Y__MASK				0xffff0000  #define MDP4_PIPE_SRC_XY_Y__SHIFT				16  static inline uint32_t MDP4_PIPE_SRC_XY_Y(uint32_t val) @@ -631,7 +603,7 @@ static inline uint32_t MDP4_PIPE_SRC_XY_X(uint32_t val)  	return ((val) << MDP4_PIPE_SRC_XY_X__SHIFT) & MDP4_PIPE_SRC_XY_X__MASK;  } -static inline uint32_t REG_MDP4_PIPE_DST_SIZE(enum mpd4_pipe i0) { return 0x00020008 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_DST_SIZE(enum mdp4_pipe i0) { return 0x00020008 + 0x10000*i0; }  #define MDP4_PIPE_DST_SIZE_HEIGHT__MASK				0xffff0000  #define MDP4_PIPE_DST_SIZE_HEIGHT__SHIFT			16  static inline uint32_t MDP4_PIPE_DST_SIZE_HEIGHT(uint32_t val) @@ -645,7 +617,7 @@ static inline uint32_t MDP4_PIPE_DST_SIZE_WIDTH(uint32_t val)  	return ((val) << MDP4_PIPE_DST_SIZE_WIDTH__SHIFT) & MDP4_PIPE_DST_SIZE_WIDTH__MASK;  } -static inline uint32_t REG_MDP4_PIPE_DST_XY(enum mpd4_pipe i0) { return 0x0002000c + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_DST_XY(enum mdp4_pipe i0) { return 0x0002000c + 0x10000*i0; }  #define MDP4_PIPE_DST_XY_Y__MASK				0xffff0000  #define MDP4_PIPE_DST_XY_Y__SHIFT				16  static inline uint32_t MDP4_PIPE_DST_XY_Y(uint32_t val) @@ -659,13 +631,13 @@ static inline uint32_t MDP4_PIPE_DST_XY_X(uint32_t val)  	return ((val) << MDP4_PIPE_DST_XY_X__SHIFT) & MDP4_PIPE_DST_XY_X__MASK;  } -static inline uint32_t REG_MDP4_PIPE_SRCP0_BASE(enum mpd4_pipe i0) { return 0x00020010 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_SRCP0_BASE(enum mdp4_pipe i0) { return 0x00020010 + 0x10000*i0; } -static inline uint32_t REG_MDP4_PIPE_SRCP1_BASE(enum mpd4_pipe i0) { return 0x00020014 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_SRCP1_BASE(enum mdp4_pipe i0) { return 0x00020014 + 0x10000*i0; } -static inline uint32_t REG_MDP4_PIPE_SRCP2_BASE(enum mpd4_pipe i0) { return 0x00020018 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_SRCP2_BASE(enum mdp4_pipe i0) { return 0x00020018 + 0x10000*i0; } -static inline uint32_t REG_MDP4_PIPE_SRC_STRIDE_A(enum mpd4_pipe i0) { return 0x00020040 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_SRC_STRIDE_A(enum mdp4_pipe i0) { return 0x00020040 + 0x10000*i0; }  #define MDP4_PIPE_SRC_STRIDE_A_P0__MASK				0x0000ffff  #define MDP4_PIPE_SRC_STRIDE_A_P0__SHIFT			0  static inline uint32_t MDP4_PIPE_SRC_STRIDE_A_P0(uint32_t val) @@ -679,7 +651,7 @@ static inline uint32_t MDP4_PIPE_SRC_STRIDE_A_P1(uint32_t val)  	return ((val) << MDP4_PIPE_SRC_STRIDE_A_P1__SHIFT) & MDP4_PIPE_SRC_STRIDE_A_P1__MASK;  } -static inline uint32_t REG_MDP4_PIPE_SRC_STRIDE_B(enum mpd4_pipe i0) { return 0x00020044 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_SRC_STRIDE_B(enum mdp4_pipe i0) { return 0x00020044 + 0x10000*i0; }  #define MDP4_PIPE_SRC_STRIDE_B_P2__MASK				0x0000ffff  #define MDP4_PIPE_SRC_STRIDE_B_P2__SHIFT			0  static inline uint32_t MDP4_PIPE_SRC_STRIDE_B_P2(uint32_t val) @@ -693,7 +665,7 @@ static inline uint32_t MDP4_PIPE_SRC_STRIDE_B_P3(uint32_t val)  	return ((val) << MDP4_PIPE_SRC_STRIDE_B_P3__SHIFT) & MDP4_PIPE_SRC_STRIDE_B_P3__MASK;  } -static inline uint32_t REG_MDP4_PIPE_FRAME_SIZE(enum mpd4_pipe i0) { return 0x00020048 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_FRAME_SIZE(enum mdp4_pipe i0) { return 0x00020048 + 0x10000*i0; }  #define MDP4_PIPE_FRAME_SIZE_HEIGHT__MASK			0xffff0000  #define MDP4_PIPE_FRAME_SIZE_HEIGHT__SHIFT			16  static inline uint32_t MDP4_PIPE_FRAME_SIZE_HEIGHT(uint32_t val) @@ -707,28 +679,28 @@ static inline uint32_t MDP4_PIPE_FRAME_SIZE_WIDTH(uint32_t val)  	return ((val) << MDP4_PIPE_FRAME_SIZE_WIDTH__SHIFT) & MDP4_PIPE_FRAME_SIZE_WIDTH__MASK;  } -static inline uint32_t REG_MDP4_PIPE_SRC_FORMAT(enum mpd4_pipe i0) { return 0x00020050 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_SRC_FORMAT(enum mdp4_pipe i0) { return 0x00020050 + 0x10000*i0; }  #define MDP4_PIPE_SRC_FORMAT_G_BPC__MASK			0x00000003  #define MDP4_PIPE_SRC_FORMAT_G_BPC__SHIFT			0 -static inline uint32_t MDP4_PIPE_SRC_FORMAT_G_BPC(enum mpd4_bpc val) +static inline uint32_t MDP4_PIPE_SRC_FORMAT_G_BPC(enum mdp_bpc val)  {  	return ((val) << MDP4_PIPE_SRC_FORMAT_G_BPC__SHIFT) & MDP4_PIPE_SRC_FORMAT_G_BPC__MASK;  }  #define MDP4_PIPE_SRC_FORMAT_B_BPC__MASK			0x0000000c  #define MDP4_PIPE_SRC_FORMAT_B_BPC__SHIFT			2 -static inline uint32_t MDP4_PIPE_SRC_FORMAT_B_BPC(enum mpd4_bpc val) +static inline uint32_t MDP4_PIPE_SRC_FORMAT_B_BPC(enum mdp_bpc val)  {  	return ((val) << MDP4_PIPE_SRC_FORMAT_B_BPC__SHIFT) & MDP4_PIPE_SRC_FORMAT_B_BPC__MASK;  }  #define MDP4_PIPE_SRC_FORMAT_R_BPC__MASK			0x00000030  #define MDP4_PIPE_SRC_FORMAT_R_BPC__SHIFT			4 -static inline uint32_t MDP4_PIPE_SRC_FORMAT_R_BPC(enum mpd4_bpc val) +static inline uint32_t MDP4_PIPE_SRC_FORMAT_R_BPC(enum mdp_bpc val)  {  	return ((val) << MDP4_PIPE_SRC_FORMAT_R_BPC__SHIFT) & MDP4_PIPE_SRC_FORMAT_R_BPC__MASK;  }  #define MDP4_PIPE_SRC_FORMAT_A_BPC__MASK			0x000000c0  #define MDP4_PIPE_SRC_FORMAT_A_BPC__SHIFT			6 -static inline uint32_t MDP4_PIPE_SRC_FORMAT_A_BPC(enum mpd4_bpc_alpha val) +static inline uint32_t MDP4_PIPE_SRC_FORMAT_A_BPC(enum mdp_bpc_alpha val)  {  	return ((val) << MDP4_PIPE_SRC_FORMAT_A_BPC__SHIFT) & MDP4_PIPE_SRC_FORMAT_A_BPC__MASK;  } @@ -750,7 +722,7 @@ static inline uint32_t MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT(uint32_t val)  #define MDP4_PIPE_SRC_FORMAT_UNPACK_ALIGN_MSB			0x00040000  #define MDP4_PIPE_SRC_FORMAT_SOLID_FILL				0x00400000 -static inline uint32_t REG_MDP4_PIPE_SRC_UNPACK(enum mpd4_pipe i0) { return 0x00020054 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_SRC_UNPACK(enum mdp4_pipe i0) { return 0x00020054 + 0x10000*i0; }  #define MDP4_PIPE_SRC_UNPACK_ELEM0__MASK			0x000000ff  #define MDP4_PIPE_SRC_UNPACK_ELEM0__SHIFT			0  static inline uint32_t MDP4_PIPE_SRC_UNPACK_ELEM0(uint32_t val) @@ -776,7 +748,7 @@ static inline uint32_t MDP4_PIPE_SRC_UNPACK_ELEM3(uint32_t val)  	return ((val) << MDP4_PIPE_SRC_UNPACK_ELEM3__SHIFT) & MDP4_PIPE_SRC_UNPACK_ELEM3__MASK;  } -static inline uint32_t REG_MDP4_PIPE_OP_MODE(enum mpd4_pipe i0) { return 0x00020058 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_OP_MODE(enum mdp4_pipe i0) { return 0x00020058 + 0x10000*i0; }  #define MDP4_PIPE_OP_MODE_SCALEX_EN				0x00000001  #define MDP4_PIPE_OP_MODE_SCALEY_EN				0x00000002  #define MDP4_PIPE_OP_MODE_SRC_YCBCR				0x00000200 @@ -789,36 +761,36 @@ static inline uint32_t REG_MDP4_PIPE_OP_MODE(enum mpd4_pipe i0) { return 0x00020  #define MDP4_PIPE_OP_MODE_DEINT_EN				0x00040000  #define MDP4_PIPE_OP_MODE_DEINT_ODD_REF				0x00080000 -static inline uint32_t REG_MDP4_PIPE_PHASEX_STEP(enum mpd4_pipe i0) { return 0x0002005c + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_PHASEX_STEP(enum mdp4_pipe i0) { return 0x0002005c + 0x10000*i0; } -static inline uint32_t REG_MDP4_PIPE_PHASEY_STEP(enum mpd4_pipe i0) { return 0x00020060 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_PHASEY_STEP(enum mdp4_pipe i0) { return 0x00020060 + 0x10000*i0; } -static inline uint32_t REG_MDP4_PIPE_FETCH_CONFIG(enum mpd4_pipe i0) { return 0x00021004 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_FETCH_CONFIG(enum mdp4_pipe i0) { return 0x00021004 + 0x10000*i0; } -static inline uint32_t REG_MDP4_PIPE_SOLID_COLOR(enum mpd4_pipe i0) { return 0x00021008 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_SOLID_COLOR(enum mdp4_pipe i0) { return 0x00021008 + 0x10000*i0; } -static inline uint32_t REG_MDP4_PIPE_CSC(enum mpd4_pipe i0) { return 0x00024000 + 0x10000*i0; } +static inline uint32_t REG_MDP4_PIPE_CSC(enum mdp4_pipe i0) { return 0x00024000 + 0x10000*i0; } -static inline uint32_t REG_MDP4_PIPE_CSC_MV(enum mpd4_pipe i0, uint32_t i1) { return 0x00024400 + 0x10000*i0 + 0x4*i1; } +static inline uint32_t REG_MDP4_PIPE_CSC_MV(enum mdp4_pipe i0, uint32_t i1) { return 0x00024400 + 0x10000*i0 + 0x4*i1; } -static inline uint32_t REG_MDP4_PIPE_CSC_MV_VAL(enum mpd4_pipe i0, uint32_t i1) { return 0x00024400 + 0x10000*i0 + 0x4*i1; } +static inline uint32_t REG_MDP4_PIPE_CSC_MV_VAL(enum mdp4_pipe i0, uint32_t i1) { return 0x00024400 + 0x10000*i0 + 0x4*i1; } -static inline uint32_t REG_MDP4_PIPE_CSC_PRE_BV(enum mpd4_pipe i0, uint32_t i1) { return 0x00024500 + 0x10000*i0 + 0x4*i1; } +static inline uint32_t REG_MDP4_PIPE_CSC_PRE_BV(enum mdp4_pipe i0, uint32_t i1) { return 0x00024500 + 0x10000*i0 + 0x4*i1; } -static inline uint32_t REG_MDP4_PIPE_CSC_PRE_BV_VAL(enum mpd4_pipe i0, uint32_t i1) { return 0x00024500 + 0x10000*i0 + 0x4*i1; } +static inline uint32_t REG_MDP4_PIPE_CSC_PRE_BV_VAL(enum mdp4_pipe i0, uint32_t i1) { return 0x00024500 + 0x10000*i0 + 0x4*i1; } -static inline uint32_t REG_MDP4_PIPE_CSC_POST_BV(enum mpd4_pipe i0, uint32_t i1) { return 0x00024580 + 0x10000*i0 + 0x4*i1; } +static inline uint32_t REG_MDP4_PIPE_CSC_POST_BV(enum mdp4_pipe i0, uint32_t i1) { return 0x00024580 + 0x10000*i0 + 0x4*i1; } -static inline uint32_t REG_MDP4_PIPE_CSC_POST_BV_VAL(enum mpd4_pipe i0, uint32_t i1) { return 0x00024580 + 0x10000*i0 + 0x4*i1; } +static inline uint32_t REG_MDP4_PIPE_CSC_POST_BV_VAL(enum mdp4_pipe i0, uint32_t i1) { return 0x00024580 + 0x10000*i0 + 0x4*i1; } -static inline uint32_t REG_MDP4_PIPE_CSC_PRE_LV(enum mpd4_pipe i0, uint32_t i1) { return 0x00024600 + 0x10000*i0 + 0x4*i1; } +static inline uint32_t REG_MDP4_PIPE_CSC_PRE_LV(enum mdp4_pipe i0, uint32_t i1) { return 0x00024600 + 0x10000*i0 + 0x4*i1; } -static inline uint32_t REG_MDP4_PIPE_CSC_PRE_LV_VAL(enum mpd4_pipe i0, uint32_t i1) { return 0x00024600 + 0x10000*i0 + 0x4*i1; } +static inline uint32_t REG_MDP4_PIPE_CSC_PRE_LV_VAL(enum mdp4_pipe i0, uint32_t i1) { return 0x00024600 + 0x10000*i0 + 0x4*i1; } -static inline uint32_t REG_MDP4_PIPE_CSC_POST_LV(enum mpd4_pipe i0, uint32_t i1) { return 0x00024680 + 0x10000*i0 + 0x4*i1; } +static inline uint32_t REG_MDP4_PIPE_CSC_POST_LV(enum mdp4_pipe i0, uint32_t i1) { return 0x00024680 + 0x10000*i0 + 0x4*i1; } -static inline uint32_t REG_MDP4_PIPE_CSC_POST_LV_VAL(enum mpd4_pipe i0, uint32_t i1) { return 0x00024680 + 0x10000*i0 + 0x4*i1; } +static inline uint32_t REG_MDP4_PIPE_CSC_POST_LV_VAL(enum mdp4_pipe i0, uint32_t i1) { return 0x00024680 + 0x10000*i0 + 0x4*i1; }  #define REG_MDP4_LCDC						0x000c0000 diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index de6bea297cd..74cebb51e8c 100644 --- a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -26,6 +26,7 @@ struct mdp4_crtc {  	struct drm_crtc base;  	char name[8];  	struct drm_plane *plane; +	struct drm_plane *planes[8];  	int id;  	int ovlp;  	enum mdp4_dma dma; @@ -38,6 +39,7 @@ struct mdp4_crtc {  		spinlock_t lock;  		bool stale;  		uint32_t width, height; +		uint32_t x, y;  		/* next cursor to scan-out: */  		uint32_t next_iova; @@ -50,49 +52,110 @@ struct mdp4_crtc {  	/* if there is a pending flip, these will be non-null: */  	struct drm_pending_vblank_event *event; -	struct work_struct pageflip_work; +	struct msm_fence_cb pageflip_cb; -	/* the fb that we currently hold a scanout ref to: */ +#define PENDING_CURSOR 0x1 +#define PENDING_FLIP   0x2 +	atomic_t pending; + +	/* the fb that we logically (from PoV of KMS API) hold a ref +	 * to.  Which we may not yet be scanning out (we may still +	 * be scanning out previous in case of page_flip while waiting +	 * for gpu rendering to complete: +	 */  	struct drm_framebuffer *fb; +	/* the fb that we currently hold a scanout ref to: */ +	struct drm_framebuffer *scanout_fb; +  	/* for unref'ing framebuffers after scanout completes: */  	struct drm_flip_work unref_fb_work;  	/* for unref'ing cursor bo's after scanout completes: */  	struct drm_flip_work unref_cursor_work; -	struct mdp4_irq vblank; -	struct mdp4_irq err; +	struct mdp_irq vblank; +	struct mdp_irq err;  };  #define to_mdp4_crtc(x) container_of(x, struct mdp4_crtc, base)  static struct mdp4_kms *get_kms(struct drm_crtc *crtc)  {  	struct msm_drm_private *priv = crtc->dev->dev_private; -	return to_mdp4_kms(priv->kms); +	return to_mdp4_kms(to_mdp_kms(priv->kms));  } -static void update_fb(struct drm_crtc *crtc, bool async, -		struct drm_framebuffer *new_fb) +static void request_pending(struct drm_crtc *crtc, uint32_t pending)  {  	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); -	struct drm_framebuffer *old_fb = mdp4_crtc->fb; -	if (old_fb) -		drm_flip_work_queue(&mdp4_crtc->unref_fb_work, old_fb); +	atomic_or(pending, &mdp4_crtc->pending); +	mdp_irq_register(&get_kms(crtc)->base, &mdp4_crtc->vblank); +} + +static void crtc_flush(struct drm_crtc *crtc) +{ +	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); +	struct mdp4_kms *mdp4_kms = get_kms(crtc); +	uint32_t i, flush = 0; + +	for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) { +		struct drm_plane *plane = mdp4_crtc->planes[i]; +		if (plane) { +			enum mdp4_pipe pipe_id = mdp4_plane_pipe(plane); +			flush |= pipe2flush(pipe_id); +		} +	} +	flush |= ovlp2flush(mdp4_crtc->ovlp); + +	DBG("%s: flush=%08x", mdp4_crtc->name, flush); + +	mdp4_write(mdp4_kms, REG_MDP4_OVERLAY_FLUSH, flush); +} + +static void update_fb(struct drm_crtc *crtc, struct drm_framebuffer *new_fb) +{ +	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); +	struct drm_framebuffer *old_fb = mdp4_crtc->fb;  	/* grab reference to incoming scanout fb: */  	drm_framebuffer_reference(new_fb); -	mdp4_crtc->base.fb = new_fb; +	mdp4_crtc->base.primary->fb = new_fb;  	mdp4_crtc->fb = new_fb; -	if (!async) { -		/* enable vblank to pick up the old_fb */ -		mdp4_irq_register(get_kms(crtc), &mdp4_crtc->vblank); -	} +	if (old_fb) +		drm_flip_work_queue(&mdp4_crtc->unref_fb_work, old_fb); +} + +/* unlike update_fb(), take a ref to the new scanout fb *before* updating + * plane, then call this.  Needed to ensure we don't unref the buffer that + * is actually still being scanned out. + * + * Note that this whole thing goes away with atomic.. since we can defer + * calling into driver until rendering is done. + */ +static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) +{ +	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); + +	/* flush updates, to make sure hw is updated to new scanout fb, +	 * so that we can safely queue unref to current fb (ie. next +	 * vblank we know hw is done w/ previous scanout_fb). +	 */ +	crtc_flush(crtc); + +	if (mdp4_crtc->scanout_fb) +		drm_flip_work_queue(&mdp4_crtc->unref_fb_work, +				mdp4_crtc->scanout_fb); + +	mdp4_crtc->scanout_fb = fb; + +	/* enable vblank to complete flip: */ +	request_pending(crtc, PENDING_FLIP);  } -static void complete_flip(struct drm_crtc *crtc, bool canceled) +/* if file!=NULL, this is preclose potential cancel-flip path */ +static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)  {  	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);  	struct drm_device *dev = crtc->dev; @@ -102,40 +165,31 @@ static void complete_flip(struct drm_crtc *crtc, bool canceled)  	spin_lock_irqsave(&dev->event_lock, flags);  	event = mdp4_crtc->event;  	if (event) { -		mdp4_crtc->event = NULL; -		if (canceled) -			event->base.destroy(&event->base); -		else +		/* if regular vblank case (!file) or if cancel-flip from +		 * preclose on file that requested flip, then send the +		 * event: +		 */ +		if (!file || (event->base.file_priv == file)) { +			mdp4_crtc->event = NULL;  			drm_send_vblank_event(dev, mdp4_crtc->id, event); +		}  	}  	spin_unlock_irqrestore(&dev->event_lock, flags);  } -static void crtc_flush(struct drm_crtc *crtc) -{ -	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); -	struct mdp4_kms *mdp4_kms = get_kms(crtc); -	uint32_t flush = 0; - -	flush |= pipe2flush(mdp4_plane_pipe(mdp4_crtc->plane)); -	flush |= ovlp2flush(mdp4_crtc->ovlp); - -	DBG("%s: flush=%08x", mdp4_crtc->name, flush); - -	mdp4_write(mdp4_kms, REG_MDP4_OVERLAY_FLUSH, flush); -} - -static void pageflip_worker(struct work_struct *work) +static void pageflip_cb(struct msm_fence_cb *cb)  {  	struct mdp4_crtc *mdp4_crtc = -		container_of(work, struct mdp4_crtc, pageflip_work); +		container_of(cb, struct mdp4_crtc, pageflip_cb);  	struct drm_crtc *crtc = &mdp4_crtc->base; +	struct drm_framebuffer *fb = crtc->primary->fb; -	mdp4_plane_set_scanout(mdp4_crtc->plane, crtc->fb); -	crtc_flush(crtc); +	if (!fb) +		return; -	/* enable vblank to complete flip: */ -	mdp4_irq_register(get_kms(crtc), &mdp4_crtc->vblank); +	drm_framebuffer_reference(fb); +	mdp4_plane_set_scanout(mdp4_crtc->plane, fb); +	update_scanout(crtc, fb);  }  static void unref_fb_worker(struct drm_flip_work *work, void *val) @@ -163,8 +217,6 @@ static void mdp4_crtc_destroy(struct drm_crtc *crtc)  {  	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); -	mdp4_crtc->plane->funcs->destroy(mdp4_crtc->plane); -  	drm_crtc_cleanup(crtc);  	drm_flip_work_cleanup(&mdp4_crtc->unref_fb_work);  	drm_flip_work_cleanup(&mdp4_crtc->unref_cursor_work); @@ -183,9 +235,9 @@ static void mdp4_crtc_dpms(struct drm_crtc *crtc, int mode)  	if (enabled != mdp4_crtc->enabled) {  		if (enabled) {  			mdp4_enable(mdp4_kms); -			mdp4_irq_register(mdp4_kms, &mdp4_crtc->err); +			mdp_irq_register(&mdp4_kms->base, &mdp4_crtc->err);  		} else { -			mdp4_irq_unregister(mdp4_kms, &mdp4_crtc->err); +			mdp_irq_unregister(&mdp4_kms->base, &mdp4_crtc->err);  			mdp4_disable(mdp4_kms);  		}  		mdp4_crtc->enabled = enabled; @@ -205,67 +257,69 @@ static void blend_setup(struct drm_crtc *crtc)  	struct mdp4_kms *mdp4_kms = get_kms(crtc);  	int i, ovlp = mdp4_crtc->ovlp;  	uint32_t mixer_cfg = 0; - -	/* -	 * This probably would also need to be triggered by any attached -	 * plane when it changes.. for now since we are only using a single -	 * private plane, the configuration is hard-coded: -	 */ +	static const enum mdp_mixer_stage_id stages[] = { +			STAGE_BASE, STAGE0, STAGE1, STAGE2, STAGE3, +	}; +	/* statically (for now) map planes to mixer stage (z-order): */ +	static const int idxs[] = { +			[VG1]  = 1, +			[VG2]  = 2, +			[RGB1] = 0, +			[RGB2] = 0, +			[RGB3] = 0, +			[VG3]  = 3, +			[VG4]  = 4, + +	}; +	bool alpha[4]= { false, false, false, false };  	mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW0(ovlp), 0);  	mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW1(ovlp), 0);  	mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH0(ovlp), 0);  	mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH1(ovlp), 0); +	/* TODO single register for all CRTCs, so this won't work properly +	 * when multiple CRTCs are active.. +	 */ +	for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) { +		struct drm_plane *plane = mdp4_crtc->planes[i]; +		if (plane) { +			enum mdp4_pipe pipe_id = mdp4_plane_pipe(plane); +			int idx = idxs[pipe_id]; +			if (idx > 0) { +				const struct mdp_format *format = +					to_mdp_format(msm_framebuffer_format(plane->fb)); +				alpha[idx-1] = format->alpha_enable; +			} +			mixer_cfg |= mixercfg(mdp4_crtc->mixer, pipe_id, stages[idx]); +		} +	} + +	/* this shouldn't happen.. and seems to cause underflow: */ +	WARN_ON(!mixer_cfg); +  	for (i = 0; i < 4; i++) { -		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_FG_ALPHA(ovlp, i), 0); -		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_BG_ALPHA(ovlp, i), 0); -		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_OP(ovlp, i), -				MDP4_OVLP_STAGE_OP_FG_ALPHA(FG_CONST) | -				MDP4_OVLP_STAGE_OP_BG_ALPHA(BG_CONST)); -		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_CO3(ovlp, i), 0); +		uint32_t op; + +		if (alpha[i]) { +			op = MDP4_OVLP_STAGE_OP_FG_ALPHA(FG_PIXEL) | +					MDP4_OVLP_STAGE_OP_BG_ALPHA(FG_PIXEL) | +					MDP4_OVLP_STAGE_OP_BG_INV_ALPHA; +		} else { +			op = MDP4_OVLP_STAGE_OP_FG_ALPHA(FG_CONST) | +					MDP4_OVLP_STAGE_OP_BG_ALPHA(BG_CONST); +		} + +		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_FG_ALPHA(ovlp, i), 0xff); +		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_BG_ALPHA(ovlp, i), 0x00); +		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_OP(ovlp, i), op); +		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_CO3(ovlp, i), 1);  		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_LOW0(ovlp, i), 0);  		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_LOW1(ovlp, i), 0);  		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_HIGH0(ovlp, i), 0);  		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_HIGH1(ovlp, i), 0);  	} -	/* TODO single register for all CRTCs, so this won't work properly -	 * when multiple CRTCs are active.. -	 */ -	switch (mdp4_plane_pipe(mdp4_crtc->plane)) { -	case VG1: -		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE0(STAGE_BASE) | -			COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE0_MIXER1); -		break; -	case VG2: -		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE1(STAGE_BASE) | -			COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE1_MIXER1); -		break; -	case RGB1: -		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE2(STAGE_BASE) | -			COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE2_MIXER1); -		break; -	case RGB2: -		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE3(STAGE_BASE) | -			COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE3_MIXER1); -		break; -	case RGB3: -		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE4(STAGE_BASE) | -			COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE4_MIXER1); -		break; -	case VG3: -		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE5(STAGE_BASE) | -			COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE5_MIXER1); -		break; -	case VG4: -		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE6(STAGE_BASE) | -			COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE6_MIXER1); -		break; -	default: -		WARN_ON("invalid pipe"); -		break; -	}  	mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG, mixer_cfg);  } @@ -291,6 +345,20 @@ static int mdp4_crtc_mode_set(struct drm_crtc *crtc,  			mode->vsync_end, mode->vtotal,  			mode->type, mode->flags); +	/* grab extra ref for update_scanout() */ +	drm_framebuffer_reference(crtc->primary->fb); + +	ret = mdp4_plane_mode_set(mdp4_crtc->plane, crtc, crtc->primary->fb, +			0, 0, mode->hdisplay, mode->vdisplay, +			x << 16, y << 16, +			mode->hdisplay << 16, mode->vdisplay << 16); +	if (ret) { +		drm_framebuffer_unreference(crtc->primary->fb); +		dev_err(crtc->dev->dev, "%s: failed to set mode on plane: %d\n", +				mdp4_crtc->name, ret); +		return ret; +	} +  	mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_SIZE(dma),  			MDP4_DMA_SRC_SIZE_WIDTH(mode->hdisplay) |  			MDP4_DMA_SRC_SIZE_HEIGHT(mode->vdisplay)); @@ -298,7 +366,7 @@ static int mdp4_crtc_mode_set(struct drm_crtc *crtc,  	/* take data from pipe: */  	mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_BASE(dma), 0);  	mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_STRIDE(dma), -			crtc->fb->pitches[0]); +			crtc->primary->fb->pitches[0]);  	mdp4_write(mdp4_kms, REG_MDP4_DMA_DST_SIZE(dma),  			MDP4_DMA_DST_SIZE_WIDTH(0) |  			MDP4_DMA_DST_SIZE_HEIGHT(0)); @@ -308,28 +376,19 @@ static int mdp4_crtc_mode_set(struct drm_crtc *crtc,  			MDP4_OVLP_SIZE_WIDTH(mode->hdisplay) |  			MDP4_OVLP_SIZE_HEIGHT(mode->vdisplay));  	mdp4_write(mdp4_kms, REG_MDP4_OVLP_STRIDE(ovlp), -			crtc->fb->pitches[0]); +			crtc->primary->fb->pitches[0]);  	mdp4_write(mdp4_kms, REG_MDP4_OVLP_CFG(ovlp), 1); -	update_fb(crtc, false, crtc->fb); - -	ret = mdp4_plane_mode_set(mdp4_crtc->plane, crtc, crtc->fb, -			0, 0, mode->hdisplay, mode->vdisplay, -			x << 16, y << 16, -			mode->hdisplay << 16, mode->vdisplay << 16); -	if (ret) { -		dev_err(crtc->dev->dev, "%s: failed to set mode on plane: %d\n", -				mdp4_crtc->name, ret); -		return ret; -	} -  	if (dma == DMA_E) {  		mdp4_write(mdp4_kms, REG_MDP4_DMA_E_QUANT(0), 0x00ff0000);  		mdp4_write(mdp4_kms, REG_MDP4_DMA_E_QUANT(1), 0x00ff0000);  		mdp4_write(mdp4_kms, REG_MDP4_DMA_E_QUANT(2), 0x00ff0000);  	} +	update_fb(crtc, crtc->primary->fb); +	update_scanout(crtc, crtc->primary->fb); +  	return 0;  } @@ -356,13 +415,24 @@ static int mdp4_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,  	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);  	struct drm_plane *plane = mdp4_crtc->plane;  	struct drm_display_mode *mode = &crtc->mode; +	int ret; -	update_fb(crtc, false, crtc->fb); +	/* grab extra ref for update_scanout() */ +	drm_framebuffer_reference(crtc->primary->fb); -	return mdp4_plane_mode_set(plane, crtc, crtc->fb, +	ret = mdp4_plane_mode_set(plane, crtc, crtc->primary->fb,  			0, 0, mode->hdisplay, mode->vdisplay,  			x << 16, y << 16,  			mode->hdisplay << 16, mode->vdisplay << 16); +	if (ret) { +		drm_framebuffer_unreference(crtc->primary->fb); +		return ret; +	} + +	update_fb(crtc, crtc->primary->fb); +	update_scanout(crtc, crtc->primary->fb); + +	return 0;  }  static void mdp4_crtc_load_lut(struct drm_crtc *crtc) @@ -377,6 +447,7 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc,  	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);  	struct drm_device *dev = crtc->dev;  	struct drm_gem_object *obj; +	unsigned long flags;  	if (mdp4_crtc->event) {  		dev_err(dev->dev, "already pending flip!\n"); @@ -385,11 +456,13 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc,  	obj = msm_framebuffer_bo(new_fb, 0); +	spin_lock_irqsave(&dev->event_lock, flags);  	mdp4_crtc->event = event; -	update_fb(crtc, true, new_fb); +	spin_unlock_irqrestore(&dev->event_lock, flags); + +	update_fb(crtc, new_fb); -	return msm_gem_queue_inactive_work(obj, -			&mdp4_crtc->pageflip_work); +	return msm_gem_queue_inactive_cb(obj, &mdp4_crtc->pageflip_cb);  }  static int mdp4_crtc_set_property(struct drm_crtc *crtc, @@ -410,12 +483,12 @@ static int mdp4_crtc_set_property(struct drm_crtc *crtc,  static void update_cursor(struct drm_crtc *crtc)  {  	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); +	struct mdp4_kms *mdp4_kms = get_kms(crtc);  	enum mdp4_dma dma = mdp4_crtc->dma;  	unsigned long flags;  	spin_lock_irqsave(&mdp4_crtc->cursor.lock, flags);  	if (mdp4_crtc->cursor.stale) { -		struct mdp4_kms *mdp4_kms = get_kms(crtc);  		struct drm_gem_object *next_bo = mdp4_crtc->cursor.next_bo;  		struct drm_gem_object *prev_bo = mdp4_crtc->cursor.scanout_bo;  		uint32_t iova = mdp4_crtc->cursor.next_iova; @@ -435,9 +508,8 @@ static void update_cursor(struct drm_crtc *crtc)  					MDP4_DMA_CURSOR_BLEND_CONFIG_CURSOR_EN);  		} else {  			/* disable cursor: */ -			mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BASE(dma), 0); -			mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BLEND_CONFIG(dma), -					MDP4_DMA_CURSOR_BLEND_CONFIG_FORMAT(CURSOR_ARGB)); +			mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BASE(dma), +					mdp4_kms->blank_cursor_iova);  		}  		/* and drop the iova ref + obj rev when done scanning out: */ @@ -447,6 +519,11 @@ static void update_cursor(struct drm_crtc *crtc)  		mdp4_crtc->cursor.scanout_bo = next_bo;  		mdp4_crtc->cursor.stale = false;  	} + +	mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_POS(dma), +			MDP4_DMA_CURSOR_POS_X(mdp4_crtc->cursor.x) | +			MDP4_DMA_CURSOR_POS_Y(mdp4_crtc->cursor.y)); +  	spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags);  } @@ -494,10 +571,11 @@ static int mdp4_crtc_cursor_set(struct drm_crtc *crtc,  	if (old_bo) {  		/* drop our previous reference: */ -		msm_gem_put_iova(old_bo, mdp4_kms->id); -		drm_gem_object_unreference_unlocked(old_bo); +		drm_flip_work_queue(&mdp4_crtc->unref_cursor_work, old_bo);  	} +	request_pending(crtc, PENDING_CURSOR); +  	return 0;  fail: @@ -508,12 +586,15 @@ fail:  static int mdp4_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)  {  	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); -	struct mdp4_kms *mdp4_kms = get_kms(crtc); -	enum mdp4_dma dma = mdp4_crtc->dma; +	unsigned long flags; -	mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_POS(dma), -			MDP4_DMA_CURSOR_POS_X(x) | -			MDP4_DMA_CURSOR_POS_Y(y)); +	spin_lock_irqsave(&mdp4_crtc->cursor.lock, flags); +	mdp4_crtc->cursor.x = x; +	mdp4_crtc->cursor.y = y; +	spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags); + +	crtc_flush(crtc); +	request_pending(crtc, PENDING_CURSOR);  	return 0;  } @@ -537,21 +618,29 @@ static const struct drm_crtc_helper_funcs mdp4_crtc_helper_funcs = {  	.load_lut = mdp4_crtc_load_lut,  }; -static void mdp4_crtc_vblank_irq(struct mdp4_irq *irq, uint32_t irqstatus) +static void mdp4_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus)  {  	struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, vblank);  	struct drm_crtc *crtc = &mdp4_crtc->base;  	struct msm_drm_private *priv = crtc->dev->dev_private; +	unsigned pending; -	update_cursor(crtc); -	complete_flip(crtc, false); -	mdp4_irq_unregister(get_kms(crtc), &mdp4_crtc->vblank); +	mdp_irq_unregister(&get_kms(crtc)->base, &mdp4_crtc->vblank); -	drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq); -	drm_flip_work_commit(&mdp4_crtc->unref_cursor_work, priv->wq); +	pending = atomic_xchg(&mdp4_crtc->pending, 0); + +	if (pending & PENDING_FLIP) { +		complete_flip(crtc, NULL); +		drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq); +	} + +	if (pending & PENDING_CURSOR) { +		update_cursor(crtc); +		drm_flip_work_commit(&mdp4_crtc->unref_cursor_work, priv->wq); +	}  } -static void mdp4_crtc_err_irq(struct mdp4_irq *irq, uint32_t irqstatus) +static void mdp4_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus)  {  	struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, err);  	struct drm_crtc *crtc = &mdp4_crtc->base; @@ -565,9 +654,10 @@ uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc)  	return mdp4_crtc->vblank.irqmask;  } -void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc) +void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file)  { -	complete_flip(crtc, true); +	DBG("cancel: %p", file); +	complete_flip(crtc, file);  }  /* set dma config, ie. the format the encoder wants. */ @@ -622,6 +712,35 @@ void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf)  	mdp4_write(mdp4_kms, REG_MDP4_DISP_INTF_SEL, intf_sel);  } +static void set_attach(struct drm_crtc *crtc, enum mdp4_pipe pipe_id, +		struct drm_plane *plane) +{ +	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); + +	BUG_ON(pipe_id >= ARRAY_SIZE(mdp4_crtc->planes)); + +	if (mdp4_crtc->planes[pipe_id] == plane) +		return; + +	mdp4_crtc->planes[pipe_id] = plane; +	blend_setup(crtc); +	if (mdp4_crtc->enabled && (plane != mdp4_crtc->plane)) +		crtc_flush(crtc); +} + +void mdp4_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane) +{ +	set_attach(crtc, mdp4_plane_pipe(plane), plane); +} + +void mdp4_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane) +{ +	/* don't actually detatch our primary plane: */ +	if (to_mdp4_crtc(crtc)->plane == plane) +		return; +	set_attach(crtc, mdp4_plane_pipe(plane), NULL); +} +  static const char *dma_names[] = {  		"DMA_P", "DMA_S", "DMA_E",  }; @@ -644,7 +763,7 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,  	crtc = &mdp4_crtc->base;  	mdp4_crtc->plane = plane; -	mdp4_crtc->plane->crtc = crtc; +	mdp4_crtc->id = id;  	mdp4_crtc->ovlp = ovlp_id;  	mdp4_crtc->dma = dma_id; @@ -668,9 +787,9 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,  	ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64,  			"unref cursor", unref_cursor_worker); -	INIT_WORK(&mdp4_crtc->pageflip_work, pageflip_worker); +	INIT_FENCE_CB(&mdp4_crtc->pageflip_cb, pageflip_cb); -	drm_crtc_init(dev, crtc, &mdp4_crtc_funcs); +	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp4_crtc_funcs);  	drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);  	mdp4_plane_install_properties(mdp4_crtc->plane, &crtc->base); diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_dtv_encoder.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c index 5e0dcae70ab..067ed03b35f 100644 --- a/drivers/gpu/drm/msm/mdp4/mdp4_dtv_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c @@ -15,8 +15,6 @@   * this program.  If not, see <http://www.gnu.org/licenses/>.   */ -#include <mach/clk.h> -  #include "mdp4_kms.h"  #include "drm_crtc.h" @@ -37,7 +35,7 @@ struct mdp4_dtv_encoder {  static struct mdp4_kms *get_kms(struct drm_encoder *encoder)  {  	struct msm_drm_private *priv = encoder->dev->dev_private; -	return to_mdp4_kms(priv->kms); +	return to_mdp4_kms(to_mdp_kms(priv->kms));  }  #ifdef CONFIG_MSM_BUS_SCALING @@ -139,7 +137,7 @@ static void mdp4_dtv_encoder_dpms(struct drm_encoder *encoder, int mode)  		 * the settings changes for the new modeset (like new  		 * scanout buffer) don't latch properly..  		 */ -		mdp4_irq_wait(mdp4_kms, MDP4_IRQ_EXTERNAL_VSYNC); +		mdp_irq_wait(&mdp4_kms->base, MDP4_IRQ_EXTERNAL_VSYNC);  		clk_disable_unprepare(mdp4_dtv_encoder->src_clk);  		clk_disable_unprepare(mdp4_dtv_encoder->hdmi_clk); diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c new file mode 100644 index 00000000000..8edd531cb62 --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "msm_drv.h" +#include "mdp4_kms.h" + +void mdp4_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask) +{ +	mdp4_write(to_mdp4_kms(mdp_kms), REG_MDP4_INTR_ENABLE, irqmask); +} + +static void mdp4_irq_error_handler(struct mdp_irq *irq, uint32_t irqstatus) +{ +	DRM_ERROR("errors: %08x\n", irqstatus); +} + +void mdp4_irq_preinstall(struct msm_kms *kms) +{ +	struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); +	mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, 0xffffffff); +} + +int mdp4_irq_postinstall(struct msm_kms *kms) +{ +	struct mdp_kms *mdp_kms = to_mdp_kms(kms); +	struct mdp4_kms *mdp4_kms = to_mdp4_kms(mdp_kms); +	struct mdp_irq *error_handler = &mdp4_kms->error_handler; + +	error_handler->irq = mdp4_irq_error_handler; +	error_handler->irqmask = MDP4_IRQ_PRIMARY_INTF_UDERRUN | +			MDP4_IRQ_EXTERNAL_INTF_UDERRUN; + +	mdp_irq_register(mdp_kms, error_handler); + +	return 0; +} + +void mdp4_irq_uninstall(struct msm_kms *kms) +{ +	struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); +	mdp4_write(mdp4_kms, REG_MDP4_INTR_ENABLE, 0x00000000); +} + +irqreturn_t mdp4_irq(struct msm_kms *kms) +{ +	struct mdp_kms *mdp_kms = to_mdp_kms(kms); +	struct mdp4_kms *mdp4_kms = to_mdp4_kms(mdp_kms); +	struct drm_device *dev = mdp4_kms->dev; +	struct msm_drm_private *priv = dev->dev_private; +	unsigned int id; +	uint32_t status; + +	status = mdp4_read(mdp4_kms, REG_MDP4_INTR_STATUS); +	mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, status); + +	VERB("status=%08x", status); + +	mdp_dispatch_irqs(mdp_kms, status); + +	for (id = 0; id < priv->num_crtcs; id++) +		if (status & mdp4_crtc_vblank(priv->crtcs[id])) +			drm_handle_vblank(dev, id); + +	return IRQ_HANDLED; +} + +int mdp4_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) +{ +	mdp_update_vblank_mask(to_mdp_kms(kms), +			mdp4_crtc_vblank(crtc), true); +	return 0; +} + +void mdp4_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) +{ +	mdp_update_vblank_mask(to_mdp_kms(kms), +			mdp4_crtc_vblank(crtc), false); +} diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index 5db5bbaedae..0bb4faa1752 100644 --- a/drivers/gpu/drm/msm/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -17,15 +17,14 @@  #include "msm_drv.h" +#include "msm_mmu.h"  #include "mdp4_kms.h" -#include <mach/iommu.h> -  static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev);  static int mdp4_hw_init(struct msm_kms *kms)  { -	struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms); +	struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));  	struct drm_device *dev = mdp4_kms->dev;  	uint32_t version, major, minor, dmap_cfg, vg_cfg;  	unsigned long clk; @@ -33,12 +32,14 @@ static int mdp4_hw_init(struct msm_kms *kms)  	pm_runtime_get_sync(dev->dev); +	mdp4_enable(mdp4_kms);  	version = mdp4_read(mdp4_kms, REG_MDP4_VERSION); +	mdp4_disable(mdp4_kms);  	major = FIELD(version, MDP4_VERSION_MAJOR);  	minor = FIELD(version, MDP4_VERSION_MINOR); -	DBG("found MDP version v%d.%d", major, minor); +	DBG("found MDP4 version v%d.%d", major, minor);  	if (major != 4) {  		dev_err(dev->dev, "unexpected MDP version: v%d.%d\n", @@ -132,21 +133,26 @@ static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate,  static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file)  { -	struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms); +	struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));  	struct msm_drm_private *priv = mdp4_kms->dev->dev_private;  	unsigned i;  	for (i = 0; i < priv->num_crtcs; i++) -		mdp4_crtc_cancel_pending_flip(priv->crtcs[i]); +		mdp4_crtc_cancel_pending_flip(priv->crtcs[i], file);  }  static void mdp4_destroy(struct msm_kms *kms)  { -	struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms); +	struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); +	if (mdp4_kms->blank_cursor_iova) +		msm_gem_put_iova(mdp4_kms->blank_cursor_bo, mdp4_kms->id); +	if (mdp4_kms->blank_cursor_bo) +		drm_gem_object_unreference(mdp4_kms->blank_cursor_bo);  	kfree(mdp4_kms);  } -static const struct msm_kms_funcs kms_funcs = { +static const struct mdp_kms_funcs kms_funcs = { +	.base = {  		.hw_init         = mdp4_hw_init,  		.irq_preinstall  = mdp4_irq_preinstall,  		.irq_postinstall = mdp4_irq_postinstall, @@ -154,10 +160,12 @@ static const struct msm_kms_funcs kms_funcs = {  		.irq             = mdp4_irq,  		.enable_vblank   = mdp4_enable_vblank,  		.disable_vblank  = mdp4_disable_vblank, -		.get_format      = mdp4_get_format, +		.get_format      = mdp_get_format,  		.round_pixclk    = mdp4_round_pixclk,  		.preclose        = mdp4_preclose,  		.destroy         = mdp4_destroy, +	}, +	.set_irqmask         = mdp4_set_irqmask,  };  int mdp4_disable(struct mdp4_kms *mdp4_kms) @@ -191,6 +199,7 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)  	struct drm_plane *plane;  	struct drm_crtc *crtc;  	struct drm_encoder *encoder; +	struct hdmi *hdmi;  	int ret;  	/* @@ -198,6 +207,23 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)  	 * for more than just RGB1->DMA_E->DTV->HDMI  	 */ +	/* construct non-private planes: */ +	plane = mdp4_plane_init(dev, VG1, false); +	if (IS_ERR(plane)) { +		dev_err(dev->dev, "failed to construct plane for VG1\n"); +		ret = PTR_ERR(plane); +		goto fail; +	} +	priv->planes[priv->num_planes++] = plane; + +	plane = mdp4_plane_init(dev, VG2, false); +	if (IS_ERR(plane)) { +		dev_err(dev->dev, "failed to construct plane for VG2\n"); +		ret = PTR_ERR(plane); +		goto fail; +	} +	priv->planes[priv->num_planes++] = plane; +  	/* the CRTCs get constructed with a private plane: */  	plane = mdp4_plane_init(dev, RGB1, true);  	if (IS_ERR(plane)) { @@ -223,9 +249,10 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)  	encoder->possible_crtcs = 0x1;     /* DTV can be hooked to DMA_E */  	priv->encoders[priv->num_encoders++] = encoder; -	ret = hdmi_init(dev, encoder); -	if (ret) { -		dev_err(dev->dev, "failed to initialize HDMI\n"); +	hdmi = hdmi_init(dev, encoder); +	if (IS_ERR(hdmi)) { +		ret = PTR_ERR(hdmi); +		dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret);  		goto fail;  	} @@ -245,6 +272,7 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)  	struct mdp4_platform_config *config = mdp4_get_config(pdev);  	struct mdp4_kms *mdp4_kms;  	struct msm_kms *kms = NULL; +	struct msm_mmu *mmu;  	int ret;  	mdp4_kms = kzalloc(sizeof(*mdp4_kms), GFP_KERNEL); @@ -254,8 +282,9 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)  		goto fail;  	} -	kms = &mdp4_kms->base; -	kms->funcs = &kms_funcs; +	mdp_kms_init(&mdp4_kms->base, &kms_funcs); + +	kms = &mdp4_kms->base.base;  	mdp4_kms->dev = dev; @@ -307,27 +336,34 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)  	clk_set_rate(mdp4_kms->clk, config->max_clk);  	clk_set_rate(mdp4_kms->lut_clk, config->max_clk); -	if (!config->iommu) { -		dev_err(dev->dev, "no iommu\n"); -		ret = -ENXIO; -		goto fail; -	} -  	/* make sure things are off before attaching iommu (bootloader could  	 * have left things on, in which case we'll start getting faults if  	 * we don't disable):  	 */ +	mdp4_enable(mdp4_kms);  	mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0);  	mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0);  	mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 0); +	mdp4_disable(mdp4_kms);  	mdelay(16); -	ret = msm_iommu_attach(dev, config->iommu, -			iommu_ports, ARRAY_SIZE(iommu_ports)); -	if (ret) -		goto fail; +	if (config->iommu) { +		mmu = msm_iommu_new(dev, config->iommu); +		if (IS_ERR(mmu)) { +			ret = PTR_ERR(mmu); +			goto fail; +		} +		ret = mmu->funcs->attach(mmu, iommu_ports, +				ARRAY_SIZE(iommu_ports)); +		if (ret) +			goto fail; +	} else { +		dev_info(dev->dev, "no iommu, fallback to phys " +				"contig buffers for scanout\n"); +		mmu = NULL; +	} -	mdp4_kms->id = msm_register_iommu(dev, config->iommu); +	mdp4_kms->id = msm_register_mmu(dev, mmu);  	if (mdp4_kms->id < 0) {  		ret = mdp4_kms->id;  		dev_err(dev->dev, "failed to register mdp4 iommu: %d\n", ret); @@ -340,6 +376,23 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)  		goto fail;  	} +	mutex_lock(&dev->struct_mutex); +	mdp4_kms->blank_cursor_bo = msm_gem_new(dev, SZ_16K, MSM_BO_WC); +	mutex_unlock(&dev->struct_mutex); +	if (IS_ERR(mdp4_kms->blank_cursor_bo)) { +		ret = PTR_ERR(mdp4_kms->blank_cursor_bo); +		dev_err(dev->dev, "could not allocate blank-cursor bo: %d\n", ret); +		mdp4_kms->blank_cursor_bo = NULL; +		goto fail; +	} + +	ret = msm_gem_get_iova(mdp4_kms->blank_cursor_bo, mdp4_kms->id, +			&mdp4_kms->blank_cursor_iova); +	if (ret) { +		dev_err(dev->dev, "could not pin blank-cursor bo: %d\n", ret); +		goto fail; +	} +  	return kms;  fail: diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h index 1e83554955f..715520c54cd 100644 --- a/drivers/gpu/drm/msm/mdp4/mdp4_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h @@ -18,29 +18,13 @@  #ifndef __MDP4_KMS_H__  #define __MDP4_KMS_H__ -#include <linux/clk.h> -#include <linux/platform_device.h> -#include <linux/regulator/consumer.h> -  #include "msm_drv.h" +#include "msm_kms.h" +#include "mdp/mdp_kms.h"  #include "mdp4.xml.h" - -/* For transiently registering for different MDP4 irqs that various parts - * of the KMS code need during setup/configuration.  We these are not - * necessarily the same as what drm_vblank_get/put() are requesting, and - * the hysteresis in drm_vblank_put() is not necessarily desirable for - * internal housekeeping related irq usage. - */ -struct mdp4_irq { -	struct list_head node; -	uint32_t irqmask; -	bool registered; -	void (*irq)(struct mdp4_irq *irq, uint32_t irqstatus); -}; -  struct mdp4_kms { -	struct msm_kms base; +	struct mdp_kms base;  	struct drm_device *dev; @@ -59,11 +43,11 @@ struct mdp4_kms {  	struct clk *pclk;  	struct clk *lut_clk; -	/* irq handling: */ -	bool in_irq; -	struct list_head irq_list;    /* list of mdp4_irq */ -	uint32_t vblank_mask;         /* irq bits set for userspace vblank */ -	struct mdp4_irq error_handler; +	struct mdp_irq error_handler; + +	/* empty/blank cursor bo to use when cursor is "disabled" */ +	struct drm_gem_object *blank_cursor_bo; +	uint32_t blank_cursor_iova;  };  #define to_mdp4_kms(x) container_of(x, struct mdp4_kms, base) @@ -73,16 +57,6 @@ struct mdp4_platform_config {  	uint32_t max_clk;  }; -struct mdp4_format { -	struct msm_format base; -	enum mpd4_bpc bpc_r, bpc_g, bpc_b; -	enum mpd4_bpc_alpha bpc_a; -	uint8_t unpack[4]; -	bool alpha_enable, unpack_tight; -	uint8_t cpp, unpack_count; -}; -#define to_mdp4_format(x) container_of(x, struct mdp4_format, base) -  static inline void mdp4_write(struct mdp4_kms *mdp4_kms, u32 reg, u32 data)  {  	msm_writel(data, mdp4_kms->mmio + reg); @@ -93,7 +67,7 @@ static inline u32 mdp4_read(struct mdp4_kms *mdp4_kms, u32 reg)  	return msm_readl(mdp4_kms->mmio + reg);  } -static inline uint32_t pipe2flush(enum mpd4_pipe pipe) +static inline uint32_t pipe2flush(enum mdp4_pipe pipe)  {  	switch (pipe) {  	case VG1:      return MDP4_OVERLAY_FLUSH_VG1; @@ -133,20 +107,68 @@ static inline uint32_t dma2err(enum mdp4_dma dma)  	}  } +static inline uint32_t mixercfg(int mixer, enum mdp4_pipe pipe, +		enum mdp_mixer_stage_id stage) +{ +	uint32_t mixer_cfg = 0; + +	switch (pipe) { +	case VG1: +		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE0(stage) | +			COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE0_MIXER1); +		break; +	case VG2: +		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE1(stage) | +			COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE1_MIXER1); +		break; +	case RGB1: +		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE2(stage) | +			COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE2_MIXER1); +		break; +	case RGB2: +		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE3(stage) | +			COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE3_MIXER1); +		break; +	case RGB3: +		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE4(stage) | +			COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE4_MIXER1); +		break; +	case VG3: +		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE5(stage) | +			COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE5_MIXER1); +		break; +	case VG4: +		mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE6(stage) | +			COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE6_MIXER1); +		break; +	default: +		WARN_ON("invalid pipe"); +		break; +	} + +	return mixer_cfg; +} +  int mdp4_disable(struct mdp4_kms *mdp4_kms);  int mdp4_enable(struct mdp4_kms *mdp4_kms); +void mdp4_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask);  void mdp4_irq_preinstall(struct msm_kms *kms);  int mdp4_irq_postinstall(struct msm_kms *kms);  void mdp4_irq_uninstall(struct msm_kms *kms);  irqreturn_t mdp4_irq(struct msm_kms *kms); -void mdp4_irq_wait(struct mdp4_kms *mdp4_kms, uint32_t irqmask); -void mdp4_irq_register(struct mdp4_kms *mdp4_kms, struct mdp4_irq *irq); -void mdp4_irq_unregister(struct mdp4_kms *mdp4_kms, struct mdp4_irq *irq);  int mdp4_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);  void mdp4_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc); -const struct msm_format *mdp4_get_format(struct msm_kms *kms, uint32_t format); +static inline +uint32_t mdp4_get_formats(enum mdp4_pipe pipe_id, uint32_t *pixel_formats, +		uint32_t max_formats) +{ +	/* TODO when we have YUV, we need to filter supported formats +	 * based on pipe_id.. +	 */ +	return mdp_get_formats(pixel_formats, max_formats); +}  void mdp4_plane_install_properties(struct drm_plane *plane,  		struct drm_mode_object *obj); @@ -158,14 +180,16 @@ int mdp4_plane_mode_set(struct drm_plane *plane,  		unsigned int crtc_w, unsigned int crtc_h,  		uint32_t src_x, uint32_t src_y,  		uint32_t src_w, uint32_t src_h); -enum mpd4_pipe mdp4_plane_pipe(struct drm_plane *plane); +enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane);  struct drm_plane *mdp4_plane_init(struct drm_device *dev, -		enum mpd4_pipe pipe_id, bool private_plane); +		enum mdp4_pipe pipe_id, bool private_plane);  uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc); -void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc); +void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);  void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config);  void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf); +void mdp4_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane); +void mdp4_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane);  struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,  		struct drm_plane *plane, int id, int ovlp_id,  		enum mdp4_dma dma_id); diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 3468229d58b..66f33dba1eb 100644 --- a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -22,7 +22,7 @@ struct mdp4_plane {  	struct drm_plane base;  	const char *name; -	enum mpd4_pipe pipe; +	enum mdp4_pipe pipe;  	uint32_t nformats;  	uint32_t formats[32]; @@ -34,7 +34,7 @@ struct mdp4_plane {  static struct mdp4_kms *get_kms(struct drm_plane *plane)  {  	struct msm_drm_private *priv = plane->dev->dev_private; -	return to_mdp4_kms(priv->kms); +	return to_mdp4_kms(to_mdp_kms(priv->kms));  }  static int mdp4_plane_update(struct drm_plane *plane, @@ -61,7 +61,9 @@ static int mdp4_plane_update(struct drm_plane *plane,  static int mdp4_plane_disable(struct drm_plane *plane)  {  	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); -	DBG("%s: TODO", mdp4_plane->name); // XXX +	DBG("%s: disable", mdp4_plane->name); +	if (plane->crtc) +		mdp4_crtc_detach(plane->crtc, plane);  	return 0;  } @@ -101,7 +103,7 @@ void mdp4_plane_set_scanout(struct drm_plane *plane,  {  	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);  	struct mdp4_kms *mdp4_kms = get_kms(plane); -	enum mpd4_pipe pipe = mdp4_plane->pipe; +	enum mdp4_pipe pipe = mdp4_plane->pipe;  	uint32_t iova;  	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_A(pipe), @@ -129,8 +131,8 @@ int mdp4_plane_mode_set(struct drm_plane *plane,  {  	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);  	struct mdp4_kms *mdp4_kms = get_kms(plane); -	enum mpd4_pipe pipe = mdp4_plane->pipe; -	const struct mdp4_format *format; +	enum mdp4_pipe pipe = mdp4_plane->pipe; +	const struct mdp_format *format;  	uint32_t op_mode = 0;  	uint32_t phasex_step = MDP4_VG_PHASE_STEP_DEFAULT;  	uint32_t phasey_step = MDP4_VG_PHASE_STEP_DEFAULT; @@ -141,6 +143,10 @@ int mdp4_plane_mode_set(struct drm_plane *plane,  	src_w = src_w >> 16;  	src_h = src_h >> 16; +	DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", mdp4_plane->name, +			fb->base.id, src_x, src_y, src_w, src_h, +			crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h); +  	if (src_w != crtc_w) {  		op_mode |= MDP4_PIPE_OP_MODE_SCALEX_EN;  		/* TODO calc phasex_step */ @@ -164,12 +170,12 @@ int mdp4_plane_mode_set(struct drm_plane *plane,  			MDP4_PIPE_DST_SIZE_HEIGHT(crtc_h));  	mdp4_write(mdp4_kms, REG_MDP4_PIPE_DST_XY(pipe), -			MDP4_PIPE_SRC_XY_X(crtc_x) | -			MDP4_PIPE_SRC_XY_Y(crtc_y)); +			MDP4_PIPE_DST_XY_X(crtc_x) | +			MDP4_PIPE_DST_XY_Y(crtc_y));  	mdp4_plane_set_scanout(plane, fb); -	format = to_mdp4_format(msm_framebuffer_format(fb)); +	format = to_mdp_format(msm_framebuffer_format(fb));  	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_FORMAT(pipe),  			MDP4_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) | @@ -191,7 +197,8 @@ int mdp4_plane_mode_set(struct drm_plane *plane,  	mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEX_STEP(pipe), phasex_step);  	mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEY_STEP(pipe), phasey_step); -	plane->crtc = crtc; +	/* TODO detach from old crtc (if we had more than one) */ +	mdp4_crtc_attach(crtc, plane);  	return 0;  } @@ -202,7 +209,7 @@ static const char *pipe_names[] = {  		"VG3", "VG4",  }; -enum mpd4_pipe mdp4_plane_pipe(struct drm_plane *plane) +enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane)  {  	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);  	return mdp4_plane->pipe; @@ -210,12 +217,12 @@ enum mpd4_pipe mdp4_plane_pipe(struct drm_plane *plane)  /* initialize plane */  struct drm_plane *mdp4_plane_init(struct drm_device *dev, -		enum mpd4_pipe pipe_id, bool private_plane) +		enum mdp4_pipe pipe_id, bool private_plane)  { -	struct msm_drm_private *priv = dev->dev_private;  	struct drm_plane *plane = NULL;  	struct mdp4_plane *mdp4_plane;  	int ret; +	enum drm_plane_type type;  	mdp4_plane = kzalloc(sizeof(*mdp4_plane), GFP_KERNEL);  	if (!mdp4_plane) { @@ -228,8 +235,13 @@ struct drm_plane *mdp4_plane_init(struct drm_device *dev,  	mdp4_plane->pipe = pipe_id;  	mdp4_plane->name = pipe_names[pipe_id]; -	drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &mdp4_plane_funcs, -			mdp4_plane->formats, mdp4_plane->nformats, private_plane); +	mdp4_plane->nformats = mdp4_get_formats(pipe_id, mdp4_plane->formats, +			ARRAY_SIZE(mdp4_plane->formats)); + +	type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; +	drm_universal_plane_init(dev, plane, 0xff, &mdp4_plane_funcs, +				 mdp4_plane->formats, mdp4_plane->nformats, +				 type);  	mdp4_plane_install_properties(plane, &plane->base); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h new file mode 100644 index 00000000000..0aa51517f82 --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h @@ -0,0 +1,1036 @@ +#ifndef MDP5_XML +#define MDP5_XML + +/* Autogenerated file, DO NOT EDIT manually! + +This file was generated by the rules-ng-ng headergen tool in this git repository: +http://github.com/freedreno/envytools/ +git clone https://github.com/freedreno/envytools.git + +The rules-ng-ng source files this header was generated from are: +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    647 bytes, from 2013-11-30 14:45:35) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml            (  17996 bytes, from 2013-12-01 19:10:31) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml      (   1615 bytes, from 2013-11-30 15:00:52) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml            (  22517 bytes, from 2013-12-03 20:59:13) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml         (    600 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  20932 bytes, from 2013-12-01 15:13:04) + +Copyright (C) 2013 by the following authors: +- Rob Clark <robdclark@gmail.com> (robclark) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice (including the +next paragraph) shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + + +enum mdp5_intf { +	INTF_DSI = 1, +	INTF_HDMI = 3, +	INTF_LCDC = 5, +	INTF_eDP = 9, +}; + +enum mdp5_intfnum { +	NO_INTF = 0, +	INTF0 = 1, +	INTF1 = 2, +	INTF2 = 3, +	INTF3 = 4, +}; + +enum mdp5_pipe { +	SSPP_VIG0 = 0, +	SSPP_VIG1 = 1, +	SSPP_VIG2 = 2, +	SSPP_RGB0 = 3, +	SSPP_RGB1 = 4, +	SSPP_RGB2 = 5, +	SSPP_DMA0 = 6, +	SSPP_DMA1 = 7, +}; + +enum mdp5_ctl_mode { +	MODE_NONE = 0, +	MODE_ROT0 = 1, +	MODE_ROT1 = 2, +	MODE_WB0 = 3, +	MODE_WB1 = 4, +	MODE_WFD = 5, +}; + +enum mdp5_pack_3d { +	PACK_3D_FRAME_INT = 0, +	PACK_3D_H_ROW_INT = 1, +	PACK_3D_V_ROW_INT = 2, +	PACK_3D_COL_INT = 3, +}; + +enum mdp5_chroma_samp_type { +	CHROMA_RGB = 0, +	CHROMA_H2V1 = 1, +	CHROMA_H1V2 = 2, +	CHROMA_420 = 3, +}; + +enum mdp5_scale_filter { +	SCALE_FILTER_NEAREST = 0, +	SCALE_FILTER_BIL = 1, +	SCALE_FILTER_PCMN = 2, +	SCALE_FILTER_CA = 3, +}; + +enum mdp5_pipe_bwc { +	BWC_LOSSLESS = 0, +	BWC_Q_HIGH = 1, +	BWC_Q_MED = 2, +}; + +enum mdp5_client_id { +	CID_UNUSED = 0, +	CID_VIG0_Y = 1, +	CID_VIG0_CR = 2, +	CID_VIG0_CB = 3, +	CID_VIG1_Y = 4, +	CID_VIG1_CR = 5, +	CID_VIG1_CB = 6, +	CID_VIG2_Y = 7, +	CID_VIG2_CR = 8, +	CID_VIG2_CB = 9, +	CID_DMA0_Y = 10, +	CID_DMA0_CR = 11, +	CID_DMA0_CB = 12, +	CID_DMA1_Y = 13, +	CID_DMA1_CR = 14, +	CID_DMA1_CB = 15, +	CID_RGB0 = 16, +	CID_RGB1 = 17, +	CID_RGB2 = 18, +	CID_MAX = 19, +}; + +enum mdp5_igc_type { +	IGC_VIG = 0, +	IGC_RGB = 1, +	IGC_DMA = 2, +	IGC_DSPP = 3, +}; + +#define MDP5_IRQ_INTF0_WB_ROT_COMP				0x00000001 +#define MDP5_IRQ_INTF1_WB_ROT_COMP				0x00000002 +#define MDP5_IRQ_INTF2_WB_ROT_COMP				0x00000004 +#define MDP5_IRQ_INTF3_WB_ROT_COMP				0x00000008 +#define MDP5_IRQ_INTF0_WB_WFD					0x00000010 +#define MDP5_IRQ_INTF1_WB_WFD					0x00000020 +#define MDP5_IRQ_INTF2_WB_WFD					0x00000040 +#define MDP5_IRQ_INTF3_WB_WFD					0x00000080 +#define MDP5_IRQ_INTF0_PING_PONG_COMP				0x00000100 +#define MDP5_IRQ_INTF1_PING_PONG_COMP				0x00000200 +#define MDP5_IRQ_INTF2_PING_PONG_COMP				0x00000400 +#define MDP5_IRQ_INTF3_PING_PONG_COMP				0x00000800 +#define MDP5_IRQ_INTF0_PING_PONG_RD_PTR				0x00001000 +#define MDP5_IRQ_INTF1_PING_PONG_RD_PTR				0x00002000 +#define MDP5_IRQ_INTF2_PING_PONG_RD_PTR				0x00004000 +#define MDP5_IRQ_INTF3_PING_PONG_RD_PTR				0x00008000 +#define MDP5_IRQ_INTF0_PING_PONG_WR_PTR				0x00010000 +#define MDP5_IRQ_INTF1_PING_PONG_WR_PTR				0x00020000 +#define MDP5_IRQ_INTF2_PING_PONG_WR_PTR				0x00040000 +#define MDP5_IRQ_INTF3_PING_PONG_WR_PTR				0x00080000 +#define MDP5_IRQ_INTF0_PING_PONG_AUTO_REF			0x00100000 +#define MDP5_IRQ_INTF1_PING_PONG_AUTO_REF			0x00200000 +#define MDP5_IRQ_INTF2_PING_PONG_AUTO_REF			0x00400000 +#define MDP5_IRQ_INTF3_PING_PONG_AUTO_REF			0x00800000 +#define MDP5_IRQ_INTF0_UNDER_RUN				0x01000000 +#define MDP5_IRQ_INTF0_VSYNC					0x02000000 +#define MDP5_IRQ_INTF1_UNDER_RUN				0x04000000 +#define MDP5_IRQ_INTF1_VSYNC					0x08000000 +#define MDP5_IRQ_INTF2_UNDER_RUN				0x10000000 +#define MDP5_IRQ_INTF2_VSYNC					0x20000000 +#define MDP5_IRQ_INTF3_UNDER_RUN				0x40000000 +#define MDP5_IRQ_INTF3_VSYNC					0x80000000 +#define REG_MDP5_HW_VERSION					0x00000000 + +#define REG_MDP5_HW_INTR_STATUS					0x00000010 +#define MDP5_HW_INTR_STATUS_INTR_MDP				0x00000001 +#define MDP5_HW_INTR_STATUS_INTR_DSI0				0x00000010 +#define MDP5_HW_INTR_STATUS_INTR_DSI1				0x00000020 +#define MDP5_HW_INTR_STATUS_INTR_HDMI				0x00000100 +#define MDP5_HW_INTR_STATUS_INTR_EDP				0x00001000 + +#define REG_MDP5_MDP_VERSION					0x00000100 +#define MDP5_MDP_VERSION_MINOR__MASK				0x00ff0000 +#define MDP5_MDP_VERSION_MINOR__SHIFT				16 +static inline uint32_t MDP5_MDP_VERSION_MINOR(uint32_t val) +{ +	return ((val) << MDP5_MDP_VERSION_MINOR__SHIFT) & MDP5_MDP_VERSION_MINOR__MASK; +} +#define MDP5_MDP_VERSION_MAJOR__MASK				0xf0000000 +#define MDP5_MDP_VERSION_MAJOR__SHIFT				28 +static inline uint32_t MDP5_MDP_VERSION_MAJOR(uint32_t val) +{ +	return ((val) << MDP5_MDP_VERSION_MAJOR__SHIFT) & MDP5_MDP_VERSION_MAJOR__MASK; +} + +#define REG_MDP5_DISP_INTF_SEL					0x00000104 +#define MDP5_DISP_INTF_SEL_INTF0__MASK				0x000000ff +#define MDP5_DISP_INTF_SEL_INTF0__SHIFT				0 +static inline uint32_t MDP5_DISP_INTF_SEL_INTF0(enum mdp5_intf val) +{ +	return ((val) << MDP5_DISP_INTF_SEL_INTF0__SHIFT) & MDP5_DISP_INTF_SEL_INTF0__MASK; +} +#define MDP5_DISP_INTF_SEL_INTF1__MASK				0x0000ff00 +#define MDP5_DISP_INTF_SEL_INTF1__SHIFT				8 +static inline uint32_t MDP5_DISP_INTF_SEL_INTF1(enum mdp5_intf val) +{ +	return ((val) << MDP5_DISP_INTF_SEL_INTF1__SHIFT) & MDP5_DISP_INTF_SEL_INTF1__MASK; +} +#define MDP5_DISP_INTF_SEL_INTF2__MASK				0x00ff0000 +#define MDP5_DISP_INTF_SEL_INTF2__SHIFT				16 +static inline uint32_t MDP5_DISP_INTF_SEL_INTF2(enum mdp5_intf val) +{ +	return ((val) << MDP5_DISP_INTF_SEL_INTF2__SHIFT) & MDP5_DISP_INTF_SEL_INTF2__MASK; +} +#define MDP5_DISP_INTF_SEL_INTF3__MASK				0xff000000 +#define MDP5_DISP_INTF_SEL_INTF3__SHIFT				24 +static inline uint32_t MDP5_DISP_INTF_SEL_INTF3(enum mdp5_intf val) +{ +	return ((val) << MDP5_DISP_INTF_SEL_INTF3__SHIFT) & MDP5_DISP_INTF_SEL_INTF3__MASK; +} + +#define REG_MDP5_INTR_EN					0x00000110 + +#define REG_MDP5_INTR_STATUS					0x00000114 + +#define REG_MDP5_INTR_CLEAR					0x00000118 + +#define REG_MDP5_HIST_INTR_EN					0x0000011c + +#define REG_MDP5_HIST_INTR_STATUS				0x00000120 + +#define REG_MDP5_HIST_INTR_CLEAR				0x00000124 + +static inline uint32_t REG_MDP5_SMP_ALLOC_W(uint32_t i0) { return 0x00000180 + 0x4*i0; } + +static inline uint32_t REG_MDP5_SMP_ALLOC_W_REG(uint32_t i0) { return 0x00000180 + 0x4*i0; } +#define MDP5_SMP_ALLOC_W_REG_CLIENT0__MASK			0x000000ff +#define MDP5_SMP_ALLOC_W_REG_CLIENT0__SHIFT			0 +static inline uint32_t MDP5_SMP_ALLOC_W_REG_CLIENT0(enum mdp5_client_id val) +{ +	return ((val) << MDP5_SMP_ALLOC_W_REG_CLIENT0__SHIFT) & MDP5_SMP_ALLOC_W_REG_CLIENT0__MASK; +} +#define MDP5_SMP_ALLOC_W_REG_CLIENT1__MASK			0x0000ff00 +#define MDP5_SMP_ALLOC_W_REG_CLIENT1__SHIFT			8 +static inline uint32_t MDP5_SMP_ALLOC_W_REG_CLIENT1(enum mdp5_client_id val) +{ +	return ((val) << MDP5_SMP_ALLOC_W_REG_CLIENT1__SHIFT) & MDP5_SMP_ALLOC_W_REG_CLIENT1__MASK; +} +#define MDP5_SMP_ALLOC_W_REG_CLIENT2__MASK			0x00ff0000 +#define MDP5_SMP_ALLOC_W_REG_CLIENT2__SHIFT			16 +static inline uint32_t MDP5_SMP_ALLOC_W_REG_CLIENT2(enum mdp5_client_id val) +{ +	return ((val) << MDP5_SMP_ALLOC_W_REG_CLIENT2__SHIFT) & MDP5_SMP_ALLOC_W_REG_CLIENT2__MASK; +} + +static inline uint32_t REG_MDP5_SMP_ALLOC_R(uint32_t i0) { return 0x00000230 + 0x4*i0; } + +static inline uint32_t REG_MDP5_SMP_ALLOC_R_REG(uint32_t i0) { return 0x00000230 + 0x4*i0; } +#define MDP5_SMP_ALLOC_R_REG_CLIENT0__MASK			0x000000ff +#define MDP5_SMP_ALLOC_R_REG_CLIENT0__SHIFT			0 +static inline uint32_t MDP5_SMP_ALLOC_R_REG_CLIENT0(enum mdp5_client_id val) +{ +	return ((val) << MDP5_SMP_ALLOC_R_REG_CLIENT0__SHIFT) & MDP5_SMP_ALLOC_R_REG_CLIENT0__MASK; +} +#define MDP5_SMP_ALLOC_R_REG_CLIENT1__MASK			0x0000ff00 +#define MDP5_SMP_ALLOC_R_REG_CLIENT1__SHIFT			8 +static inline uint32_t MDP5_SMP_ALLOC_R_REG_CLIENT1(enum mdp5_client_id val) +{ +	return ((val) << MDP5_SMP_ALLOC_R_REG_CLIENT1__SHIFT) & MDP5_SMP_ALLOC_R_REG_CLIENT1__MASK; +} +#define MDP5_SMP_ALLOC_R_REG_CLIENT2__MASK			0x00ff0000 +#define MDP5_SMP_ALLOC_R_REG_CLIENT2__SHIFT			16 +static inline uint32_t MDP5_SMP_ALLOC_R_REG_CLIENT2(enum mdp5_client_id val) +{ +	return ((val) << MDP5_SMP_ALLOC_R_REG_CLIENT2__SHIFT) & MDP5_SMP_ALLOC_R_REG_CLIENT2__MASK; +} + +static inline uint32_t __offset_IGC(enum mdp5_igc_type idx) +{ +	switch (idx) { +		case IGC_VIG: return 0x00000300; +		case IGC_RGB: return 0x00000310; +		case IGC_DMA: return 0x00000320; +		case IGC_DSPP: return 0x00000400; +		default: return INVALID_IDX(idx); +	} +} +static inline uint32_t REG_MDP5_IGC(enum mdp5_igc_type i0) { return 0x00000000 + __offset_IGC(i0); } + +static inline uint32_t REG_MDP5_IGC_LUT(enum mdp5_igc_type i0, uint32_t i1) { return 0x00000000 + __offset_IGC(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP5_IGC_LUT_REG(enum mdp5_igc_type i0, uint32_t i1) { return 0x00000000 + __offset_IGC(i0) + 0x4*i1; } +#define MDP5_IGC_LUT_REG_VAL__MASK				0x00000fff +#define MDP5_IGC_LUT_REG_VAL__SHIFT				0 +static inline uint32_t MDP5_IGC_LUT_REG_VAL(uint32_t val) +{ +	return ((val) << MDP5_IGC_LUT_REG_VAL__SHIFT) & MDP5_IGC_LUT_REG_VAL__MASK; +} +#define MDP5_IGC_LUT_REG_INDEX_UPDATE				0x02000000 +#define MDP5_IGC_LUT_REG_DISABLE_PIPE_0				0x10000000 +#define MDP5_IGC_LUT_REG_DISABLE_PIPE_1				0x20000000 +#define MDP5_IGC_LUT_REG_DISABLE_PIPE_2				0x40000000 + +static inline uint32_t REG_MDP5_CTL(uint32_t i0) { return 0x00000600 + 0x100*i0; } + +static inline uint32_t REG_MDP5_CTL_LAYER(uint32_t i0, uint32_t i1) { return 0x00000600 + 0x100*i0 + 0x4*i1; } + +static inline uint32_t REG_MDP5_CTL_LAYER_REG(uint32_t i0, uint32_t i1) { return 0x00000600 + 0x100*i0 + 0x4*i1; } +#define MDP5_CTL_LAYER_REG_VIG0__MASK				0x00000007 +#define MDP5_CTL_LAYER_REG_VIG0__SHIFT				0 +static inline uint32_t MDP5_CTL_LAYER_REG_VIG0(enum mdp_mixer_stage_id val) +{ +	return ((val) << MDP5_CTL_LAYER_REG_VIG0__SHIFT) & MDP5_CTL_LAYER_REG_VIG0__MASK; +} +#define MDP5_CTL_LAYER_REG_VIG1__MASK				0x00000038 +#define MDP5_CTL_LAYER_REG_VIG1__SHIFT				3 +static inline uint32_t MDP5_CTL_LAYER_REG_VIG1(enum mdp_mixer_stage_id val) +{ +	return ((val) << MDP5_CTL_LAYER_REG_VIG1__SHIFT) & MDP5_CTL_LAYER_REG_VIG1__MASK; +} +#define MDP5_CTL_LAYER_REG_VIG2__MASK				0x000001c0 +#define MDP5_CTL_LAYER_REG_VIG2__SHIFT				6 +static inline uint32_t MDP5_CTL_LAYER_REG_VIG2(enum mdp_mixer_stage_id val) +{ +	return ((val) << MDP5_CTL_LAYER_REG_VIG2__SHIFT) & MDP5_CTL_LAYER_REG_VIG2__MASK; +} +#define MDP5_CTL_LAYER_REG_RGB0__MASK				0x00000e00 +#define MDP5_CTL_LAYER_REG_RGB0__SHIFT				9 +static inline uint32_t MDP5_CTL_LAYER_REG_RGB0(enum mdp_mixer_stage_id val) +{ +	return ((val) << MDP5_CTL_LAYER_REG_RGB0__SHIFT) & MDP5_CTL_LAYER_REG_RGB0__MASK; +} +#define MDP5_CTL_LAYER_REG_RGB1__MASK				0x00007000 +#define MDP5_CTL_LAYER_REG_RGB1__SHIFT				12 +static inline uint32_t MDP5_CTL_LAYER_REG_RGB1(enum mdp_mixer_stage_id val) +{ +	return ((val) << MDP5_CTL_LAYER_REG_RGB1__SHIFT) & MDP5_CTL_LAYER_REG_RGB1__MASK; +} +#define MDP5_CTL_LAYER_REG_RGB2__MASK				0x00038000 +#define MDP5_CTL_LAYER_REG_RGB2__SHIFT				15 +static inline uint32_t MDP5_CTL_LAYER_REG_RGB2(enum mdp_mixer_stage_id val) +{ +	return ((val) << MDP5_CTL_LAYER_REG_RGB2__SHIFT) & MDP5_CTL_LAYER_REG_RGB2__MASK; +} +#define MDP5_CTL_LAYER_REG_DMA0__MASK				0x001c0000 +#define MDP5_CTL_LAYER_REG_DMA0__SHIFT				18 +static inline uint32_t MDP5_CTL_LAYER_REG_DMA0(enum mdp_mixer_stage_id val) +{ +	return ((val) << MDP5_CTL_LAYER_REG_DMA0__SHIFT) & MDP5_CTL_LAYER_REG_DMA0__MASK; +} +#define MDP5_CTL_LAYER_REG_DMA1__MASK				0x00e00000 +#define MDP5_CTL_LAYER_REG_DMA1__SHIFT				21 +static inline uint32_t MDP5_CTL_LAYER_REG_DMA1(enum mdp_mixer_stage_id val) +{ +	return ((val) << MDP5_CTL_LAYER_REG_DMA1__SHIFT) & MDP5_CTL_LAYER_REG_DMA1__MASK; +} +#define MDP5_CTL_LAYER_REG_BORDER_COLOR				0x01000000 +#define MDP5_CTL_LAYER_REG_CURSOR_OUT				0x02000000 + +static inline uint32_t REG_MDP5_CTL_OP(uint32_t i0) { return 0x00000614 + 0x100*i0; } +#define MDP5_CTL_OP_MODE__MASK					0x0000000f +#define MDP5_CTL_OP_MODE__SHIFT					0 +static inline uint32_t MDP5_CTL_OP_MODE(enum mdp5_ctl_mode val) +{ +	return ((val) << MDP5_CTL_OP_MODE__SHIFT) & MDP5_CTL_OP_MODE__MASK; +} +#define MDP5_CTL_OP_INTF_NUM__MASK				0x00000070 +#define MDP5_CTL_OP_INTF_NUM__SHIFT				4 +static inline uint32_t MDP5_CTL_OP_INTF_NUM(enum mdp5_intfnum val) +{ +	return ((val) << MDP5_CTL_OP_INTF_NUM__SHIFT) & MDP5_CTL_OP_INTF_NUM__MASK; +} +#define MDP5_CTL_OP_CMD_MODE					0x00020000 +#define MDP5_CTL_OP_PACK_3D_ENABLE				0x00080000 +#define MDP5_CTL_OP_PACK_3D__MASK				0x00300000 +#define MDP5_CTL_OP_PACK_3D__SHIFT				20 +static inline uint32_t MDP5_CTL_OP_PACK_3D(enum mdp5_pack_3d val) +{ +	return ((val) << MDP5_CTL_OP_PACK_3D__SHIFT) & MDP5_CTL_OP_PACK_3D__MASK; +} + +static inline uint32_t REG_MDP5_CTL_FLUSH(uint32_t i0) { return 0x00000618 + 0x100*i0; } +#define MDP5_CTL_FLUSH_VIG0					0x00000001 +#define MDP5_CTL_FLUSH_VIG1					0x00000002 +#define MDP5_CTL_FLUSH_VIG2					0x00000004 +#define MDP5_CTL_FLUSH_RGB0					0x00000008 +#define MDP5_CTL_FLUSH_RGB1					0x00000010 +#define MDP5_CTL_FLUSH_RGB2					0x00000020 +#define MDP5_CTL_FLUSH_LM0					0x00000040 +#define MDP5_CTL_FLUSH_LM1					0x00000080 +#define MDP5_CTL_FLUSH_LM2					0x00000100 +#define MDP5_CTL_FLUSH_DMA0					0x00000800 +#define MDP5_CTL_FLUSH_DMA1					0x00001000 +#define MDP5_CTL_FLUSH_DSPP0					0x00002000 +#define MDP5_CTL_FLUSH_DSPP1					0x00004000 +#define MDP5_CTL_FLUSH_DSPP2					0x00008000 +#define MDP5_CTL_FLUSH_CTL					0x00020000 + +static inline uint32_t REG_MDP5_CTL_START(uint32_t i0) { return 0x0000061c + 0x100*i0; } + +static inline uint32_t REG_MDP5_CTL_PACK_3D(uint32_t i0) { return 0x00000620 + 0x100*i0; } + +static inline uint32_t REG_MDP5_PIPE(enum mdp5_pipe i0) { return 0x00001200 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_HIST_CTL_BASE(enum mdp5_pipe i0) { return 0x000014c4 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_HIST_LUT_BASE(enum mdp5_pipe i0) { return 0x000014f0 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_HIST_LUT_SWAP(enum mdp5_pipe i0) { return 0x00001500 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_SRC_SIZE(enum mdp5_pipe i0) { return 0x00001200 + 0x400*i0; } +#define MDP5_PIPE_SRC_SIZE_HEIGHT__MASK				0xffff0000 +#define MDP5_PIPE_SRC_SIZE_HEIGHT__SHIFT			16 +static inline uint32_t MDP5_PIPE_SRC_SIZE_HEIGHT(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_SIZE_HEIGHT__SHIFT) & MDP5_PIPE_SRC_SIZE_HEIGHT__MASK; +} +#define MDP5_PIPE_SRC_SIZE_WIDTH__MASK				0x0000ffff +#define MDP5_PIPE_SRC_SIZE_WIDTH__SHIFT				0 +static inline uint32_t MDP5_PIPE_SRC_SIZE_WIDTH(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_SIZE_WIDTH__SHIFT) & MDP5_PIPE_SRC_SIZE_WIDTH__MASK; +} + +static inline uint32_t REG_MDP5_PIPE_SRC_IMG_SIZE(enum mdp5_pipe i0) { return 0x00001204 + 0x400*i0; } +#define MDP5_PIPE_SRC_IMG_SIZE_HEIGHT__MASK			0xffff0000 +#define MDP5_PIPE_SRC_IMG_SIZE_HEIGHT__SHIFT			16 +static inline uint32_t MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_IMG_SIZE_HEIGHT__SHIFT) & MDP5_PIPE_SRC_IMG_SIZE_HEIGHT__MASK; +} +#define MDP5_PIPE_SRC_IMG_SIZE_WIDTH__MASK			0x0000ffff +#define MDP5_PIPE_SRC_IMG_SIZE_WIDTH__SHIFT			0 +static inline uint32_t MDP5_PIPE_SRC_IMG_SIZE_WIDTH(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_IMG_SIZE_WIDTH__SHIFT) & MDP5_PIPE_SRC_IMG_SIZE_WIDTH__MASK; +} + +static inline uint32_t REG_MDP5_PIPE_SRC_XY(enum mdp5_pipe i0) { return 0x00001208 + 0x400*i0; } +#define MDP5_PIPE_SRC_XY_Y__MASK				0xffff0000 +#define MDP5_PIPE_SRC_XY_Y__SHIFT				16 +static inline uint32_t MDP5_PIPE_SRC_XY_Y(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_XY_Y__SHIFT) & MDP5_PIPE_SRC_XY_Y__MASK; +} +#define MDP5_PIPE_SRC_XY_X__MASK				0x0000ffff +#define MDP5_PIPE_SRC_XY_X__SHIFT				0 +static inline uint32_t MDP5_PIPE_SRC_XY_X(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_XY_X__SHIFT) & MDP5_PIPE_SRC_XY_X__MASK; +} + +static inline uint32_t REG_MDP5_PIPE_OUT_SIZE(enum mdp5_pipe i0) { return 0x0000120c + 0x400*i0; } +#define MDP5_PIPE_OUT_SIZE_HEIGHT__MASK				0xffff0000 +#define MDP5_PIPE_OUT_SIZE_HEIGHT__SHIFT			16 +static inline uint32_t MDP5_PIPE_OUT_SIZE_HEIGHT(uint32_t val) +{ +	return ((val) << MDP5_PIPE_OUT_SIZE_HEIGHT__SHIFT) & MDP5_PIPE_OUT_SIZE_HEIGHT__MASK; +} +#define MDP5_PIPE_OUT_SIZE_WIDTH__MASK				0x0000ffff +#define MDP5_PIPE_OUT_SIZE_WIDTH__SHIFT				0 +static inline uint32_t MDP5_PIPE_OUT_SIZE_WIDTH(uint32_t val) +{ +	return ((val) << MDP5_PIPE_OUT_SIZE_WIDTH__SHIFT) & MDP5_PIPE_OUT_SIZE_WIDTH__MASK; +} + +static inline uint32_t REG_MDP5_PIPE_OUT_XY(enum mdp5_pipe i0) { return 0x00001210 + 0x400*i0; } +#define MDP5_PIPE_OUT_XY_Y__MASK				0xffff0000 +#define MDP5_PIPE_OUT_XY_Y__SHIFT				16 +static inline uint32_t MDP5_PIPE_OUT_XY_Y(uint32_t val) +{ +	return ((val) << MDP5_PIPE_OUT_XY_Y__SHIFT) & MDP5_PIPE_OUT_XY_Y__MASK; +} +#define MDP5_PIPE_OUT_XY_X__MASK				0x0000ffff +#define MDP5_PIPE_OUT_XY_X__SHIFT				0 +static inline uint32_t MDP5_PIPE_OUT_XY_X(uint32_t val) +{ +	return ((val) << MDP5_PIPE_OUT_XY_X__SHIFT) & MDP5_PIPE_OUT_XY_X__MASK; +} + +static inline uint32_t REG_MDP5_PIPE_SRC0_ADDR(enum mdp5_pipe i0) { return 0x00001214 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_SRC1_ADDR(enum mdp5_pipe i0) { return 0x00001218 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_SRC2_ADDR(enum mdp5_pipe i0) { return 0x0000121c + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_SRC3_ADDR(enum mdp5_pipe i0) { return 0x00001220 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_SRC_STRIDE_A(enum mdp5_pipe i0) { return 0x00001224 + 0x400*i0; } +#define MDP5_PIPE_SRC_STRIDE_A_P0__MASK				0x0000ffff +#define MDP5_PIPE_SRC_STRIDE_A_P0__SHIFT			0 +static inline uint32_t MDP5_PIPE_SRC_STRIDE_A_P0(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_STRIDE_A_P0__SHIFT) & MDP5_PIPE_SRC_STRIDE_A_P0__MASK; +} +#define MDP5_PIPE_SRC_STRIDE_A_P1__MASK				0xffff0000 +#define MDP5_PIPE_SRC_STRIDE_A_P1__SHIFT			16 +static inline uint32_t MDP5_PIPE_SRC_STRIDE_A_P1(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_STRIDE_A_P1__SHIFT) & MDP5_PIPE_SRC_STRIDE_A_P1__MASK; +} + +static inline uint32_t REG_MDP5_PIPE_SRC_STRIDE_B(enum mdp5_pipe i0) { return 0x00001228 + 0x400*i0; } +#define MDP5_PIPE_SRC_STRIDE_B_P2__MASK				0x0000ffff +#define MDP5_PIPE_SRC_STRIDE_B_P2__SHIFT			0 +static inline uint32_t MDP5_PIPE_SRC_STRIDE_B_P2(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_STRIDE_B_P2__SHIFT) & MDP5_PIPE_SRC_STRIDE_B_P2__MASK; +} +#define MDP5_PIPE_SRC_STRIDE_B_P3__MASK				0xffff0000 +#define MDP5_PIPE_SRC_STRIDE_B_P3__SHIFT			16 +static inline uint32_t MDP5_PIPE_SRC_STRIDE_B_P3(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_STRIDE_B_P3__SHIFT) & MDP5_PIPE_SRC_STRIDE_B_P3__MASK; +} + +static inline uint32_t REG_MDP5_PIPE_STILE_FRAME_SIZE(enum mdp5_pipe i0) { return 0x0000122c + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_SRC_FORMAT(enum mdp5_pipe i0) { return 0x00001230 + 0x400*i0; } +#define MDP5_PIPE_SRC_FORMAT_G_BPC__MASK			0x00000003 +#define MDP5_PIPE_SRC_FORMAT_G_BPC__SHIFT			0 +static inline uint32_t MDP5_PIPE_SRC_FORMAT_G_BPC(enum mdp_bpc val) +{ +	return ((val) << MDP5_PIPE_SRC_FORMAT_G_BPC__SHIFT) & MDP5_PIPE_SRC_FORMAT_G_BPC__MASK; +} +#define MDP5_PIPE_SRC_FORMAT_B_BPC__MASK			0x0000000c +#define MDP5_PIPE_SRC_FORMAT_B_BPC__SHIFT			2 +static inline uint32_t MDP5_PIPE_SRC_FORMAT_B_BPC(enum mdp_bpc val) +{ +	return ((val) << MDP5_PIPE_SRC_FORMAT_B_BPC__SHIFT) & MDP5_PIPE_SRC_FORMAT_B_BPC__MASK; +} +#define MDP5_PIPE_SRC_FORMAT_R_BPC__MASK			0x00000030 +#define MDP5_PIPE_SRC_FORMAT_R_BPC__SHIFT			4 +static inline uint32_t MDP5_PIPE_SRC_FORMAT_R_BPC(enum mdp_bpc val) +{ +	return ((val) << MDP5_PIPE_SRC_FORMAT_R_BPC__SHIFT) & MDP5_PIPE_SRC_FORMAT_R_BPC__MASK; +} +#define MDP5_PIPE_SRC_FORMAT_A_BPC__MASK			0x000000c0 +#define MDP5_PIPE_SRC_FORMAT_A_BPC__SHIFT			6 +static inline uint32_t MDP5_PIPE_SRC_FORMAT_A_BPC(enum mdp_bpc_alpha val) +{ +	return ((val) << MDP5_PIPE_SRC_FORMAT_A_BPC__SHIFT) & MDP5_PIPE_SRC_FORMAT_A_BPC__MASK; +} +#define MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE			0x00000100 +#define MDP5_PIPE_SRC_FORMAT_CPP__MASK				0x00000600 +#define MDP5_PIPE_SRC_FORMAT_CPP__SHIFT				9 +static inline uint32_t MDP5_PIPE_SRC_FORMAT_CPP(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_FORMAT_CPP__SHIFT) & MDP5_PIPE_SRC_FORMAT_CPP__MASK; +} +#define MDP5_PIPE_SRC_FORMAT_ROT90				0x00000800 +#define MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT__MASK			0x00003000 +#define MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT__SHIFT		12 +static inline uint32_t MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT__SHIFT) & MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT__MASK; +} +#define MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT			0x00020000 +#define MDP5_PIPE_SRC_FORMAT_UNPACK_ALIGN_MSB			0x00040000 +#define MDP5_PIPE_SRC_FORMAT_NUM_PLANES__MASK			0x00780000 +#define MDP5_PIPE_SRC_FORMAT_NUM_PLANES__SHIFT			19 +static inline uint32_t MDP5_PIPE_SRC_FORMAT_NUM_PLANES(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_FORMAT_NUM_PLANES__SHIFT) & MDP5_PIPE_SRC_FORMAT_NUM_PLANES__MASK; +} +#define MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP__MASK			0x01800000 +#define MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP__SHIFT			23 +static inline uint32_t MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(enum mdp5_chroma_samp_type val) +{ +	return ((val) << MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP__SHIFT) & MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP__MASK; +} + +static inline uint32_t REG_MDP5_PIPE_SRC_UNPACK(enum mdp5_pipe i0) { return 0x00001234 + 0x400*i0; } +#define MDP5_PIPE_SRC_UNPACK_ELEM0__MASK			0x000000ff +#define MDP5_PIPE_SRC_UNPACK_ELEM0__SHIFT			0 +static inline uint32_t MDP5_PIPE_SRC_UNPACK_ELEM0(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_UNPACK_ELEM0__SHIFT) & MDP5_PIPE_SRC_UNPACK_ELEM0__MASK; +} +#define MDP5_PIPE_SRC_UNPACK_ELEM1__MASK			0x0000ff00 +#define MDP5_PIPE_SRC_UNPACK_ELEM1__SHIFT			8 +static inline uint32_t MDP5_PIPE_SRC_UNPACK_ELEM1(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_UNPACK_ELEM1__SHIFT) & MDP5_PIPE_SRC_UNPACK_ELEM1__MASK; +} +#define MDP5_PIPE_SRC_UNPACK_ELEM2__MASK			0x00ff0000 +#define MDP5_PIPE_SRC_UNPACK_ELEM2__SHIFT			16 +static inline uint32_t MDP5_PIPE_SRC_UNPACK_ELEM2(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_UNPACK_ELEM2__SHIFT) & MDP5_PIPE_SRC_UNPACK_ELEM2__MASK; +} +#define MDP5_PIPE_SRC_UNPACK_ELEM3__MASK			0xff000000 +#define MDP5_PIPE_SRC_UNPACK_ELEM3__SHIFT			24 +static inline uint32_t MDP5_PIPE_SRC_UNPACK_ELEM3(uint32_t val) +{ +	return ((val) << MDP5_PIPE_SRC_UNPACK_ELEM3__SHIFT) & MDP5_PIPE_SRC_UNPACK_ELEM3__MASK; +} + +static inline uint32_t REG_MDP5_PIPE_SRC_OP_MODE(enum mdp5_pipe i0) { return 0x00001238 + 0x400*i0; } +#define MDP5_PIPE_SRC_OP_MODE_BWC_EN				0x00000001 +#define MDP5_PIPE_SRC_OP_MODE_BWC__MASK				0x00000006 +#define MDP5_PIPE_SRC_OP_MODE_BWC__SHIFT			1 +static inline uint32_t MDP5_PIPE_SRC_OP_MODE_BWC(enum mdp5_pipe_bwc val) +{ +	return ((val) << MDP5_PIPE_SRC_OP_MODE_BWC__SHIFT) & MDP5_PIPE_SRC_OP_MODE_BWC__MASK; +} +#define MDP5_PIPE_SRC_OP_MODE_FLIP_LR				0x00002000 +#define MDP5_PIPE_SRC_OP_MODE_FLIP_UD				0x00004000 +#define MDP5_PIPE_SRC_OP_MODE_IGC_EN				0x00010000 +#define MDP5_PIPE_SRC_OP_MODE_IGC_ROM_0				0x00020000 +#define MDP5_PIPE_SRC_OP_MODE_IGC_ROM_1				0x00040000 +#define MDP5_PIPE_SRC_OP_MODE_DEINTERLACE			0x00400000 +#define MDP5_PIPE_SRC_OP_MODE_DEINTERLACE_ODD			0x00800000 + +static inline uint32_t REG_MDP5_PIPE_SRC_CONSTANT_COLOR(enum mdp5_pipe i0) { return 0x0000123c + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_FETCH_CONFIG(enum mdp5_pipe i0) { return 0x00001248 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_VC1_RANGE(enum mdp5_pipe i0) { return 0x0000124c + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_REQPRIO_FIFO_WM_0(enum mdp5_pipe i0) { return 0x00001250 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_REQPRIO_FIFO_WM_1(enum mdp5_pipe i0) { return 0x00001254 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_REQPRIO_FIFO_WM_2(enum mdp5_pipe i0) { return 0x00001258 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(enum mdp5_pipe i0) { return 0x00001270 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_CURRENT_SRC0_ADDR(enum mdp5_pipe i0) { return 0x000012a4 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_CURRENT_SRC1_ADDR(enum mdp5_pipe i0) { return 0x000012a8 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_CURRENT_SRC2_ADDR(enum mdp5_pipe i0) { return 0x000012ac + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_CURRENT_SRC3_ADDR(enum mdp5_pipe i0) { return 0x000012b0 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_DECIMATION(enum mdp5_pipe i0) { return 0x000012b4 + 0x400*i0; } +#define MDP5_PIPE_DECIMATION_VERT__MASK				0x000000ff +#define MDP5_PIPE_DECIMATION_VERT__SHIFT			0 +static inline uint32_t MDP5_PIPE_DECIMATION_VERT(uint32_t val) +{ +	return ((val) << MDP5_PIPE_DECIMATION_VERT__SHIFT) & MDP5_PIPE_DECIMATION_VERT__MASK; +} +#define MDP5_PIPE_DECIMATION_HORZ__MASK				0x0000ff00 +#define MDP5_PIPE_DECIMATION_HORZ__SHIFT			8 +static inline uint32_t MDP5_PIPE_DECIMATION_HORZ(uint32_t val) +{ +	return ((val) << MDP5_PIPE_DECIMATION_HORZ__SHIFT) & MDP5_PIPE_DECIMATION_HORZ__MASK; +} + +static inline uint32_t REG_MDP5_PIPE_SCALE_CONFIG(enum mdp5_pipe i0) { return 0x00001404 + 0x400*i0; } +#define MDP5_PIPE_SCALE_CONFIG_SCALEX_EN			0x00000001 +#define MDP5_PIPE_SCALE_CONFIG_SCALEY_EN			0x00000002 +#define MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER__MASK		0x00000300 +#define MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER__SHIFT		8 +static inline uint32_t MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER(enum mdp5_scale_filter val) +{ +	return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER__MASK; +} +#define MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER__MASK		0x00000c00 +#define MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER__SHIFT		10 +static inline uint32_t MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER(enum mdp5_scale_filter val) +{ +	return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER__MASK; +} +#define MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER__MASK		0x00003000 +#define MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER__SHIFT		12 +static inline uint32_t MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER(enum mdp5_scale_filter val) +{ +	return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER__MASK; +} +#define MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER__MASK		0x0000c000 +#define MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER__SHIFT		14 +static inline uint32_t MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER(enum mdp5_scale_filter val) +{ +	return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER__MASK; +} +#define MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER__MASK		0x00030000 +#define MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER__SHIFT		16 +static inline uint32_t MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER(enum mdp5_scale_filter val) +{ +	return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER__MASK; +} +#define MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER__MASK		0x000c0000 +#define MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER__SHIFT		18 +static inline uint32_t MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER(enum mdp5_scale_filter val) +{ +	return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER__MASK; +} + +static inline uint32_t REG_MDP5_PIPE_SCALE_PHASE_STEP_X(enum mdp5_pipe i0) { return 0x00001410 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(enum mdp5_pipe i0) { return 0x00001414 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_SCALE_INIT_PHASE_X(enum mdp5_pipe i0) { return 0x00001420 + 0x400*i0; } + +static inline uint32_t REG_MDP5_PIPE_SCALE_INIT_PHASE_Y(enum mdp5_pipe i0) { return 0x00001424 + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM(uint32_t i0) { return 0x00003200 + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_BLEND_COLOR_OUT(uint32_t i0) { return 0x00003200 + 0x400*i0; } +#define MDP5_LM_BLEND_COLOR_OUT_STAGE0_FG_ALPHA			0x00000002 +#define MDP5_LM_BLEND_COLOR_OUT_STAGE1_FG_ALPHA			0x00000004 +#define MDP5_LM_BLEND_COLOR_OUT_STAGE2_FG_ALPHA			0x00000008 +#define MDP5_LM_BLEND_COLOR_OUT_STAGE3_FG_ALPHA			0x00000010 + +static inline uint32_t REG_MDP5_LM_OUT_SIZE(uint32_t i0) { return 0x00003204 + 0x400*i0; } +#define MDP5_LM_OUT_SIZE_HEIGHT__MASK				0xffff0000 +#define MDP5_LM_OUT_SIZE_HEIGHT__SHIFT				16 +static inline uint32_t MDP5_LM_OUT_SIZE_HEIGHT(uint32_t val) +{ +	return ((val) << MDP5_LM_OUT_SIZE_HEIGHT__SHIFT) & MDP5_LM_OUT_SIZE_HEIGHT__MASK; +} +#define MDP5_LM_OUT_SIZE_WIDTH__MASK				0x0000ffff +#define MDP5_LM_OUT_SIZE_WIDTH__SHIFT				0 +static inline uint32_t MDP5_LM_OUT_SIZE_WIDTH(uint32_t val) +{ +	return ((val) << MDP5_LM_OUT_SIZE_WIDTH__SHIFT) & MDP5_LM_OUT_SIZE_WIDTH__MASK; +} + +static inline uint32_t REG_MDP5_LM_BORDER_COLOR_0(uint32_t i0) { return 0x00003208 + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_BORDER_COLOR_1(uint32_t i0) { return 0x00003210 + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_BLEND(uint32_t i0, uint32_t i1) { return 0x00003220 + 0x400*i0 + 0x30*i1; } + +static inline uint32_t REG_MDP5_LM_BLEND_OP_MODE(uint32_t i0, uint32_t i1) { return 0x00003220 + 0x400*i0 + 0x30*i1; } +#define MDP5_LM_BLEND_OP_MODE_FG_ALPHA__MASK			0x00000003 +#define MDP5_LM_BLEND_OP_MODE_FG_ALPHA__SHIFT			0 +static inline uint32_t MDP5_LM_BLEND_OP_MODE_FG_ALPHA(enum mdp_alpha_type val) +{ +	return ((val) << MDP5_LM_BLEND_OP_MODE_FG_ALPHA__SHIFT) & MDP5_LM_BLEND_OP_MODE_FG_ALPHA__MASK; +} +#define MDP5_LM_BLEND_OP_MODE_FG_INV_ALPHA			0x00000004 +#define MDP5_LM_BLEND_OP_MODE_FG_MOD_ALPHA			0x00000008 +#define MDP5_LM_BLEND_OP_MODE_FG_INV_MOD_ALPHA			0x00000010 +#define MDP5_LM_BLEND_OP_MODE_FG_TRANSP_EN			0x00000020 +#define MDP5_LM_BLEND_OP_MODE_BG_ALPHA__MASK			0x00000300 +#define MDP5_LM_BLEND_OP_MODE_BG_ALPHA__SHIFT			8 +static inline uint32_t MDP5_LM_BLEND_OP_MODE_BG_ALPHA(enum mdp_alpha_type val) +{ +	return ((val) << MDP5_LM_BLEND_OP_MODE_BG_ALPHA__SHIFT) & MDP5_LM_BLEND_OP_MODE_BG_ALPHA__MASK; +} +#define MDP5_LM_BLEND_OP_MODE_BG_INV_ALPHA			0x00000400 +#define MDP5_LM_BLEND_OP_MODE_BG_MOD_ALPHA			0x00000800 +#define MDP5_LM_BLEND_OP_MODE_BG_INV_MOD_ALPHA			0x00001000 +#define MDP5_LM_BLEND_OP_MODE_BG_TRANSP_EN			0x00002000 + +static inline uint32_t REG_MDP5_LM_BLEND_FG_ALPHA(uint32_t i0, uint32_t i1) { return 0x00003224 + 0x400*i0 + 0x30*i1; } + +static inline uint32_t REG_MDP5_LM_BLEND_BG_ALPHA(uint32_t i0, uint32_t i1) { return 0x00003228 + 0x400*i0 + 0x30*i1; } + +static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_LOW0(uint32_t i0, uint32_t i1) { return 0x0000322c + 0x400*i0 + 0x30*i1; } + +static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_LOW1(uint32_t i0, uint32_t i1) { return 0x00003230 + 0x400*i0 + 0x30*i1; } + +static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_HIGH0(uint32_t i0, uint32_t i1) { return 0x00003234 + 0x400*i0 + 0x30*i1; } + +static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_HIGH1(uint32_t i0, uint32_t i1) { return 0x00003238 + 0x400*i0 + 0x30*i1; } + +static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_LOW0(uint32_t i0, uint32_t i1) { return 0x0000323c + 0x400*i0 + 0x30*i1; } + +static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_LOW1(uint32_t i0, uint32_t i1) { return 0x00003240 + 0x400*i0 + 0x30*i1; } + +static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_HIGH0(uint32_t i0, uint32_t i1) { return 0x00003244 + 0x400*i0 + 0x30*i1; } + +static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_HIGH1(uint32_t i0, uint32_t i1) { return 0x00003248 + 0x400*i0 + 0x30*i1; } + +static inline uint32_t REG_MDP5_LM_CURSOR_IMG_SIZE(uint32_t i0) { return 0x000032e0 + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_CURSOR_SIZE(uint32_t i0) { return 0x000032e4 + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_CURSOR_XY(uint32_t i0) { return 0x000032e8 + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_CURSOR_STRIDE(uint32_t i0) { return 0x000032dc + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_CURSOR_FORMAT(uint32_t i0) { return 0x000032ec + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_CURSOR_BASE_ADDR(uint32_t i0) { return 0x000032f0 + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_CURSOR_START_XY(uint32_t i0) { return 0x000032f4 + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_CONFIG(uint32_t i0) { return 0x000032f8 + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_PARAM(uint32_t i0) { return 0x000032fc + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_TRANSP_LOW0(uint32_t i0) { return 0x00003300 + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_TRANSP_LOW1(uint32_t i0) { return 0x00003304 + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_TRANSP_HIGH0(uint32_t i0) { return 0x00003308 + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_TRANSP_HIGH1(uint32_t i0) { return 0x0000330c + 0x400*i0; } + +static inline uint32_t REG_MDP5_LM_GC_LUT_BASE(uint32_t i0) { return 0x00003310 + 0x400*i0; } + +static inline uint32_t REG_MDP5_DSPP(uint32_t i0) { return 0x00004600 + 0x400*i0; } + +static inline uint32_t REG_MDP5_DSPP_OP_MODE(uint32_t i0) { return 0x00004600 + 0x400*i0; } +#define MDP5_DSPP_OP_MODE_IGC_LUT_EN				0x00000001 +#define MDP5_DSPP_OP_MODE_IGC_TBL_IDX__MASK			0x0000000e +#define MDP5_DSPP_OP_MODE_IGC_TBL_IDX__SHIFT			1 +static inline uint32_t MDP5_DSPP_OP_MODE_IGC_TBL_IDX(uint32_t val) +{ +	return ((val) << MDP5_DSPP_OP_MODE_IGC_TBL_IDX__SHIFT) & MDP5_DSPP_OP_MODE_IGC_TBL_IDX__MASK; +} +#define MDP5_DSPP_OP_MODE_PCC_EN				0x00000010 +#define MDP5_DSPP_OP_MODE_DITHER_EN				0x00000100 +#define MDP5_DSPP_OP_MODE_HIST_EN				0x00010000 +#define MDP5_DSPP_OP_MODE_AUTO_CLEAR				0x00020000 +#define MDP5_DSPP_OP_MODE_HIST_LUT_EN				0x00080000 +#define MDP5_DSPP_OP_MODE_PA_EN					0x00100000 +#define MDP5_DSPP_OP_MODE_GAMUT_EN				0x00800000 +#define MDP5_DSPP_OP_MODE_GAMUT_ORDER				0x01000000 + +static inline uint32_t REG_MDP5_DSPP_PCC_BASE(uint32_t i0) { return 0x00004630 + 0x400*i0; } + +static inline uint32_t REG_MDP5_DSPP_DITHER_DEPTH(uint32_t i0) { return 0x00004750 + 0x400*i0; } + +static inline uint32_t REG_MDP5_DSPP_HIST_CTL_BASE(uint32_t i0) { return 0x00004810 + 0x400*i0; } + +static inline uint32_t REG_MDP5_DSPP_HIST_LUT_BASE(uint32_t i0) { return 0x00004830 + 0x400*i0; } + +static inline uint32_t REG_MDP5_DSPP_HIST_LUT_SWAP(uint32_t i0) { return 0x00004834 + 0x400*i0; } + +static inline uint32_t REG_MDP5_DSPP_PA_BASE(uint32_t i0) { return 0x00004838 + 0x400*i0; } + +static inline uint32_t REG_MDP5_DSPP_GAMUT_BASE(uint32_t i0) { return 0x000048dc + 0x400*i0; } + +static inline uint32_t REG_MDP5_DSPP_GC_BASE(uint32_t i0) { return 0x000048b0 + 0x400*i0; } + +static inline uint32_t REG_MDP5_INTF(uint32_t i0) { return 0x00012500 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_TIMING_ENGINE_EN(uint32_t i0) { return 0x00012500 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_CONFIG(uint32_t i0) { return 0x00012504 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_HSYNC_CTL(uint32_t i0) { return 0x00012508 + 0x200*i0; } +#define MDP5_INTF_HSYNC_CTL_PULSEW__MASK			0x0000ffff +#define MDP5_INTF_HSYNC_CTL_PULSEW__SHIFT			0 +static inline uint32_t MDP5_INTF_HSYNC_CTL_PULSEW(uint32_t val) +{ +	return ((val) << MDP5_INTF_HSYNC_CTL_PULSEW__SHIFT) & MDP5_INTF_HSYNC_CTL_PULSEW__MASK; +} +#define MDP5_INTF_HSYNC_CTL_PERIOD__MASK			0xffff0000 +#define MDP5_INTF_HSYNC_CTL_PERIOD__SHIFT			16 +static inline uint32_t MDP5_INTF_HSYNC_CTL_PERIOD(uint32_t val) +{ +	return ((val) << MDP5_INTF_HSYNC_CTL_PERIOD__SHIFT) & MDP5_INTF_HSYNC_CTL_PERIOD__MASK; +} + +static inline uint32_t REG_MDP5_INTF_VSYNC_PERIOD_F0(uint32_t i0) { return 0x0001250c + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_VSYNC_PERIOD_F1(uint32_t i0) { return 0x00012510 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_VSYNC_LEN_F0(uint32_t i0) { return 0x00012514 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_VSYNC_LEN_F1(uint32_t i0) { return 0x00012518 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_DISPLAY_VSTART_F0(uint32_t i0) { return 0x0001251c + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_DISPLAY_VSTART_F1(uint32_t i0) { return 0x00012520 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_DISPLAY_VEND_F0(uint32_t i0) { return 0x00012524 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_DISPLAY_VEND_F1(uint32_t i0) { return 0x00012528 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_ACTIVE_VSTART_F0(uint32_t i0) { return 0x0001252c + 0x200*i0; } +#define MDP5_INTF_ACTIVE_VSTART_F0_VAL__MASK			0x7fffffff +#define MDP5_INTF_ACTIVE_VSTART_F0_VAL__SHIFT			0 +static inline uint32_t MDP5_INTF_ACTIVE_VSTART_F0_VAL(uint32_t val) +{ +	return ((val) << MDP5_INTF_ACTIVE_VSTART_F0_VAL__SHIFT) & MDP5_INTF_ACTIVE_VSTART_F0_VAL__MASK; +} +#define MDP5_INTF_ACTIVE_VSTART_F0_ACTIVE_V_ENABLE		0x80000000 + +static inline uint32_t REG_MDP5_INTF_ACTIVE_VSTART_F1(uint32_t i0) { return 0x00012530 + 0x200*i0; } +#define MDP5_INTF_ACTIVE_VSTART_F1_VAL__MASK			0x7fffffff +#define MDP5_INTF_ACTIVE_VSTART_F1_VAL__SHIFT			0 +static inline uint32_t MDP5_INTF_ACTIVE_VSTART_F1_VAL(uint32_t val) +{ +	return ((val) << MDP5_INTF_ACTIVE_VSTART_F1_VAL__SHIFT) & MDP5_INTF_ACTIVE_VSTART_F1_VAL__MASK; +} + +static inline uint32_t REG_MDP5_INTF_ACTIVE_VEND_F0(uint32_t i0) { return 0x00012534 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_ACTIVE_VEND_F1(uint32_t i0) { return 0x00012538 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_DISPLAY_HCTL(uint32_t i0) { return 0x0001253c + 0x200*i0; } +#define MDP5_INTF_DISPLAY_HCTL_START__MASK			0x0000ffff +#define MDP5_INTF_DISPLAY_HCTL_START__SHIFT			0 +static inline uint32_t MDP5_INTF_DISPLAY_HCTL_START(uint32_t val) +{ +	return ((val) << MDP5_INTF_DISPLAY_HCTL_START__SHIFT) & MDP5_INTF_DISPLAY_HCTL_START__MASK; +} +#define MDP5_INTF_DISPLAY_HCTL_END__MASK			0xffff0000 +#define MDP5_INTF_DISPLAY_HCTL_END__SHIFT			16 +static inline uint32_t MDP5_INTF_DISPLAY_HCTL_END(uint32_t val) +{ +	return ((val) << MDP5_INTF_DISPLAY_HCTL_END__SHIFT) & MDP5_INTF_DISPLAY_HCTL_END__MASK; +} + +static inline uint32_t REG_MDP5_INTF_ACTIVE_HCTL(uint32_t i0) { return 0x00012540 + 0x200*i0; } +#define MDP5_INTF_ACTIVE_HCTL_START__MASK			0x00007fff +#define MDP5_INTF_ACTIVE_HCTL_START__SHIFT			0 +static inline uint32_t MDP5_INTF_ACTIVE_HCTL_START(uint32_t val) +{ +	return ((val) << MDP5_INTF_ACTIVE_HCTL_START__SHIFT) & MDP5_INTF_ACTIVE_HCTL_START__MASK; +} +#define MDP5_INTF_ACTIVE_HCTL_END__MASK				0x7fff0000 +#define MDP5_INTF_ACTIVE_HCTL_END__SHIFT			16 +static inline uint32_t MDP5_INTF_ACTIVE_HCTL_END(uint32_t val) +{ +	return ((val) << MDP5_INTF_ACTIVE_HCTL_END__SHIFT) & MDP5_INTF_ACTIVE_HCTL_END__MASK; +} +#define MDP5_INTF_ACTIVE_HCTL_ACTIVE_H_ENABLE			0x80000000 + +static inline uint32_t REG_MDP5_INTF_BORDER_COLOR(uint32_t i0) { return 0x00012544 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_UNDERFLOW_COLOR(uint32_t i0) { return 0x00012548 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_HSYNC_SKEW(uint32_t i0) { return 0x0001254c + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_POLARITY_CTL(uint32_t i0) { return 0x00012550 + 0x200*i0; } +#define MDP5_INTF_POLARITY_CTL_HSYNC_LOW			0x00000001 +#define MDP5_INTF_POLARITY_CTL_VSYNC_LOW			0x00000002 +#define MDP5_INTF_POLARITY_CTL_DATA_EN_LOW			0x00000004 + +static inline uint32_t REG_MDP5_INTF_TEST_CTL(uint32_t i0) { return 0x00012554 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_TP_COLOR0(uint32_t i0) { return 0x00012558 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_TP_COLOR1(uint32_t i0) { return 0x0001255c + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_DSI_CMD_MODE_TRIGGER_EN(uint32_t i0) { return 0x00012584 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_PANEL_FORMAT(uint32_t i0) { return 0x00012590 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_FRAME_LINE_COUNT_EN(uint32_t i0) { return 0x000125a8 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_FRAME_COUNT(uint32_t i0) { return 0x000125ac + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_LINE_COUNT(uint32_t i0) { return 0x000125b0 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_DEFLICKER_CONFIG(uint32_t i0) { return 0x000125f0 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_DEFLICKER_STRNG_COEFF(uint32_t i0) { return 0x000125f4 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_DEFLICKER_WEAK_COEFF(uint32_t i0) { return 0x000125f8 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_TPG_ENABLE(uint32_t i0) { return 0x00012600 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_TPG_MAIN_CONTROL(uint32_t i0) { return 0x00012604 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_TPG_VIDEO_CONFIG(uint32_t i0) { return 0x00012608 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_TPG_COMPONENT_LIMITS(uint32_t i0) { return 0x0001260c + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_TPG_RECTANGLE(uint32_t i0) { return 0x00012610 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_TPG_INITIAL_VALUE(uint32_t i0) { return 0x00012614 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_TPG_BLK_WHITE_PATTERN_FRAME(uint32_t i0) { return 0x00012618 + 0x200*i0; } + +static inline uint32_t REG_MDP5_INTF_TPG_RGB_MAPPING(uint32_t i0) { return 0x0001261c + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD(uint32_t i0) { return 0x00013100 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_BYPASS(uint32_t i0) { return 0x00013100 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_CTRL_0(uint32_t i0) { return 0x00013104 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_CTRL_1(uint32_t i0) { return 0x00013108 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_FRAME_SIZE(uint32_t i0) { return 0x0001310c + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_CON_CTRL_0(uint32_t i0) { return 0x00013110 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_CON_CTRL_1(uint32_t i0) { return 0x00013114 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_STR_MAN(uint32_t i0) { return 0x00013118 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_VAR(uint32_t i0) { return 0x0001311c + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_DITH(uint32_t i0) { return 0x00013120 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_DITH_CTRL(uint32_t i0) { return 0x00013124 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_AMP_LIM(uint32_t i0) { return 0x00013128 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_SLOPE(uint32_t i0) { return 0x0001312c + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_BW_LVL(uint32_t i0) { return 0x00013130 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_LOGO_POS(uint32_t i0) { return 0x00013134 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_LUT_FI(uint32_t i0) { return 0x00013138 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_LUT_CC(uint32_t i0) { return 0x0001317c + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_STR_LIM(uint32_t i0) { return 0x000131c8 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_CALIB_AB(uint32_t i0) { return 0x000131cc + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_CALIB_CD(uint32_t i0) { return 0x000131d0 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_MODE_SEL(uint32_t i0) { return 0x000131d4 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_TFILT_CTRL(uint32_t i0) { return 0x000131d8 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_BL_MINMAX(uint32_t i0) { return 0x000131dc + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_BL(uint32_t i0) { return 0x000131e0 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_BL_MAX(uint32_t i0) { return 0x000131e8 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_AL(uint32_t i0) { return 0x000131ec + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_AL_MIN(uint32_t i0) { return 0x000131f0 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_AL_FILT(uint32_t i0) { return 0x000131f4 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_CFG_BUF(uint32_t i0) { return 0x000131f8 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_LUT_AL(uint32_t i0) { return 0x00013200 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_TARG_STR(uint32_t i0) { return 0x00013244 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_START_CALC(uint32_t i0) { return 0x00013248 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_STR_OUT(uint32_t i0) { return 0x0001324c + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_BL_OUT(uint32_t i0) { return 0x00013254 + 0x200*i0; } + +static inline uint32_t REG_MDP5_AD_CALC_DONE(uint32_t i0) { return 0x00013258 + 0x200*i0; } + + +#endif /* MDP5_XML */ diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c new file mode 100644 index 00000000000..ebe2e60f3ab --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "mdp5_kms.h" + +#include <drm/drm_mode.h> +#include "drm_crtc.h" +#include "drm_crtc_helper.h" +#include "drm_flip_work.h" + +struct mdp5_crtc { +	struct drm_crtc base; +	char name[8]; +	struct drm_plane *plane; +	struct drm_plane *planes[8]; +	int id; +	bool enabled; + +	/* which mixer/encoder we route output to: */ +	int mixer; + +	/* if there is a pending flip, these will be non-null: */ +	struct drm_pending_vblank_event *event; +	struct msm_fence_cb pageflip_cb; + +#define PENDING_CURSOR 0x1 +#define PENDING_FLIP   0x2 +	atomic_t pending; + +	/* the fb that we logically (from PoV of KMS API) hold a ref +	 * to.  Which we may not yet be scanning out (we may still +	 * be scanning out previous in case of page_flip while waiting +	 * for gpu rendering to complete: +	 */ +	struct drm_framebuffer *fb; + +	/* the fb that we currently hold a scanout ref to: */ +	struct drm_framebuffer *scanout_fb; + +	/* for unref'ing framebuffers after scanout completes: */ +	struct drm_flip_work unref_fb_work; + +	struct mdp_irq vblank; +	struct mdp_irq err; +}; +#define to_mdp5_crtc(x) container_of(x, struct mdp5_crtc, base) + +static struct mdp5_kms *get_kms(struct drm_crtc *crtc) +{ +	struct msm_drm_private *priv = crtc->dev->dev_private; +	return to_mdp5_kms(to_mdp_kms(priv->kms)); +} + +static void request_pending(struct drm_crtc *crtc, uint32_t pending) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + +	atomic_or(pending, &mdp5_crtc->pending); +	mdp_irq_register(&get_kms(crtc)->base, &mdp5_crtc->vblank); +} + +static void crtc_flush(struct drm_crtc *crtc) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); +	struct mdp5_kms *mdp5_kms = get_kms(crtc); +	int id = mdp5_crtc->id; +	uint32_t i, flush = 0; + +	for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) { +		struct drm_plane *plane = mdp5_crtc->planes[i]; +		if (plane) { +			enum mdp5_pipe pipe = mdp5_plane_pipe(plane); +			flush |= pipe2flush(pipe); +		} +	} +	flush |= mixer2flush(mdp5_crtc->id); +	flush |= MDP5_CTL_FLUSH_CTL; + +	DBG("%s: flush=%08x", mdp5_crtc->name, flush); + +	mdp5_write(mdp5_kms, REG_MDP5_CTL_FLUSH(id), flush); +} + +static void update_fb(struct drm_crtc *crtc, struct drm_framebuffer *new_fb) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); +	struct drm_framebuffer *old_fb = mdp5_crtc->fb; + +	/* grab reference to incoming scanout fb: */ +	drm_framebuffer_reference(new_fb); +	mdp5_crtc->base.primary->fb = new_fb; +	mdp5_crtc->fb = new_fb; + +	if (old_fb) +		drm_flip_work_queue(&mdp5_crtc->unref_fb_work, old_fb); +} + +/* unlike update_fb(), take a ref to the new scanout fb *before* updating + * plane, then call this.  Needed to ensure we don't unref the buffer that + * is actually still being scanned out. + * + * Note that this whole thing goes away with atomic.. since we can defer + * calling into driver until rendering is done. + */ +static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + +	/* flush updates, to make sure hw is updated to new scanout fb, +	 * so that we can safely queue unref to current fb (ie. next +	 * vblank we know hw is done w/ previous scanout_fb). +	 */ +	crtc_flush(crtc); + +	if (mdp5_crtc->scanout_fb) +		drm_flip_work_queue(&mdp5_crtc->unref_fb_work, +				mdp5_crtc->scanout_fb); + +	mdp5_crtc->scanout_fb = fb; + +	/* enable vblank to complete flip: */ +	request_pending(crtc, PENDING_FLIP); +} + +/* if file!=NULL, this is preclose potential cancel-flip path */ +static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); +	struct drm_device *dev = crtc->dev; +	struct drm_pending_vblank_event *event; +	unsigned long flags, i; + +	spin_lock_irqsave(&dev->event_lock, flags); +	event = mdp5_crtc->event; +	if (event) { +		/* if regular vblank case (!file) or if cancel-flip from +		 * preclose on file that requested flip, then send the +		 * event: +		 */ +		if (!file || (event->base.file_priv == file)) { +			mdp5_crtc->event = NULL; +			drm_send_vblank_event(dev, mdp5_crtc->id, event); +		} +	} +	spin_unlock_irqrestore(&dev->event_lock, flags); + +	for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) { +		struct drm_plane *plane = mdp5_crtc->planes[i]; +		if (plane) +			mdp5_plane_complete_flip(plane); +	} +} + +static void pageflip_cb(struct msm_fence_cb *cb) +{ +	struct mdp5_crtc *mdp5_crtc = +		container_of(cb, struct mdp5_crtc, pageflip_cb); +	struct drm_crtc *crtc = &mdp5_crtc->base; +	struct drm_framebuffer *fb = mdp5_crtc->fb; + +	if (!fb) +		return; + +	drm_framebuffer_reference(fb); +	mdp5_plane_set_scanout(mdp5_crtc->plane, fb); +	update_scanout(crtc, fb); +} + +static void unref_fb_worker(struct drm_flip_work *work, void *val) +{ +	struct mdp5_crtc *mdp5_crtc = +		container_of(work, struct mdp5_crtc, unref_fb_work); +	struct drm_device *dev = mdp5_crtc->base.dev; + +	mutex_lock(&dev->mode_config.mutex); +	drm_framebuffer_unreference(val); +	mutex_unlock(&dev->mode_config.mutex); +} + +static void mdp5_crtc_destroy(struct drm_crtc *crtc) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + +	drm_crtc_cleanup(crtc); +	drm_flip_work_cleanup(&mdp5_crtc->unref_fb_work); + +	kfree(mdp5_crtc); +} + +static void mdp5_crtc_dpms(struct drm_crtc *crtc, int mode) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); +	struct mdp5_kms *mdp5_kms = get_kms(crtc); +	bool enabled = (mode == DRM_MODE_DPMS_ON); + +	DBG("%s: mode=%d", mdp5_crtc->name, mode); + +	if (enabled != mdp5_crtc->enabled) { +		if (enabled) { +			mdp5_enable(mdp5_kms); +			mdp_irq_register(&mdp5_kms->base, &mdp5_crtc->err); +		} else { +			mdp_irq_unregister(&mdp5_kms->base, &mdp5_crtc->err); +			mdp5_disable(mdp5_kms); +		} +		mdp5_crtc->enabled = enabled; +	} +} + +static bool mdp5_crtc_mode_fixup(struct drm_crtc *crtc, +		const struct drm_display_mode *mode, +		struct drm_display_mode *adjusted_mode) +{ +	return true; +} + +static void blend_setup(struct drm_crtc *crtc) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); +	struct mdp5_kms *mdp5_kms = get_kms(crtc); +	int id = mdp5_crtc->id; + +	/* +	 * Hard-coded setup for now until I figure out how the +	 * layer-mixer works +	 */ + +	/* LM[id]: */ +	mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(id), +			MDP5_LM_BLEND_COLOR_OUT_STAGE0_FG_ALPHA); +	mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_OP_MODE(id, 0), +			MDP5_LM_BLEND_OP_MODE_FG_ALPHA(FG_CONST) | +			MDP5_LM_BLEND_OP_MODE_BG_ALPHA(FG_PIXEL) | +			MDP5_LM_BLEND_OP_MODE_BG_INV_ALPHA); +	mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_FG_ALPHA(id, 0), 0xff); +	mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_BG_ALPHA(id, 0), 0x00); + +	/* NOTE: seems that LM[n] and CTL[m], we do not need n==m.. but +	 * we want to be setting CTL[m].LAYER[n].  Not sure what the +	 * point of having CTL[m].LAYER[o] (for o!=n).. maybe that is +	 * used when chaining up mixers for high resolution displays? +	 */ + +	/* CTL[id]: */ +	mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 0), +			MDP5_CTL_LAYER_REG_RGB0(STAGE0) | +			MDP5_CTL_LAYER_REG_BORDER_COLOR); +	mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 1), 0); +	mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 2), 0); +	mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 3), 0); +	mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 4), 0); +} + +static int mdp5_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 mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); +	struct mdp5_kms *mdp5_kms = get_kms(crtc); +	int ret; + +	mode = adjusted_mode; + +	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", +			mdp5_crtc->name, mode->base.id, mode->name, +			mode->vrefresh, mode->clock, +			mode->hdisplay, mode->hsync_start, +			mode->hsync_end, mode->htotal, +			mode->vdisplay, mode->vsync_start, +			mode->vsync_end, mode->vtotal, +			mode->type, mode->flags); + +	/* grab extra ref for update_scanout() */ +	drm_framebuffer_reference(crtc->primary->fb); + +	ret = mdp5_plane_mode_set(mdp5_crtc->plane, crtc, crtc->primary->fb, +			0, 0, mode->hdisplay, mode->vdisplay, +			x << 16, y << 16, +			mode->hdisplay << 16, mode->vdisplay << 16); +	if (ret) { +		drm_framebuffer_unreference(crtc->primary->fb); +		dev_err(crtc->dev->dev, "%s: failed to set mode on plane: %d\n", +				mdp5_crtc->name, ret); +		return ret; +	} + +	mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(mdp5_crtc->id), +			MDP5_LM_OUT_SIZE_WIDTH(mode->hdisplay) | +			MDP5_LM_OUT_SIZE_HEIGHT(mode->vdisplay)); + +	update_fb(crtc, crtc->primary->fb); +	update_scanout(crtc, crtc->primary->fb); + +	return 0; +} + +static void mdp5_crtc_prepare(struct drm_crtc *crtc) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); +	DBG("%s", mdp5_crtc->name); +	/* make sure we hold a ref to mdp clks while setting up mode: */ +	mdp5_enable(get_kms(crtc)); +	mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void mdp5_crtc_commit(struct drm_crtc *crtc) +{ +	mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_ON); +	crtc_flush(crtc); +	/* drop the ref to mdp clk's that we got in prepare: */ +	mdp5_disable(get_kms(crtc)); +} + +static int mdp5_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, +		struct drm_framebuffer *old_fb) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); +	struct drm_plane *plane = mdp5_crtc->plane; +	struct drm_display_mode *mode = &crtc->mode; +	int ret; + +	/* grab extra ref for update_scanout() */ +	drm_framebuffer_reference(crtc->primary->fb); + +	ret = mdp5_plane_mode_set(plane, crtc, crtc->primary->fb, +			0, 0, mode->hdisplay, mode->vdisplay, +			x << 16, y << 16, +			mode->hdisplay << 16, mode->vdisplay << 16); +	if (ret) { +		drm_framebuffer_unreference(crtc->primary->fb); +		return ret; +	} + +	update_fb(crtc, crtc->primary->fb); +	update_scanout(crtc, crtc->primary->fb); + +	return 0; +} + +static void mdp5_crtc_load_lut(struct drm_crtc *crtc) +{ +} + +static int mdp5_crtc_page_flip(struct drm_crtc *crtc, +		struct drm_framebuffer *new_fb, +		struct drm_pending_vblank_event *event, +		uint32_t page_flip_flags) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); +	struct drm_device *dev = crtc->dev; +	struct drm_gem_object *obj; +	unsigned long flags; + +	if (mdp5_crtc->event) { +		dev_err(dev->dev, "already pending flip!\n"); +		return -EBUSY; +	} + +	obj = msm_framebuffer_bo(new_fb, 0); + +	spin_lock_irqsave(&dev->event_lock, flags); +	mdp5_crtc->event = event; +	spin_unlock_irqrestore(&dev->event_lock, flags); + +	update_fb(crtc, new_fb); + +	return msm_gem_queue_inactive_cb(obj, &mdp5_crtc->pageflip_cb); +} + +static int mdp5_crtc_set_property(struct drm_crtc *crtc, +		struct drm_property *property, uint64_t val) +{ +	// XXX +	return -EINVAL; +} + +static const struct drm_crtc_funcs mdp5_crtc_funcs = { +	.set_config = drm_crtc_helper_set_config, +	.destroy = mdp5_crtc_destroy, +	.page_flip = mdp5_crtc_page_flip, +	.set_property = mdp5_crtc_set_property, +}; + +static const struct drm_crtc_helper_funcs mdp5_crtc_helper_funcs = { +	.dpms = mdp5_crtc_dpms, +	.mode_fixup = mdp5_crtc_mode_fixup, +	.mode_set = mdp5_crtc_mode_set, +	.prepare = mdp5_crtc_prepare, +	.commit = mdp5_crtc_commit, +	.mode_set_base = mdp5_crtc_mode_set_base, +	.load_lut = mdp5_crtc_load_lut, +}; + +static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus) +{ +	struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, vblank); +	struct drm_crtc *crtc = &mdp5_crtc->base; +	struct msm_drm_private *priv = crtc->dev->dev_private; +	unsigned pending; + +	mdp_irq_unregister(&get_kms(crtc)->base, &mdp5_crtc->vblank); + +	pending = atomic_xchg(&mdp5_crtc->pending, 0); + +	if (pending & PENDING_FLIP) { +		complete_flip(crtc, NULL); +		drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq); +	} +} + +static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus) +{ +	struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, err); +	struct drm_crtc *crtc = &mdp5_crtc->base; +	DBG("%s: error: %08x", mdp5_crtc->name, irqstatus); +	crtc_flush(crtc); +} + +uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); +	return mdp5_crtc->vblank.irqmask; +} + +void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file) +{ +	DBG("cancel: %p", file); +	complete_flip(crtc, file); +} + +/* set interface for routing crtc->encoder: */ +void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf, +		enum mdp5_intf intf_id) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); +	struct mdp5_kms *mdp5_kms = get_kms(crtc); +	static const enum mdp5_intfnum intfnum[] = { +			INTF0, INTF1, INTF2, INTF3, +	}; +	uint32_t intf_sel; + +	/* now that we know what irq's we want: */ +	mdp5_crtc->err.irqmask = intf2err(intf); +	mdp5_crtc->vblank.irqmask = intf2vblank(intf); + +	/* when called from modeset_init(), skip the rest until later: */ +	if (!mdp5_kms) +		return; + +	intf_sel = mdp5_read(mdp5_kms, REG_MDP5_DISP_INTF_SEL); + +	switch (intf) { +	case 0: +		intf_sel &= ~MDP5_DISP_INTF_SEL_INTF0__MASK; +		intf_sel |= MDP5_DISP_INTF_SEL_INTF0(intf_id); +		break; +	case 1: +		intf_sel &= ~MDP5_DISP_INTF_SEL_INTF1__MASK; +		intf_sel |= MDP5_DISP_INTF_SEL_INTF1(intf_id); +		break; +	case 2: +		intf_sel &= ~MDP5_DISP_INTF_SEL_INTF2__MASK; +		intf_sel |= MDP5_DISP_INTF_SEL_INTF2(intf_id); +		break; +	case 3: +		intf_sel &= ~MDP5_DISP_INTF_SEL_INTF3__MASK; +		intf_sel |= MDP5_DISP_INTF_SEL_INTF3(intf_id); +		break; +	default: +		BUG(); +		break; +	} + +	blend_setup(crtc); + +	DBG("%s: intf_sel=%08x", mdp5_crtc->name, intf_sel); + +	mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, intf_sel); +	mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(mdp5_crtc->id), +			MDP5_CTL_OP_MODE(MODE_NONE) | +			MDP5_CTL_OP_INTF_NUM(intfnum[intf])); + +	crtc_flush(crtc); +} + +static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id, +		struct drm_plane *plane) +{ +	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + +	BUG_ON(pipe_id >= ARRAY_SIZE(mdp5_crtc->planes)); + +	if (mdp5_crtc->planes[pipe_id] == plane) +		return; + +	mdp5_crtc->planes[pipe_id] = plane; +	blend_setup(crtc); +	if (mdp5_crtc->enabled && (plane != mdp5_crtc->plane)) +		crtc_flush(crtc); +} + +void mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane) +{ +	set_attach(crtc, mdp5_plane_pipe(plane), plane); +} + +void mdp5_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane) +{ +	/* don't actually detatch our primary plane: */ +	if (to_mdp5_crtc(crtc)->plane == plane) +		return; +	set_attach(crtc, mdp5_plane_pipe(plane), NULL); +} + +/* initialize crtc */ +struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, +		struct drm_plane *plane, int id) +{ +	struct drm_crtc *crtc = NULL; +	struct mdp5_crtc *mdp5_crtc; +	int ret; + +	mdp5_crtc = kzalloc(sizeof(*mdp5_crtc), GFP_KERNEL); +	if (!mdp5_crtc) { +		ret = -ENOMEM; +		goto fail; +	} + +	crtc = &mdp5_crtc->base; + +	mdp5_crtc->plane = plane; +	mdp5_crtc->id = id; + +	mdp5_crtc->vblank.irq = mdp5_crtc_vblank_irq; +	mdp5_crtc->err.irq = mdp5_crtc_err_irq; + +	snprintf(mdp5_crtc->name, sizeof(mdp5_crtc->name), "%s:%d", +			pipe2name(mdp5_plane_pipe(plane)), id); + +	ret = drm_flip_work_init(&mdp5_crtc->unref_fb_work, 16, +			"unref fb", unref_fb_worker); +	if (ret) +		goto fail; + +	INIT_FENCE_CB(&mdp5_crtc->pageflip_cb, pageflip_cb); + +	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs); +	drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs); + +	mdp5_plane_install_properties(mdp5_crtc->plane, &crtc->base); + +	return crtc; + +fail: +	if (crtc) +		mdp5_crtc_destroy(crtc); + +	return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c new file mode 100644 index 00000000000..edec7bfaa95 --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "mdp5_kms.h" + +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +struct mdp5_encoder { +	struct drm_encoder base; +	int intf; +	enum mdp5_intf intf_id; +	bool enabled; +	uint32_t bsc; +}; +#define to_mdp5_encoder(x) container_of(x, struct mdp5_encoder, base) + +static struct mdp5_kms *get_kms(struct drm_encoder *encoder) +{ +	struct msm_drm_private *priv = encoder->dev->dev_private; +	return to_mdp5_kms(to_mdp_kms(priv->kms)); +} + +#ifdef CONFIG_MSM_BUS_SCALING +#include <mach/board.h> +#include <mach/msm_bus.h> +#include <mach/msm_bus_board.h> +#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val)		\ +	{						\ +		.src = MSM_BUS_MASTER_MDP_PORT0,	\ +		.dst = MSM_BUS_SLAVE_EBI_CH0,		\ +		.ab = (ab_val),				\ +		.ib = (ib_val),				\ +	} + +static struct msm_bus_vectors mdp_bus_vectors[] = { +	MDP_BUS_VECTOR_ENTRY(0, 0), +	MDP_BUS_VECTOR_ENTRY(2000000000, 2000000000), +}; +static struct msm_bus_paths mdp_bus_usecases[] = { { +		.num_paths = 1, +		.vectors = &mdp_bus_vectors[0], +}, { +		.num_paths = 1, +		.vectors = &mdp_bus_vectors[1], +} }; +static struct msm_bus_scale_pdata mdp_bus_scale_table = { +	.usecase = mdp_bus_usecases, +	.num_usecases = ARRAY_SIZE(mdp_bus_usecases), +	.name = "mdss_mdp", +}; + +static void bs_init(struct mdp5_encoder *mdp5_encoder) +{ +	mdp5_encoder->bsc = msm_bus_scale_register_client( +			&mdp_bus_scale_table); +	DBG("bus scale client: %08x", mdp5_encoder->bsc); +} + +static void bs_fini(struct mdp5_encoder *mdp5_encoder) +{ +	if (mdp5_encoder->bsc) { +		msm_bus_scale_unregister_client(mdp5_encoder->bsc); +		mdp5_encoder->bsc = 0; +	} +} + +static void bs_set(struct mdp5_encoder *mdp5_encoder, int idx) +{ +	if (mdp5_encoder->bsc) { +		DBG("set bus scaling: %d", idx); +		/* HACK: scaling down, and then immediately back up +		 * seems to leave things broken (underflow).. so +		 * never disable: +		 */ +		idx = 1; +		msm_bus_scale_client_update_request(mdp5_encoder->bsc, idx); +	} +} +#else +static void bs_init(struct mdp5_encoder *mdp5_encoder) {} +static void bs_fini(struct mdp5_encoder *mdp5_encoder) {} +static void bs_set(struct mdp5_encoder *mdp5_encoder, int idx) {} +#endif + +static void mdp5_encoder_destroy(struct drm_encoder *encoder) +{ +	struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); +	bs_fini(mdp5_encoder); +	drm_encoder_cleanup(encoder); +	kfree(mdp5_encoder); +} + +static const struct drm_encoder_funcs mdp5_encoder_funcs = { +	.destroy = mdp5_encoder_destroy, +}; + +static void mdp5_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +	struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); +	struct mdp5_kms *mdp5_kms = get_kms(encoder); +	int intf = mdp5_encoder->intf; +	bool enabled = (mode == DRM_MODE_DPMS_ON); + +	DBG("mode=%d", mode); + +	if (enabled == mdp5_encoder->enabled) +		return; + +	if (enabled) { +		bs_set(mdp5_encoder, 1); +		mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intf), 1); +	} else { +		mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intf), 0); +		bs_set(mdp5_encoder, 0); +	} + +	mdp5_encoder->enabled = enabled; +} + +static bool mdp5_encoder_mode_fixup(struct drm_encoder *encoder, +		const struct drm_display_mode *mode, +		struct drm_display_mode *adjusted_mode) +{ +	return true; +} + +static void mdp5_encoder_mode_set(struct drm_encoder *encoder, +		struct drm_display_mode *mode, +		struct drm_display_mode *adjusted_mode) +{ +	struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); +	struct mdp5_kms *mdp5_kms = get_kms(encoder); +	int intf = mdp5_encoder->intf; +	uint32_t dtv_hsync_skew, vsync_period, vsync_len, ctrl_pol; +	uint32_t display_v_start, display_v_end; +	uint32_t hsync_start_x, hsync_end_x; +	uint32_t format; + +	mode = adjusted_mode; + +	DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", +			mode->base.id, mode->name, +			mode->vrefresh, mode->clock, +			mode->hdisplay, mode->hsync_start, +			mode->hsync_end, mode->htotal, +			mode->vdisplay, mode->vsync_start, +			mode->vsync_end, mode->vtotal, +			mode->type, mode->flags); + +	ctrl_pol = 0; +	if (mode->flags & DRM_MODE_FLAG_NHSYNC) +		ctrl_pol |= MDP5_INTF_POLARITY_CTL_HSYNC_LOW; +	if (mode->flags & DRM_MODE_FLAG_NVSYNC) +		ctrl_pol |= MDP5_INTF_POLARITY_CTL_VSYNC_LOW; +	/* probably need to get DATA_EN polarity from panel.. */ + +	dtv_hsync_skew = 0;  /* get this from panel? */ +	format = 0x213f;     /* get this from panel? */ + +	hsync_start_x = (mode->htotal - mode->hsync_start); +	hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1; + +	vsync_period = mode->vtotal * mode->htotal; +	vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal; +	display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + dtv_hsync_skew; +	display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + dtv_hsync_skew - 1; + +	mdp5_write(mdp5_kms, REG_MDP5_INTF_HSYNC_CTL(intf), +			MDP5_INTF_HSYNC_CTL_PULSEW(mode->hsync_end - mode->hsync_start) | +			MDP5_INTF_HSYNC_CTL_PERIOD(mode->htotal)); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_VSYNC_PERIOD_F0(intf), vsync_period); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_VSYNC_LEN_F0(intf), vsync_len); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_HCTL(intf), +			MDP5_INTF_DISPLAY_HCTL_START(hsync_start_x) | +			MDP5_INTF_DISPLAY_HCTL_END(hsync_end_x)); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_VSTART_F0(intf), display_v_start); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_VEND_F0(intf), display_v_end); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_BORDER_COLOR(intf), 0); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_UNDERFLOW_COLOR(intf), 0xff); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_HSYNC_SKEW(intf), dtv_hsync_skew); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_POLARITY_CTL(intf), ctrl_pol); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_HCTL(intf), +			MDP5_INTF_ACTIVE_HCTL_START(0) | +			MDP5_INTF_ACTIVE_HCTL_END(0)); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_VSTART_F0(intf), 0); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_VEND_F0(intf), 0); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_PANEL_FORMAT(intf), format); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_FRAME_LINE_COUNT_EN(intf), 0x3);  /* frame+line? */ +} + +static void mdp5_encoder_prepare(struct drm_encoder *encoder) +{ +	mdp5_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void mdp5_encoder_commit(struct drm_encoder *encoder) +{ +	struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); +	mdp5_crtc_set_intf(encoder->crtc, mdp5_encoder->intf, +			mdp5_encoder->intf_id); +	mdp5_encoder_dpms(encoder, DRM_MODE_DPMS_ON); +} + +static const struct drm_encoder_helper_funcs mdp5_encoder_helper_funcs = { +	.dpms = mdp5_encoder_dpms, +	.mode_fixup = mdp5_encoder_mode_fixup, +	.mode_set = mdp5_encoder_mode_set, +	.prepare = mdp5_encoder_prepare, +	.commit = mdp5_encoder_commit, +}; + +/* initialize encoder */ +struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, int intf, +		enum mdp5_intf intf_id) +{ +	struct drm_encoder *encoder = NULL; +	struct mdp5_encoder *mdp5_encoder; +	int ret; + +	mdp5_encoder = kzalloc(sizeof(*mdp5_encoder), GFP_KERNEL); +	if (!mdp5_encoder) { +		ret = -ENOMEM; +		goto fail; +	} + +	mdp5_encoder->intf = intf; +	mdp5_encoder->intf_id = intf_id; +	encoder = &mdp5_encoder->base; + +	drm_encoder_init(dev, encoder, &mdp5_encoder_funcs, +			 DRM_MODE_ENCODER_TMDS); +	drm_encoder_helper_add(encoder, &mdp5_encoder_helper_funcs); + +	bs_init(mdp5_encoder); + +	return encoder; + +fail: +	if (encoder) +		mdp5_encoder_destroy(encoder); + +	return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c new file mode 100644 index 00000000000..f2b985bc2ad --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "msm_drv.h" +#include "mdp5_kms.h" + +void mdp5_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask) +{ +	mdp5_write(to_mdp5_kms(mdp_kms), REG_MDP5_INTR_EN, irqmask); +} + +static void mdp5_irq_error_handler(struct mdp_irq *irq, uint32_t irqstatus) +{ +	DRM_ERROR("errors: %08x\n", irqstatus); +} + +void mdp5_irq_preinstall(struct msm_kms *kms) +{ +	struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); +	mdp5_write(mdp5_kms, REG_MDP5_INTR_CLEAR, 0xffffffff); +} + +int mdp5_irq_postinstall(struct msm_kms *kms) +{ +	struct mdp_kms *mdp_kms = to_mdp_kms(kms); +	struct mdp5_kms *mdp5_kms = to_mdp5_kms(mdp_kms); +	struct mdp_irq *error_handler = &mdp5_kms->error_handler; + +	error_handler->irq = mdp5_irq_error_handler; +	error_handler->irqmask = MDP5_IRQ_INTF0_UNDER_RUN | +			MDP5_IRQ_INTF1_UNDER_RUN | +			MDP5_IRQ_INTF2_UNDER_RUN | +			MDP5_IRQ_INTF3_UNDER_RUN; + +	mdp_irq_register(mdp_kms, error_handler); + +	return 0; +} + +void mdp5_irq_uninstall(struct msm_kms *kms) +{ +	struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); +	mdp5_write(mdp5_kms, REG_MDP5_INTR_EN, 0x00000000); +} + +static void mdp5_irq_mdp(struct mdp_kms *mdp_kms) +{ +	struct mdp5_kms *mdp5_kms = to_mdp5_kms(mdp_kms); +	struct drm_device *dev = mdp5_kms->dev; +	struct msm_drm_private *priv = dev->dev_private; +	unsigned int id; +	uint32_t status; + +	status = mdp5_read(mdp5_kms, REG_MDP5_INTR_STATUS); +	mdp5_write(mdp5_kms, REG_MDP5_INTR_CLEAR, status); + +	VERB("status=%08x", status); + +	mdp_dispatch_irqs(mdp_kms, status); + +	for (id = 0; id < priv->num_crtcs; id++) +		if (status & mdp5_crtc_vblank(priv->crtcs[id])) +			drm_handle_vblank(dev, id); +} + +irqreturn_t mdp5_irq(struct msm_kms *kms) +{ +	struct mdp_kms *mdp_kms = to_mdp_kms(kms); +	struct mdp5_kms *mdp5_kms = to_mdp5_kms(mdp_kms); +	uint32_t intr; + +	intr = mdp5_read(mdp5_kms, REG_MDP5_HW_INTR_STATUS); + +	VERB("intr=%08x", intr); + +	if (intr & MDP5_HW_INTR_STATUS_INTR_MDP) +		mdp5_irq_mdp(mdp_kms); + +	if (intr & MDP5_HW_INTR_STATUS_INTR_HDMI) +		hdmi_irq(0, mdp5_kms->hdmi); + +	return IRQ_HANDLED; +} + +int mdp5_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) +{ +	mdp_update_vblank_mask(to_mdp_kms(kms), +			mdp5_crtc_vblank(crtc), true); +	return 0; +} + +void mdp5_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) +{ +	mdp_update_vblank_mask(to_mdp_kms(kms), +			mdp5_crtc_vblank(crtc), false); +} diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c new file mode 100644 index 00000000000..71510ee26e9 --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "msm_drv.h" +#include "msm_mmu.h" +#include "mdp5_kms.h" + +static const char *iommu_ports[] = { +		"mdp_0", +}; + +static struct mdp5_platform_config *mdp5_get_config(struct platform_device *dev); + +static int mdp5_hw_init(struct msm_kms *kms) +{ +	struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); +	struct drm_device *dev = mdp5_kms->dev; +	uint32_t version, major, minor; +	int ret = 0; + +	pm_runtime_get_sync(dev->dev); + +	mdp5_enable(mdp5_kms); +	version = mdp5_read(mdp5_kms, REG_MDP5_MDP_VERSION); +	mdp5_disable(mdp5_kms); + +	major = FIELD(version, MDP5_MDP_VERSION_MAJOR); +	minor = FIELD(version, MDP5_MDP_VERSION_MINOR); + +	DBG("found MDP5 version v%d.%d", major, minor); + +	if ((major != 1) || ((minor != 0) && (minor != 2))) { +		dev_err(dev->dev, "unexpected MDP version: v%d.%d\n", +				major, minor); +		ret = -ENXIO; +		goto out; +	} + +	mdp5_kms->rev = minor; + +	/* Magic unknown register writes: +	 * +	 *    W VBIF:0x004 00000001      (mdss_mdp.c:839) +	 *    W MDP5:0x2e0 0xe9          (mdss_mdp.c:839) +	 *    W MDP5:0x2e4 0x55          (mdss_mdp.c:839) +	 *    W MDP5:0x3ac 0xc0000ccc    (mdss_mdp.c:839) +	 *    W MDP5:0x3b4 0xc0000ccc    (mdss_mdp.c:839) +	 *    W MDP5:0x3bc 0xcccccc      (mdss_mdp.c:839) +	 *    W MDP5:0x4a8 0xcccc0c0     (mdss_mdp.c:839) +	 *    W MDP5:0x4b0 0xccccc0c0    (mdss_mdp.c:839) +	 *    W MDP5:0x4b8 0xccccc000    (mdss_mdp.c:839) +	 * +	 * Downstream fbdev driver gets these register offsets/values +	 * from DT.. not really sure what these registers are or if +	 * different values for different boards/SoC's, etc.  I guess +	 * they are the golden registers. +	 * +	 * Not setting these does not seem to cause any problem.  But +	 * we may be getting lucky with the bootloader initializing +	 * them for us.  OTOH, if we can always count on the bootloader +	 * setting the golden registers, then perhaps we don't need to +	 * care. +	 */ + +	mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, 0); +	mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(0), 0); +	mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(1), 0); +	mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(2), 0); +	mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(3), 0); + +out: +	pm_runtime_put_sync(dev->dev); + +	return ret; +} + +static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate, +		struct drm_encoder *encoder) +{ +	return rate; +} + +static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file) +{ +	struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); +	struct msm_drm_private *priv = mdp5_kms->dev->dev_private; +	unsigned i; + +	for (i = 0; i < priv->num_crtcs; i++) +		mdp5_crtc_cancel_pending_flip(priv->crtcs[i], file); +} + +static void mdp5_destroy(struct msm_kms *kms) +{ +	struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); +	struct msm_mmu *mmu = mdp5_kms->mmu; + +	if (mmu) { +		mmu->funcs->detach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); +		mmu->funcs->destroy(mmu); +	} +	kfree(mdp5_kms); +} + +static const struct mdp_kms_funcs kms_funcs = { +	.base = { +		.hw_init         = mdp5_hw_init, +		.irq_preinstall  = mdp5_irq_preinstall, +		.irq_postinstall = mdp5_irq_postinstall, +		.irq_uninstall   = mdp5_irq_uninstall, +		.irq             = mdp5_irq, +		.enable_vblank   = mdp5_enable_vblank, +		.disable_vblank  = mdp5_disable_vblank, +		.get_format      = mdp_get_format, +		.round_pixclk    = mdp5_round_pixclk, +		.preclose        = mdp5_preclose, +		.destroy         = mdp5_destroy, +	}, +	.set_irqmask         = mdp5_set_irqmask, +}; + +int mdp5_disable(struct mdp5_kms *mdp5_kms) +{ +	DBG(""); + +	clk_disable_unprepare(mdp5_kms->ahb_clk); +	clk_disable_unprepare(mdp5_kms->axi_clk); +	clk_disable_unprepare(mdp5_kms->core_clk); +	clk_disable_unprepare(mdp5_kms->lut_clk); + +	return 0; +} + +int mdp5_enable(struct mdp5_kms *mdp5_kms) +{ +	DBG(""); + +	clk_prepare_enable(mdp5_kms->ahb_clk); +	clk_prepare_enable(mdp5_kms->axi_clk); +	clk_prepare_enable(mdp5_kms->core_clk); +	clk_prepare_enable(mdp5_kms->lut_clk); + +	return 0; +} + +static int modeset_init(struct mdp5_kms *mdp5_kms) +{ +	static const enum mdp5_pipe crtcs[] = { +			SSPP_RGB0, SSPP_RGB1, SSPP_RGB2, +	}; +	struct drm_device *dev = mdp5_kms->dev; +	struct msm_drm_private *priv = dev->dev_private; +	struct drm_encoder *encoder; +	int i, ret; + +	/* construct CRTCs: */ +	for (i = 0; i < ARRAY_SIZE(crtcs); i++) { +		struct drm_plane *plane; +		struct drm_crtc *crtc; + +		plane = mdp5_plane_init(dev, crtcs[i], true); +		if (IS_ERR(plane)) { +			ret = PTR_ERR(plane); +			dev_err(dev->dev, "failed to construct plane for %s (%d)\n", +					pipe2name(crtcs[i]), ret); +			goto fail; +		} + +		crtc  = mdp5_crtc_init(dev, plane, i); +		if (IS_ERR(crtc)) { +			ret = PTR_ERR(crtc); +			dev_err(dev->dev, "failed to construct crtc for %s (%d)\n", +					pipe2name(crtcs[i]), ret); +			goto fail; +		} +		priv->crtcs[priv->num_crtcs++] = crtc; +	} + +	/* Construct encoder for HDMI: */ +	encoder = mdp5_encoder_init(dev, 3, INTF_HDMI); +	if (IS_ERR(encoder)) { +		dev_err(dev->dev, "failed to construct encoder\n"); +		ret = PTR_ERR(encoder); +		goto fail; +	} + +	/* NOTE: the vsync and error irq's are actually associated with +	 * the INTF/encoder.. the easiest way to deal with this (ie. what +	 * we do now) is assume a fixed relationship between crtc's and +	 * encoders.  I'm not sure if there is ever a need to more freely +	 * assign crtcs to encoders, but if there is then we need to take +	 * care of error and vblank irq's that the crtc has registered, +	 * and also update user-requested vblank_mask. +	 */ +	encoder->possible_crtcs = BIT(0); +	mdp5_crtc_set_intf(priv->crtcs[0], 3, INTF_HDMI); + +	priv->encoders[priv->num_encoders++] = encoder; + +	/* Construct bridge/connector for HDMI: */ +	mdp5_kms->hdmi = hdmi_init(dev, encoder); +	if (IS_ERR(mdp5_kms->hdmi)) { +		ret = PTR_ERR(mdp5_kms->hdmi); +		dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret); +		goto fail; +	} + +	return 0; + +fail: +	return ret; +} + +static int get_clk(struct platform_device *pdev, struct clk **clkp, +		const char *name) +{ +	struct device *dev = &pdev->dev; +	struct clk *clk = devm_clk_get(dev, name); +	if (IS_ERR(clk)) { +		dev_err(dev, "failed to get %s (%ld)\n", name, PTR_ERR(clk)); +		return PTR_ERR(clk); +	} +	*clkp = clk; +	return 0; +} + +struct msm_kms *mdp5_kms_init(struct drm_device *dev) +{ +	struct platform_device *pdev = dev->platformdev; +	struct mdp5_platform_config *config = mdp5_get_config(pdev); +	struct mdp5_kms *mdp5_kms; +	struct msm_kms *kms = NULL; +	struct msm_mmu *mmu; +	int ret; + +	mdp5_kms = kzalloc(sizeof(*mdp5_kms), GFP_KERNEL); +	if (!mdp5_kms) { +		dev_err(dev->dev, "failed to allocate kms\n"); +		ret = -ENOMEM; +		goto fail; +	} + +	mdp_kms_init(&mdp5_kms->base, &kms_funcs); + +	kms = &mdp5_kms->base.base; + +	mdp5_kms->dev = dev; +	mdp5_kms->smp_blk_cnt = config->smp_blk_cnt; + +	mdp5_kms->mmio = msm_ioremap(pdev, "mdp_phys", "MDP5"); +	if (IS_ERR(mdp5_kms->mmio)) { +		ret = PTR_ERR(mdp5_kms->mmio); +		goto fail; +	} + +	mdp5_kms->vbif = msm_ioremap(pdev, "vbif_phys", "VBIF"); +	if (IS_ERR(mdp5_kms->vbif)) { +		ret = PTR_ERR(mdp5_kms->vbif); +		goto fail; +	} + +	mdp5_kms->vdd = devm_regulator_get(&pdev->dev, "vdd"); +	if (IS_ERR(mdp5_kms->vdd)) { +		ret = PTR_ERR(mdp5_kms->vdd); +		goto fail; +	} + +	ret = regulator_enable(mdp5_kms->vdd); +	if (ret) { +		dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret); +		goto fail; +	} + +	ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk"); +	if (ret) +		goto fail; +	ret = get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk"); +	if (ret) +		goto fail; +	ret = get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src"); +	if (ret) +		goto fail; +	ret = get_clk(pdev, &mdp5_kms->core_clk, "core_clk"); +	if (ret) +		goto fail; +	ret = get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk"); +	if (ret) +		goto fail; +	ret = get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk"); +	if (ret) +		goto fail; + +	ret = clk_set_rate(mdp5_kms->src_clk, config->max_clk); + +	/* make sure things are off before attaching iommu (bootloader could +	 * have left things on, in which case we'll start getting faults if +	 * we don't disable): +	 */ +	mdp5_enable(mdp5_kms); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(0), 0); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(1), 0); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(2), 0); +	mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(3), 0); +	mdp5_disable(mdp5_kms); +	mdelay(16); + +	if (config->iommu) { +		mmu = msm_iommu_new(dev, config->iommu); +		if (IS_ERR(mmu)) { +			ret = PTR_ERR(mmu); +			dev_err(dev->dev, "failed to init iommu: %d\n", ret); +			goto fail; +		} + +		ret = mmu->funcs->attach(mmu, iommu_ports, +				ARRAY_SIZE(iommu_ports)); +		if (ret) { +			dev_err(dev->dev, "failed to attach iommu: %d\n", ret); +			mmu->funcs->destroy(mmu); +			goto fail; +		} +	} else { +		dev_info(dev->dev, "no iommu, fallback to phys " +				"contig buffers for scanout\n"); +		mmu = NULL; +	} +	mdp5_kms->mmu = mmu; + +	mdp5_kms->id = msm_register_mmu(dev, mmu); +	if (mdp5_kms->id < 0) { +		ret = mdp5_kms->id; +		dev_err(dev->dev, "failed to register mdp5 iommu: %d\n", ret); +		goto fail; +	} + +	ret = modeset_init(mdp5_kms); +	if (ret) { +		dev_err(dev->dev, "modeset_init failed: %d\n", ret); +		goto fail; +	} + +	return kms; + +fail: +	if (kms) +		mdp5_destroy(kms); +	return ERR_PTR(ret); +} + +static struct mdp5_platform_config *mdp5_get_config(struct platform_device *dev) +{ +	static struct mdp5_platform_config config = {}; +#ifdef CONFIG_OF +	/* TODO */ +#endif +	return &config; +} diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h new file mode 100644 index 00000000000..6e981b692d1 --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __MDP5_KMS_H__ +#define __MDP5_KMS_H__ + +#include "msm_drv.h" +#include "msm_kms.h" +#include "mdp/mdp_kms.h" +#include "mdp5.xml.h" +#include "mdp5_smp.h" + +struct mdp5_kms { +	struct mdp_kms base; + +	struct drm_device *dev; + +	int rev; + +	/* mapper-id used to request GEM buffer mapped for scanout: */ +	int id; +	struct msm_mmu *mmu; + +	/* for tracking smp allocation amongst pipes: */ +	mdp5_smp_state_t smp_state; +	struct mdp5_client_smp_state smp_client_state[CID_MAX]; +	int smp_blk_cnt; + +	/* io/register spaces: */ +	void __iomem *mmio, *vbif; + +	struct regulator *vdd; + +	struct clk *axi_clk; +	struct clk *ahb_clk; +	struct clk *src_clk; +	struct clk *core_clk; +	struct clk *lut_clk; +	struct clk *vsync_clk; + +	struct hdmi *hdmi; + +	struct mdp_irq error_handler; +}; +#define to_mdp5_kms(x) container_of(x, struct mdp5_kms, base) + +/* platform config data (ie. from DT, or pdata) */ +struct mdp5_platform_config { +	struct iommu_domain *iommu; +	uint32_t max_clk; +	int smp_blk_cnt; +}; + +static inline void mdp5_write(struct mdp5_kms *mdp5_kms, u32 reg, u32 data) +{ +	msm_writel(data, mdp5_kms->mmio + reg); +} + +static inline u32 mdp5_read(struct mdp5_kms *mdp5_kms, u32 reg) +{ +	return msm_readl(mdp5_kms->mmio + reg); +} + +static inline const char *pipe2name(enum mdp5_pipe pipe) +{ +	static const char *names[] = { +#define NAME(n) [SSPP_ ## n] = #n +		NAME(VIG0), NAME(VIG1), NAME(VIG2), +		NAME(RGB0), NAME(RGB1), NAME(RGB2), +		NAME(DMA0), NAME(DMA1), +#undef NAME +	}; +	return names[pipe]; +} + +static inline uint32_t pipe2flush(enum mdp5_pipe pipe) +{ +	switch (pipe) { +	case SSPP_VIG0: return MDP5_CTL_FLUSH_VIG0; +	case SSPP_VIG1: return MDP5_CTL_FLUSH_VIG1; +	case SSPP_VIG2: return MDP5_CTL_FLUSH_VIG2; +	case SSPP_RGB0: return MDP5_CTL_FLUSH_RGB0; +	case SSPP_RGB1: return MDP5_CTL_FLUSH_RGB1; +	case SSPP_RGB2: return MDP5_CTL_FLUSH_RGB2; +	case SSPP_DMA0: return MDP5_CTL_FLUSH_DMA0; +	case SSPP_DMA1: return MDP5_CTL_FLUSH_DMA1; +	default:        return 0; +	} +} + +static inline int pipe2nclients(enum mdp5_pipe pipe) +{ +	switch (pipe) { +	case SSPP_RGB0: +	case SSPP_RGB1: +	case SSPP_RGB2: +		return 1; +	default: +		return 3; +	} +} + +static inline enum mdp5_client_id pipe2client(enum mdp5_pipe pipe, int plane) +{ +	WARN_ON(plane >= pipe2nclients(pipe)); +	switch (pipe) { +	case SSPP_VIG0: return CID_VIG0_Y + plane; +	case SSPP_VIG1: return CID_VIG1_Y + plane; +	case SSPP_VIG2: return CID_VIG2_Y + plane; +	case SSPP_RGB0: return CID_RGB0; +	case SSPP_RGB1: return CID_RGB1; +	case SSPP_RGB2: return CID_RGB2; +	case SSPP_DMA0: return CID_DMA0_Y + plane; +	case SSPP_DMA1: return CID_DMA1_Y + plane; +	default:        return CID_UNUSED; +	} +} + +static inline uint32_t mixer2flush(int lm) +{ +	switch (lm) { +	case 0:  return MDP5_CTL_FLUSH_LM0; +	case 1:  return MDP5_CTL_FLUSH_LM1; +	case 2:  return MDP5_CTL_FLUSH_LM2; +	default: return 0; +	} +} + +static inline uint32_t intf2err(int intf) +{ +	switch (intf) { +	case 0:  return MDP5_IRQ_INTF0_UNDER_RUN; +	case 1:  return MDP5_IRQ_INTF1_UNDER_RUN; +	case 2:  return MDP5_IRQ_INTF2_UNDER_RUN; +	case 3:  return MDP5_IRQ_INTF3_UNDER_RUN; +	default: return 0; +	} +} + +static inline uint32_t intf2vblank(int intf) +{ +	switch (intf) { +	case 0:  return MDP5_IRQ_INTF0_VSYNC; +	case 1:  return MDP5_IRQ_INTF1_VSYNC; +	case 2:  return MDP5_IRQ_INTF2_VSYNC; +	case 3:  return MDP5_IRQ_INTF3_VSYNC; +	default: return 0; +	} +} + +int mdp5_disable(struct mdp5_kms *mdp5_kms); +int mdp5_enable(struct mdp5_kms *mdp5_kms); + +void mdp5_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask); +void mdp5_irq_preinstall(struct msm_kms *kms); +int mdp5_irq_postinstall(struct msm_kms *kms); +void mdp5_irq_uninstall(struct msm_kms *kms); +irqreturn_t mdp5_irq(struct msm_kms *kms); +int mdp5_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc); +void mdp5_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc); + +static inline +uint32_t mdp5_get_formats(enum mdp5_pipe pipe, uint32_t *pixel_formats, +		uint32_t max_formats) +{ +	/* TODO when we have YUV, we need to filter supported formats +	 * based on pipe id.. +	 */ +	return mdp_get_formats(pixel_formats, max_formats); +} + +void mdp5_plane_install_properties(struct drm_plane *plane, +		struct drm_mode_object *obj); +void mdp5_plane_set_scanout(struct drm_plane *plane, +		struct drm_framebuffer *fb); +int mdp5_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 mdp5_plane_complete_flip(struct drm_plane *plane); +enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane); +struct drm_plane *mdp5_plane_init(struct drm_device *dev, +		enum mdp5_pipe pipe, bool private_plane); + +uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc); + +void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file); +void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf, +		enum mdp5_intf intf_id); +void mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane); +void mdp5_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane); +struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, +		struct drm_plane *plane, int id); + +struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, int intf, +		enum mdp5_intf intf_id); + +#endif /* __MDP5_KMS_H__ */ diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c new file mode 100644 index 00000000000..f3daec4412a --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "mdp5_kms.h" + + +struct mdp5_plane { +	struct drm_plane base; +	const char *name; + +	enum mdp5_pipe pipe; + +	uint32_t nformats; +	uint32_t formats[32]; + +	bool enabled; +}; +#define to_mdp5_plane(x) container_of(x, struct mdp5_plane, base) + +static struct mdp5_kms *get_kms(struct drm_plane *plane) +{ +	struct msm_drm_private *priv = plane->dev->dev_private; +	return to_mdp5_kms(to_mdp_kms(priv->kms)); +} + +static int mdp5_plane_update(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 mdp5_plane *mdp5_plane = to_mdp5_plane(plane); + +	mdp5_plane->enabled = true; + +	if (plane->fb) +		drm_framebuffer_unreference(plane->fb); + +	drm_framebuffer_reference(fb); + +	return mdp5_plane_mode_set(plane, crtc, fb, +			crtc_x, crtc_y, crtc_w, crtc_h, +			src_x, src_y, src_w, src_h); +} + +static int mdp5_plane_disable(struct drm_plane *plane) +{ +	struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); +	struct mdp5_kms *mdp5_kms = get_kms(plane); +	enum mdp5_pipe pipe = mdp5_plane->pipe; +	int i; + +	DBG("%s: disable", mdp5_plane->name); + +	/* update our SMP request to zero (release all our blks): */ +	for (i = 0; i < pipe2nclients(pipe); i++) +		mdp5_smp_request(mdp5_kms, pipe2client(pipe, i), 0); + +	/* TODO detaching now will cause us not to get the last +	 * vblank and mdp5_smp_commit().. so other planes will +	 * still see smp blocks previously allocated to us as +	 * in-use.. +	 */ +	if (plane->crtc) +		mdp5_crtc_detach(plane->crtc, plane); + +	return 0; +} + +static void mdp5_plane_destroy(struct drm_plane *plane) +{ +	struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); +	struct msm_drm_private *priv = plane->dev->dev_private; + +	if (priv->kms) +		mdp5_plane_disable(plane); + +	drm_plane_cleanup(plane); + +	kfree(mdp5_plane); +} + +/* helper to install properties which are common to planes and crtcs */ +void mdp5_plane_install_properties(struct drm_plane *plane, +		struct drm_mode_object *obj) +{ +	// XXX +} + +int mdp5_plane_set_property(struct drm_plane *plane, +		struct drm_property *property, uint64_t val) +{ +	// XXX +	return -EINVAL; +} + +static const struct drm_plane_funcs mdp5_plane_funcs = { +		.update_plane = mdp5_plane_update, +		.disable_plane = mdp5_plane_disable, +		.destroy = mdp5_plane_destroy, +		.set_property = mdp5_plane_set_property, +}; + +void mdp5_plane_set_scanout(struct drm_plane *plane, +		struct drm_framebuffer *fb) +{ +	struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); +	struct mdp5_kms *mdp5_kms = get_kms(plane); +	enum mdp5_pipe pipe = mdp5_plane->pipe; +	uint32_t nplanes = drm_format_num_planes(fb->pixel_format); +	uint32_t iova[4]; +	int i; + +	for (i = 0; i < nplanes; i++) { +		struct drm_gem_object *bo = msm_framebuffer_bo(fb, i); +		msm_gem_get_iova(bo, mdp5_kms->id, &iova[i]); +	} +	for (; i < 4; i++) +		iova[i] = 0; + +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe), +			MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) | +			MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1])); + +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe), +			MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) | +			MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3])); + +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe), iova[0]); +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe), iova[1]); +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe), iova[2]); +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe), iova[3]); + +	plane->fb = fb; +} + +/* NOTE: looks like if horizontal decimation is used (if we supported that) + * then the width used to calculate SMP block requirements is the post- + * decimated width.  Ie. SMP buffering sits downstream of decimation (which + * presumably happens during the dma from scanout buffer). + */ +static int request_smp_blocks(struct drm_plane *plane, uint32_t format, +		uint32_t nplanes, uint32_t width) +{ +	struct drm_device *dev = plane->dev; +	struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); +	struct mdp5_kms *mdp5_kms = get_kms(plane); +	enum mdp5_pipe pipe = mdp5_plane->pipe; +	int i, hsub, nlines, nblks, ret; + +	hsub = drm_format_horz_chroma_subsampling(format); + +	/* different if BWC (compressed framebuffer?) enabled: */ +	nlines = 2; + +	for (i = 0, nblks = 0; i < nplanes; i++) { +		int n, fetch_stride, cpp; + +		cpp = drm_format_plane_cpp(format, i); +		fetch_stride = width * cpp / (i ? hsub : 1); + +		n = DIV_ROUND_UP(fetch_stride * nlines, SMP_BLK_SIZE); + +		/* for hw rev v1.00 */ +		if (mdp5_kms->rev == 0) +			n = roundup_pow_of_two(n); + +		DBG("%s[%d]: request %d SMP blocks", mdp5_plane->name, i, n); +		ret = mdp5_smp_request(mdp5_kms, pipe2client(pipe, i), n); +		if (ret) { +			dev_err(dev->dev, "Could not allocate %d SMP blocks: %d\n", +					n, ret); +			return ret; +		} + +		nblks += n; +	} + +	/* in success case, return total # of blocks allocated: */ +	return nblks; +} + +static void set_fifo_thresholds(struct drm_plane *plane, int nblks) +{ +	struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); +	struct mdp5_kms *mdp5_kms = get_kms(plane); +	enum mdp5_pipe pipe = mdp5_plane->pipe; +	uint32_t val; + +	/* 1/4 of SMP pool that is being fetched */ +	val = (nblks * SMP_ENTRIES_PER_BLK) / 4; + +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_0(pipe), val * 1); +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_1(pipe), val * 2); +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_2(pipe), val * 3); + +} + +int mdp5_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 mdp5_plane *mdp5_plane = to_mdp5_plane(plane); +	struct mdp5_kms *mdp5_kms = get_kms(plane); +	enum mdp5_pipe pipe = mdp5_plane->pipe; +	const struct mdp_format *format; +	uint32_t nplanes, config = 0; +	uint32_t phasex_step = 0, phasey_step = 0; +	uint32_t hdecm = 0, vdecm = 0; +	int i, nblks; + +	nplanes = drm_format_num_planes(fb->pixel_format); + +	/* bad formats should already be rejected: */ +	if (WARN_ON(nplanes > pipe2nclients(pipe))) +		return -EINVAL; + +	/* src values are in Q16 fixed point, convert to integer: */ +	src_x = src_x >> 16; +	src_y = src_y >> 16; +	src_w = src_w >> 16; +	src_h = src_h >> 16; + +	DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", mdp5_plane->name, +			fb->base.id, src_x, src_y, src_w, src_h, +			crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h); + +	/* +	 * Calculate and request required # of smp blocks: +	 */ +	nblks = request_smp_blocks(plane, fb->pixel_format, nplanes, src_w); +	if (nblks < 0) +		return nblks; + +	/* +	 * Currently we update the hw for allocations/requests immediately, +	 * but once atomic modeset/pageflip is in place, the allocation +	 * would move into atomic->check_plane_state(), while updating the +	 * hw would remain here: +	 */ +	for (i = 0; i < pipe2nclients(pipe); i++) +		mdp5_smp_configure(mdp5_kms, pipe2client(pipe, i)); + +	if (src_w != crtc_w) { +		config |= MDP5_PIPE_SCALE_CONFIG_SCALEX_EN; +		/* TODO calc phasex_step, hdecm */ +	} + +	if (src_h != crtc_h) { +		config |= MDP5_PIPE_SCALE_CONFIG_SCALEY_EN; +		/* TODO calc phasey_step, vdecm */ +	} + +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe), +			MDP5_PIPE_SRC_IMG_SIZE_WIDTH(src_w) | +			MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(src_h)); + +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_SIZE(pipe), +			MDP5_PIPE_SRC_SIZE_WIDTH(src_w) | +			MDP5_PIPE_SRC_SIZE_HEIGHT(src_h)); + +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_XY(pipe), +			MDP5_PIPE_SRC_XY_X(src_x) | +			MDP5_PIPE_SRC_XY_Y(src_y)); + +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_SIZE(pipe), +			MDP5_PIPE_OUT_SIZE_WIDTH(crtc_w) | +			MDP5_PIPE_OUT_SIZE_HEIGHT(crtc_h)); + +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_XY(pipe), +			MDP5_PIPE_OUT_XY_X(crtc_x) | +			MDP5_PIPE_OUT_XY_Y(crtc_y)); + +	mdp5_plane_set_scanout(plane, fb); + +	format = to_mdp_format(msm_framebuffer_format(fb)); + +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe), +			MDP5_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) | +			MDP5_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) | +			MDP5_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) | +			MDP5_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) | +			COND(format->alpha_enable, MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE) | +			MDP5_PIPE_SRC_FORMAT_CPP(format->cpp - 1) | +			MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) | +			COND(format->unpack_tight, MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) | +			MDP5_PIPE_SRC_FORMAT_NUM_PLANES(nplanes - 1) | +			MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(CHROMA_RGB)); + +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe), +			MDP5_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) | +			MDP5_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) | +			MDP5_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) | +			MDP5_PIPE_SRC_UNPACK_ELEM3(format->unpack[3])); + +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe), +			MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS)); + +	/* not using secure mode: */ +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0); + +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe), phasex_step); +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe), phasey_step); +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe), +			MDP5_PIPE_DECIMATION_VERT(vdecm) | +			MDP5_PIPE_DECIMATION_HORZ(hdecm)); +	mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe), +			MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER(SCALE_FILTER_NEAREST) | +			MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER(SCALE_FILTER_NEAREST) | +			MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER(SCALE_FILTER_NEAREST) | +			MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER(SCALE_FILTER_NEAREST) | +			MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER(SCALE_FILTER_NEAREST) | +			MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER(SCALE_FILTER_NEAREST)); + +	set_fifo_thresholds(plane, nblks); + +	/* TODO detach from old crtc (if we had more than one) */ +	mdp5_crtc_attach(crtc, plane); + +	return 0; +} + +void mdp5_plane_complete_flip(struct drm_plane *plane) +{ +	struct mdp5_kms *mdp5_kms = get_kms(plane); +	enum mdp5_pipe pipe = to_mdp5_plane(plane)->pipe; +	int i; + +	for (i = 0; i < pipe2nclients(pipe); i++) +		mdp5_smp_commit(mdp5_kms, pipe2client(pipe, i)); +} + +enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane) +{ +	struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); +	return mdp5_plane->pipe; +} + +/* initialize plane */ +struct drm_plane *mdp5_plane_init(struct drm_device *dev, +		enum mdp5_pipe pipe, bool private_plane) +{ +	struct drm_plane *plane = NULL; +	struct mdp5_plane *mdp5_plane; +	int ret; +	enum drm_plane_type type; + +	mdp5_plane = kzalloc(sizeof(*mdp5_plane), GFP_KERNEL); +	if (!mdp5_plane) { +		ret = -ENOMEM; +		goto fail; +	} + +	plane = &mdp5_plane->base; + +	mdp5_plane->pipe = pipe; +	mdp5_plane->name = pipe2name(pipe); + +	mdp5_plane->nformats = mdp5_get_formats(pipe, mdp5_plane->formats, +			ARRAY_SIZE(mdp5_plane->formats)); + +	type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; +	drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs, +				 mdp5_plane->formats, mdp5_plane->nformats, +				 type); + +	mdp5_plane_install_properties(plane, &plane->base); + +	return plane; + +fail: +	if (plane) +		mdp5_plane_destroy(plane); + +	return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c new file mode 100644 index 00000000000..2d0236b963a --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "mdp5_kms.h" +#include "mdp5_smp.h" + + +/* SMP - Shared Memory Pool + * + * These are shared between all the clients, where each plane in a + * scanout buffer is a SMP client.  Ie. scanout of 3 plane I420 on + * pipe VIG0 => 3 clients: VIG0_Y, VIG0_CB, VIG0_CR. + * + * Based on the size of the attached scanout buffer, a certain # of + * blocks must be allocated to that client out of the shared pool. + * + * For each block, it can be either free, or pending/in-use by a + * client.  The updates happen in three steps: + * + *  1) mdp5_smp_request(): + *     When plane scanout is setup, calculate required number of + *     blocks needed per client, and request.  Blocks not inuse or + *     pending by any other client are added to client's pending + *     set. + * + *  2) mdp5_smp_configure(): + *     As hw is programmed, before FLUSH, MDP5_SMP_ALLOC registers + *     are configured for the union(pending, inuse) + * + *  3) mdp5_smp_commit(): + *     After next vblank, copy pending -> inuse.  Optionally update + *     MDP5_SMP_ALLOC registers if there are newly unused blocks + * + * On the next vblank after changes have been committed to hw, the + * client's pending blocks become it's in-use blocks (and no-longer + * in-use blocks become available to other clients). + * + * btw, hurray for confusing overloaded acronyms!  :-/ + * + * NOTE: for atomic modeset/pageflip NONBLOCK operations, step #1 + * should happen at (or before)? atomic->check().  And we'd need + * an API to discard previous requests if update is aborted or + * (test-only). + * + * TODO would perhaps be nice to have debugfs to dump out kernel + * inuse and pending state of all clients.. + */ + +static DEFINE_SPINLOCK(smp_lock); + + +/* step #1: update # of blocks pending for the client: */ +int mdp5_smp_request(struct mdp5_kms *mdp5_kms, +		enum mdp5_client_id cid, int nblks) +{ +	struct mdp5_client_smp_state *ps = &mdp5_kms->smp_client_state[cid]; +	int i, ret, avail, cur_nblks, cnt = mdp5_kms->smp_blk_cnt; +	unsigned long flags; + +	spin_lock_irqsave(&smp_lock, flags); + +	avail = cnt - bitmap_weight(mdp5_kms->smp_state, cnt); +	if (nblks > avail) { +		ret = -ENOSPC; +		goto fail; +	} + +	cur_nblks = bitmap_weight(ps->pending, cnt); +	if (nblks > cur_nblks) { +		/* grow the existing pending reservation: */ +		for (i = cur_nblks; i < nblks; i++) { +			int blk = find_first_zero_bit(mdp5_kms->smp_state, cnt); +			set_bit(blk, ps->pending); +			set_bit(blk, mdp5_kms->smp_state); +		} +	} else { +		/* shrink the existing pending reservation: */ +		for (i = cur_nblks; i > nblks; i--) { +			int blk = find_first_bit(ps->pending, cnt); +			clear_bit(blk, ps->pending); +			/* don't clear in global smp_state until _commit() */ +		} +	} + +fail: +	spin_unlock_irqrestore(&smp_lock, flags); +	return 0; +} + +static void update_smp_state(struct mdp5_kms *mdp5_kms, +		enum mdp5_client_id cid, mdp5_smp_state_t *assigned) +{ +	int cnt = mdp5_kms->smp_blk_cnt; +	uint32_t blk, val; + +	for_each_set_bit(blk, *assigned, cnt) { +		int idx = blk / 3; +		int fld = blk % 3; + +		val = mdp5_read(mdp5_kms, REG_MDP5_SMP_ALLOC_W_REG(idx)); + +		switch (fld) { +		case 0: +			val &= ~MDP5_SMP_ALLOC_W_REG_CLIENT0__MASK; +			val |= MDP5_SMP_ALLOC_W_REG_CLIENT0(cid); +			break; +		case 1: +			val &= ~MDP5_SMP_ALLOC_W_REG_CLIENT1__MASK; +			val |= MDP5_SMP_ALLOC_W_REG_CLIENT1(cid); +			break; +		case 2: +			val &= ~MDP5_SMP_ALLOC_W_REG_CLIENT2__MASK; +			val |= MDP5_SMP_ALLOC_W_REG_CLIENT2(cid); +			break; +		} + +		mdp5_write(mdp5_kms, REG_MDP5_SMP_ALLOC_W_REG(idx), val); +		mdp5_write(mdp5_kms, REG_MDP5_SMP_ALLOC_R_REG(idx), val); +	} +} + +/* step #2: configure hw for union(pending, inuse): */ +void mdp5_smp_configure(struct mdp5_kms *mdp5_kms, enum mdp5_client_id cid) +{ +	struct mdp5_client_smp_state *ps = &mdp5_kms->smp_client_state[cid]; +	int cnt = mdp5_kms->smp_blk_cnt; +	mdp5_smp_state_t assigned; + +	bitmap_or(assigned, ps->inuse, ps->pending, cnt); +	update_smp_state(mdp5_kms, cid, &assigned); +} + +/* step #3: after vblank, copy pending -> inuse: */ +void mdp5_smp_commit(struct mdp5_kms *mdp5_kms, enum mdp5_client_id cid) +{ +	struct mdp5_client_smp_state *ps = &mdp5_kms->smp_client_state[cid]; +	int cnt = mdp5_kms->smp_blk_cnt; +	mdp5_smp_state_t released; + +	/* +	 * Figure out if there are any blocks we where previously +	 * using, which can be released and made available to other +	 * clients: +	 */ +	if (bitmap_andnot(released, ps->inuse, ps->pending, cnt)) { +		unsigned long flags; + +		spin_lock_irqsave(&smp_lock, flags); +		/* clear released blocks: */ +		bitmap_andnot(mdp5_kms->smp_state, mdp5_kms->smp_state, +				released, cnt); +		spin_unlock_irqrestore(&smp_lock, flags); + +		update_smp_state(mdp5_kms, CID_UNUSED, &released); +	} + +	bitmap_copy(ps->inuse, ps->pending, cnt); +} diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h new file mode 100644 index 00000000000..0ab739e1a1d --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __MDP5_SMP_H__ +#define __MDP5_SMP_H__ + +#include "msm_drv.h" + +#define MAX_SMP_BLOCKS  22 +#define SMP_BLK_SIZE    4096 +#define SMP_ENTRIES_PER_BLK (SMP_BLK_SIZE / 16) + +typedef DECLARE_BITMAP(mdp5_smp_state_t, MAX_SMP_BLOCKS); + +struct mdp5_client_smp_state { +	mdp5_smp_state_t inuse; +	mdp5_smp_state_t pending; +}; + +struct mdp5_kms; + +int mdp5_smp_request(struct mdp5_kms *mdp5_kms, enum mdp5_client_id cid, int nblks); +void mdp5_smp_configure(struct mdp5_kms *mdp5_kms, enum mdp5_client_id cid); +void mdp5_smp_commit(struct mdp5_kms *mdp5_kms, enum mdp5_client_id cid); + + +#endif /* __MDP5_SMP_H__ */ diff --git a/drivers/gpu/drm/msm/mdp/mdp_common.xml.h b/drivers/gpu/drm/msm/mdp/mdp_common.xml.h new file mode 100644 index 00000000000..a9629b85b98 --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp_common.xml.h @@ -0,0 +1,78 @@ +#ifndef MDP_COMMON_XML +#define MDP_COMMON_XML + +/* Autogenerated file, DO NOT EDIT manually! + +This file was generated by the rules-ng-ng headergen tool in this git repository: +http://github.com/freedreno/envytools/ +git clone https://github.com/freedreno/envytools.git + +The rules-ng-ng source files this header was generated from are: +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    647 bytes, from 2013-11-30 14:45:35) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml            (  17996 bytes, from 2013-12-01 19:10:31) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml      (   1615 bytes, from 2013-11-30 15:00:52) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml            (  22517 bytes, from 2013-12-03 20:59:13) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1544 bytes, from 2013-08-16 19:17:05) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml         (    600 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  20932 bytes, from 2013-12-01 15:13:04) + +Copyright (C) 2013 by the following authors: +- Rob Clark <robdclark@gmail.com> (robclark) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice (including the +next paragraph) shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + + +enum mdp_mixer_stage_id { +	STAGE_UNUSED = 0, +	STAGE_BASE = 1, +	STAGE0 = 2, +	STAGE1 = 3, +	STAGE2 = 4, +	STAGE3 = 5, +}; + +enum mdp_alpha_type { +	FG_CONST = 0, +	BG_CONST = 1, +	FG_PIXEL = 2, +	BG_PIXEL = 3, +}; + +enum mdp_bpc { +	BPC1 = 0, +	BPC5 = 1, +	BPC6 = 2, +	BPC8 = 3, +}; + +enum mdp_bpc_alpha { +	BPC1A = 0, +	BPC4A = 1, +	BPC6A = 2, +	BPC8A = 3, +}; + + +#endif /* MDP_COMMON_XML */ diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_format.c b/drivers/gpu/drm/msm/mdp/mdp_format.c index 7b645f2e837..e0a6ffbe6ab 100644 --- a/drivers/gpu/drm/msm/mdp4/mdp4_format.c +++ b/drivers/gpu/drm/msm/mdp/mdp_format.c @@ -17,7 +17,7 @@  #include "msm_drv.h" -#include "mdp4_kms.h" +#include "mdp_kms.h"  #define FMT(name, a, r, g, b, e0, e1, e2, e3, alpha, tight, c, cnt) { \  		.base = { .pixel_format = DRM_FORMAT_ ## name }, \ @@ -34,7 +34,7 @@  #define BPC0A 0 -static const struct mdp4_format formats[] = { +static const struct mdp_format formats[] = {  	/*  name      a  r  g  b   e0 e1 e2 e3  alpha   tight  cpp cnt */  	FMT(ARGB8888, 8, 8, 8, 8,  1, 0, 2, 3,  true,   true,  4,  4),  	FMT(XRGB8888, 8, 8, 8, 8,  1, 0, 2, 3,  false,  true,  4,  4), @@ -44,11 +44,26 @@ static const struct mdp4_format formats[] = {  	FMT(BGR565,   0, 5, 6, 5,  2, 0, 1, 0,  false,  true,  2,  3),  }; -const struct msm_format *mdp4_get_format(struct msm_kms *kms, uint32_t format) +uint32_t mdp_get_formats(uint32_t *pixel_formats, uint32_t max_formats) +{ +	uint32_t i; +	for (i = 0; i < ARRAY_SIZE(formats); i++) { +		const struct mdp_format *f = &formats[i]; + +		if (i == max_formats) +			break; + +		pixel_formats[i] = f->base.pixel_format; +	} + +	return i; +} + +const struct msm_format *mdp_get_format(struct msm_kms *kms, uint32_t format)  {  	int i;  	for (i = 0; i < ARRAY_SIZE(formats); i++) { -		const struct mdp4_format *f = &formats[i]; +		const struct mdp_format *f = &formats[i];  		if (f->base.pixel_format == format)  			return &f->base;  	} diff --git a/drivers/gpu/drm/msm/mdp/mdp_kms.c b/drivers/gpu/drm/msm/mdp/mdp_kms.c new file mode 100644 index 00000000000..03455b64a24 --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp_kms.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "msm_drv.h" +#include "mdp_kms.h" + + +struct mdp_irq_wait { +	struct mdp_irq irq; +	int count; +}; + +static DECLARE_WAIT_QUEUE_HEAD(wait_event); + +static DEFINE_SPINLOCK(list_lock); + +static void update_irq(struct mdp_kms *mdp_kms) +{ +	struct mdp_irq *irq; +	uint32_t irqmask = mdp_kms->vblank_mask; + +	BUG_ON(!spin_is_locked(&list_lock)); + +	list_for_each_entry(irq, &mdp_kms->irq_list, node) +		irqmask |= irq->irqmask; + +	mdp_kms->funcs->set_irqmask(mdp_kms, irqmask); +} + +static void update_irq_unlocked(struct mdp_kms *mdp_kms) +{ +	unsigned long flags; +	spin_lock_irqsave(&list_lock, flags); +	update_irq(mdp_kms); +	spin_unlock_irqrestore(&list_lock, flags); +} + +void mdp_dispatch_irqs(struct mdp_kms *mdp_kms, uint32_t status) +{ +	struct mdp_irq *handler, *n; +	unsigned long flags; + +	spin_lock_irqsave(&list_lock, flags); +	mdp_kms->in_irq = true; +	list_for_each_entry_safe(handler, n, &mdp_kms->irq_list, node) { +		if (handler->irqmask & status) { +			spin_unlock_irqrestore(&list_lock, flags); +			handler->irq(handler, handler->irqmask & status); +			spin_lock_irqsave(&list_lock, flags); +		} +	} +	mdp_kms->in_irq = false; +	update_irq(mdp_kms); +	spin_unlock_irqrestore(&list_lock, flags); + +} + +void mdp_update_vblank_mask(struct mdp_kms *mdp_kms, uint32_t mask, bool enable) +{ +	unsigned long flags; + +	spin_lock_irqsave(&list_lock, flags); +	if (enable) +		mdp_kms->vblank_mask |= mask; +	else +		mdp_kms->vblank_mask &= ~mask; +	update_irq(mdp_kms); +	spin_unlock_irqrestore(&list_lock, flags); +} + +static void wait_irq(struct mdp_irq *irq, uint32_t irqstatus) +{ +	struct mdp_irq_wait *wait = +			container_of(irq, struct mdp_irq_wait, irq); +	wait->count--; +	wake_up_all(&wait_event); +} + +void mdp_irq_wait(struct mdp_kms *mdp_kms, uint32_t irqmask) +{ +	struct mdp_irq_wait wait = { +		.irq = { +			.irq = wait_irq, +			.irqmask = irqmask, +		}, +		.count = 1, +	}; +	mdp_irq_register(mdp_kms, &wait.irq); +	wait_event_timeout(wait_event, (wait.count <= 0), +			msecs_to_jiffies(100)); +	mdp_irq_unregister(mdp_kms, &wait.irq); +} + +void mdp_irq_register(struct mdp_kms *mdp_kms, struct mdp_irq *irq) +{ +	unsigned long flags; +	bool needs_update = false; + +	spin_lock_irqsave(&list_lock, flags); + +	if (!irq->registered) { +		irq->registered = true; +		list_add(&irq->node, &mdp_kms->irq_list); +		needs_update = !mdp_kms->in_irq; +	} + +	spin_unlock_irqrestore(&list_lock, flags); + +	if (needs_update) +		update_irq_unlocked(mdp_kms); +} + +void mdp_irq_unregister(struct mdp_kms *mdp_kms, struct mdp_irq *irq) +{ +	unsigned long flags; +	bool needs_update = false; + +	spin_lock_irqsave(&list_lock, flags); + +	if (irq->registered) { +		irq->registered = false; +		list_del(&irq->node); +		needs_update = !mdp_kms->in_irq; +	} + +	spin_unlock_irqrestore(&list_lock, flags); + +	if (needs_update) +		update_irq_unlocked(mdp_kms); +} diff --git a/drivers/gpu/drm/msm/mdp/mdp_kms.h b/drivers/gpu/drm/msm/mdp/mdp_kms.h new file mode 100644 index 00000000000..99557b5ad4f --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp_kms.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __MDP_KMS_H__ +#define __MDP_KMS_H__ + +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> + +#include "msm_drv.h" +#include "msm_kms.h" +#include "mdp_common.xml.h" + +struct mdp_kms; + +struct mdp_kms_funcs { +	struct msm_kms_funcs base; +	void (*set_irqmask)(struct mdp_kms *mdp_kms, uint32_t irqmask); +}; + +struct mdp_kms { +	struct msm_kms base; + +	const struct mdp_kms_funcs *funcs; + +	/* irq handling: */ +	bool in_irq; +	struct list_head irq_list;    /* list of mdp4_irq */ +	uint32_t vblank_mask;         /* irq bits set for userspace vblank */ +}; +#define to_mdp_kms(x) container_of(x, struct mdp_kms, base) + +static inline void mdp_kms_init(struct mdp_kms *mdp_kms, +		const struct mdp_kms_funcs *funcs) +{ +	mdp_kms->funcs = funcs; +	INIT_LIST_HEAD(&mdp_kms->irq_list); +	msm_kms_init(&mdp_kms->base, &funcs->base); +} + +/* + * irq helpers: + */ + +/* For transiently registering for different MDP irqs that various parts + * of the KMS code need during setup/configuration.  These are not + * necessarily the same as what drm_vblank_get/put() are requesting, and + * the hysteresis in drm_vblank_put() is not necessarily desirable for + * internal housekeeping related irq usage. + */ +struct mdp_irq { +	struct list_head node; +	uint32_t irqmask; +	bool registered; +	void (*irq)(struct mdp_irq *irq, uint32_t irqstatus); +}; + +void mdp_dispatch_irqs(struct mdp_kms *mdp_kms, uint32_t status); +void mdp_update_vblank_mask(struct mdp_kms *mdp_kms, uint32_t mask, bool enable); +void mdp_irq_wait(struct mdp_kms *mdp_kms, uint32_t irqmask); +void mdp_irq_register(struct mdp_kms *mdp_kms, struct mdp_irq *irq); +void mdp_irq_unregister(struct mdp_kms *mdp_kms, struct mdp_irq *irq); + + +/* + * pixel format helpers: + */ + +struct mdp_format { +	struct msm_format base; +	enum mdp_bpc bpc_r, bpc_g, bpc_b; +	enum mdp_bpc_alpha bpc_a; +	uint8_t unpack[4]; +	bool alpha_enable, unpack_tight; +	uint8_t cpp, unpack_count; +}; +#define to_mdp_format(x) container_of(x, struct mdp_format, base) + +uint32_t mdp_get_formats(uint32_t *formats, uint32_t max_formats); +const struct msm_format *mdp_get_format(struct msm_kms *kms, uint32_t format); + +#endif /* __MDP_KMS_H__ */ diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_irq.c b/drivers/gpu/drm/msm/mdp4/mdp4_irq.c deleted file mode 100644 index 5c6b7fca4ed..00000000000 --- a/drivers/gpu/drm/msm/mdp4/mdp4_irq.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2013 Red Hat - * Author: Rob Clark <robdclark@gmail.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program.  If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "msm_drv.h" -#include "mdp4_kms.h" - - -struct mdp4_irq_wait { -	struct mdp4_irq irq; -	int count; -}; - -static DECLARE_WAIT_QUEUE_HEAD(wait_event); - -static DEFINE_SPINLOCK(list_lock); - -static void update_irq(struct mdp4_kms *mdp4_kms) -{ -	struct mdp4_irq *irq; -	uint32_t irqmask = mdp4_kms->vblank_mask; - -	BUG_ON(!spin_is_locked(&list_lock)); - -	list_for_each_entry(irq, &mdp4_kms->irq_list, node) -		irqmask |= irq->irqmask; - -	mdp4_write(mdp4_kms, REG_MDP4_INTR_ENABLE, irqmask); -} - -static void update_irq_unlocked(struct mdp4_kms *mdp4_kms) -{ -	unsigned long flags; -	spin_lock_irqsave(&list_lock, flags); -	update_irq(mdp4_kms); -	spin_unlock_irqrestore(&list_lock, flags); -} - -static void mdp4_irq_error_handler(struct mdp4_irq *irq, uint32_t irqstatus) -{ -	DRM_ERROR("errors: %08x\n", irqstatus); -} - -void mdp4_irq_preinstall(struct msm_kms *kms) -{ -	struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms); -	mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, 0xffffffff); -} - -int mdp4_irq_postinstall(struct msm_kms *kms) -{ -	struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms); -	struct mdp4_irq *error_handler = &mdp4_kms->error_handler; - -	INIT_LIST_HEAD(&mdp4_kms->irq_list); - -	error_handler->irq = mdp4_irq_error_handler; -	error_handler->irqmask = MDP4_IRQ_PRIMARY_INTF_UDERRUN | -			MDP4_IRQ_EXTERNAL_INTF_UDERRUN; - -	mdp4_irq_register(mdp4_kms, error_handler); - -	return 0; -} - -void mdp4_irq_uninstall(struct msm_kms *kms) -{ -	struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms); -	mdp4_write(mdp4_kms, REG_MDP4_INTR_ENABLE, 0x00000000); -} - -irqreturn_t mdp4_irq(struct msm_kms *kms) -{ -	struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms); -	struct drm_device *dev = mdp4_kms->dev; -	struct msm_drm_private *priv = dev->dev_private; -	struct mdp4_irq *handler, *n; -	unsigned long flags; -	unsigned int id; -	uint32_t status; - -	status = mdp4_read(mdp4_kms, REG_MDP4_INTR_STATUS); -	mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, status); - -	VERB("status=%08x", status); - -	for (id = 0; id < priv->num_crtcs; id++) -		if (status & mdp4_crtc_vblank(priv->crtcs[id])) -			drm_handle_vblank(dev, id); - -	spin_lock_irqsave(&list_lock, flags); -	mdp4_kms->in_irq = true; -	list_for_each_entry_safe(handler, n, &mdp4_kms->irq_list, node) { -		if (handler->irqmask & status) { -			spin_unlock_irqrestore(&list_lock, flags); -			handler->irq(handler, handler->irqmask & status); -			spin_lock_irqsave(&list_lock, flags); -		} -	} -	mdp4_kms->in_irq = false; -	update_irq(mdp4_kms); -	spin_unlock_irqrestore(&list_lock, flags); - -	return IRQ_HANDLED; -} - -int mdp4_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) -{ -	struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms); -	unsigned long flags; - -	spin_lock_irqsave(&list_lock, flags); -	mdp4_kms->vblank_mask |= mdp4_crtc_vblank(crtc); -	update_irq(mdp4_kms); -	spin_unlock_irqrestore(&list_lock, flags); - -	return 0; -} - -void mdp4_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) -{ -	struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms); -	unsigned long flags; - -	spin_lock_irqsave(&list_lock, flags); -	mdp4_kms->vblank_mask &= ~mdp4_crtc_vblank(crtc); -	update_irq(mdp4_kms); -	spin_unlock_irqrestore(&list_lock, flags); -} - -static void wait_irq(struct mdp4_irq *irq, uint32_t irqstatus) -{ -	struct mdp4_irq_wait *wait = -			container_of(irq, struct mdp4_irq_wait, irq); -	wait->count--; -	wake_up_all(&wait_event); -} - -void mdp4_irq_wait(struct mdp4_kms *mdp4_kms, uint32_t irqmask) -{ -	struct mdp4_irq_wait wait = { -		.irq = { -			.irq = wait_irq, -			.irqmask = irqmask, -		}, -		.count = 1, -	}; -	mdp4_irq_register(mdp4_kms, &wait.irq); -	wait_event(wait_event, (wait.count <= 0)); -	mdp4_irq_unregister(mdp4_kms, &wait.irq); -} - -void mdp4_irq_register(struct mdp4_kms *mdp4_kms, struct mdp4_irq *irq) -{ -	unsigned long flags; -	bool needs_update = false; - -	spin_lock_irqsave(&list_lock, flags); - -	if (!irq->registered) { -		irq->registered = true; -		list_add(&irq->node, &mdp4_kms->irq_list); -		needs_update = !mdp4_kms->in_irq; -	} - -	spin_unlock_irqrestore(&list_lock, flags); - -	if (needs_update) -		update_irq_unlocked(mdp4_kms); -} - -void mdp4_irq_unregister(struct mdp4_kms *mdp4_kms, struct mdp4_irq *irq) -{ -	unsigned long flags; -	bool needs_update = false; - -	spin_lock_irqsave(&list_lock, flags); - -	if (irq->registered) { -		irq->registered = false; -		list_del(&irq->node); -		needs_update = !mdp4_kms->in_irq; -	} - -	spin_unlock_irqrestore(&list_lock, flags); - -	if (needs_update) -		update_irq_unlocked(mdp4_kms); -} diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 864c9773636..9a5d87db5c2 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -17,8 +17,7 @@  #include "msm_drv.h"  #include "msm_gpu.h" - -#include <mach/iommu.h> +#include "msm_kms.h"  static void msm_fb_output_poll_changed(struct drm_device *dev)  { @@ -32,48 +31,19 @@ static const struct drm_mode_config_funcs mode_config_funcs = {  	.output_poll_changed = msm_fb_output_poll_changed,  }; -static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev, -		unsigned long iova, int flags, void *arg) -{ -	DBG("*** fault: iova=%08lx, flags=%d", iova, flags); -	return 0; -} - -int msm_register_iommu(struct drm_device *dev, struct iommu_domain *iommu) +int msm_register_mmu(struct drm_device *dev, struct msm_mmu *mmu)  {  	struct msm_drm_private *priv = dev->dev_private; -	int idx = priv->num_iommus++; +	int idx = priv->num_mmus++; -	if (WARN_ON(idx >= ARRAY_SIZE(priv->iommus))) +	if (WARN_ON(idx >= ARRAY_SIZE(priv->mmus)))  		return -EINVAL; -	priv->iommus[idx] = iommu; - -	iommu_set_fault_handler(iommu, msm_fault_handler, dev); - -	/* need to iommu_attach_device() somewhere??  on resume?? */ +	priv->mmus[idx] = mmu;  	return idx;  } -int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu, -		const char **names, int cnt) -{ -	int i, ret; - -	for (i = 0; i < cnt; i++) { -		struct device *ctx = msm_iommu_get_ctx(names[i]); -		if (!ctx) -			continue; -		ret = iommu_attach_device(iommu, ctx); -		if (ret) { -			dev_warn(dev->dev, "could not attach iommu to %s", names[i]); -			return ret; -		} -	} -	return 0; -} -  #ifdef CONFIG_DRM_MSM_REGISTER_LOGGING  static bool reglog = false;  MODULE_PARM_DESC(reglog, "Enable register read/write logging"); @@ -82,6 +52,14 @@ module_param(reglog, bool, 0600);  #define reglog 0  #endif +static char *vram; +MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU"); +module_param(vram, charp, 0); + +/* + * Util/helpers: + */ +  void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,  		const char *dbgname)  { @@ -161,6 +139,16 @@ static int msm_unload(struct drm_device *dev)  		mutex_unlock(&dev->struct_mutex);  	} +	if (priv->vram.paddr) { +		DEFINE_DMA_ATTRS(attrs); +		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs); +		drm_mm_takedown(&priv->vram.mm); +		dma_free_attrs(dev->dev, priv->vram.size, NULL, +				priv->vram.paddr, &attrs); +	} + +	component_unbind_all(dev->dev, dev); +  	dev->dev_private = NULL;  	kfree(priv); @@ -168,6 +156,24 @@ static int msm_unload(struct drm_device *dev)  	return 0;  } +static int get_mdp_ver(struct platform_device *pdev) +{ +#ifdef CONFIG_OF +	static const struct of_device_id match_types[] = { { +		.compatible = "qcom,mdss_mdp", +		.data	= (void	*)5, +	}, { +		/* end node */ +	} }; +	struct device *dev = &pdev->dev; +	const struct of_device_id *match; +	match = of_match_node(match_types, dev->of_node); +	if (match) +		return (int)match->data; +#endif +	return 4; +} +  static int msm_load(struct drm_device *dev, unsigned long flags)  {  	struct platform_device *pdev = dev->platformdev; @@ -175,6 +181,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)  	struct msm_kms *kms;  	int ret; +  	priv = kzalloc(sizeof(*priv), GFP_KERNEL);  	if (!priv) {  		dev_err(dev->dev, "failed to allocate private data\n"); @@ -187,10 +194,64 @@ static int msm_load(struct drm_device *dev, unsigned long flags)  	init_waitqueue_head(&priv->fence_event);  	INIT_LIST_HEAD(&priv->inactive_list); +	INIT_LIST_HEAD(&priv->fence_cbs);  	drm_mode_config_init(dev); -	kms = mdp4_kms_init(dev); +	/* if we have no IOMMU, then we need to use carveout allocator. +	 * Grab the entire CMA chunk carved out in early startup in +	 * mach-msm: +	 */ +	if (!iommu_present(&platform_bus_type)) { +		DEFINE_DMA_ATTRS(attrs); +		unsigned long size; +		void *p; + +		DBG("using %s VRAM carveout", vram); +		size = memparse(vram, NULL); +		priv->vram.size = size; + +		drm_mm_init(&priv->vram.mm, 0, (size >> PAGE_SHIFT) - 1); + +		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs); +		dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs); + +		/* note that for no-kernel-mapping, the vaddr returned +		 * is bogus, but non-null if allocation succeeded: +		 */ +		p = dma_alloc_attrs(dev->dev, size, +				&priv->vram.paddr, GFP_KERNEL, &attrs); +		if (!p) { +			dev_err(dev->dev, "failed to allocate VRAM\n"); +			priv->vram.paddr = 0; +			ret = -ENOMEM; +			goto fail; +		} + +		dev_info(dev->dev, "VRAM: %08x->%08x\n", +				(uint32_t)priv->vram.paddr, +				(uint32_t)(priv->vram.paddr + size)); +	} + +	platform_set_drvdata(pdev, dev); + +	/* Bind all our sub-components: */ +	ret = component_bind_all(dev->dev, dev); +	if (ret) +		return ret; + +	switch (get_mdp_ver(pdev)) { +	case 4: +		kms = mdp4_kms_init(dev); +		break; +	case 5: +		kms = mdp5_kms_init(dev); +		break; +	default: +		kms = ERR_PTR(-ENODEV); +		break; +	} +  	if (IS_ERR(kms)) {  		/*  		 * NOTE: once we have GPU support, having no kms should not @@ -199,7 +260,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)  		 * imx drm driver on iMX5  		 */  		dev_err(dev->dev, "failed to load kms\n"); -		ret = PTR_ERR(priv->kms); +		ret = PTR_ERR(kms);  		goto fail;  	} @@ -227,19 +288,21 @@ static int msm_load(struct drm_device *dev, unsigned long flags)  	}  	pm_runtime_get_sync(dev->dev); -	ret = drm_irq_install(dev); +	ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));  	pm_runtime_put_sync(dev->dev);  	if (ret < 0) {  		dev_err(dev->dev, "failed to install IRQ handler\n");  		goto fail;  	} -	platform_set_drvdata(pdev, dev); -  #ifdef CONFIG_DRM_MSM_FBDEV  	priv->fbdev = msm_fbdev_init(dev);  #endif +	ret = msm_debugfs_late_init(dev); +	if (ret) +		goto fail; +  	drm_kms_helper_poll_init(dev);  	return 0; @@ -264,7 +327,6 @@ static void load_gpu(struct drm_device *dev)  		gpu = NULL;  		/* not fatal */  	} -	mutex_unlock(&dev->struct_mutex);  	if (gpu) {  		int ret; @@ -274,10 +336,16 @@ static void load_gpu(struct drm_device *dev)  			dev_err(dev->dev, "gpu hw init failed: %d\n", ret);  			gpu->funcs->destroy(gpu);  			gpu = NULL; +		} else { +			/* give inactive pm a chance to kick in: */ +			msm_gpu_retire(gpu);  		} +  	}  	priv->gpu = gpu; + +	mutex_unlock(&dev->struct_mutex);  }  static int msm_open(struct drm_device *dev, struct drm_file *file) @@ -318,14 +386,11 @@ static void msm_preclose(struct drm_device *dev, struct drm_file *file)  static void msm_lastclose(struct drm_device *dev)  {  	struct msm_drm_private *priv = dev->dev_private; -	if (priv->fbdev) { -		drm_modeset_lock_all(dev); -		drm_fb_helper_restore_fbdev_mode(priv->fbdev); -		drm_modeset_unlock_all(dev); -	} +	if (priv->fbdev) +		drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);  } -static irqreturn_t msm_irq(DRM_IRQ_ARGS) +static irqreturn_t msm_irq(int irq, void *arg)  {  	struct drm_device *dev = arg;  	struct msm_drm_private *priv = dev->dev_private; @@ -414,7 +479,7 @@ static int msm_gem_show(struct drm_device *dev, struct seq_file *m)  static int msm_mm_show(struct drm_device *dev, struct seq_file *m)  { -	return drm_mm_dump_table(m, dev->mm_private); +	return drm_mm_dump_table(m, &dev->vma_offset_manager->vm_addr_space_mm);  }  static int msm_fb_show(struct drm_device *dev, struct seq_file *m) @@ -467,6 +532,41 @@ static struct drm_info_list msm_debugfs_list[] = {  		{ "fb", show_locked, 0, msm_fb_show },  }; +static int late_init_minor(struct drm_minor *minor) +{ +	int ret; + +	if (!minor) +		return 0; + +	ret = msm_rd_debugfs_init(minor); +	if (ret) { +		dev_err(minor->dev->dev, "could not install rd debugfs\n"); +		return ret; +	} + +	ret = msm_perf_debugfs_init(minor); +	if (ret) { +		dev_err(minor->dev->dev, "could not install perf debugfs\n"); +		return ret; +	} + +	return 0; +} + +int msm_debugfs_late_init(struct drm_device *dev) +{ +	int ret; +	ret = late_init_minor(dev->primary); +	if (ret) +		return ret; +	ret = late_init_minor(dev->render); +	if (ret) +		return ret; +	ret = late_init_minor(dev->control); +	return ret; +} +  static int msm_debugfs_init(struct drm_minor *minor)  {  	struct drm_device *dev = minor->dev; @@ -481,13 +581,17 @@ static int msm_debugfs_init(struct drm_minor *minor)  		return ret;  	} -	return ret; +	return 0;  }  static void msm_debugfs_cleanup(struct drm_minor *minor)  {  	drm_debugfs_remove_files(msm_debugfs_list,  			ARRAY_SIZE(msm_debugfs_list), minor); +	if (!minor->dev->dev_private) +		return; +	msm_rd_debugfs_cleanup(minor); +	msm_perf_debugfs_cleanup(minor);  }  #endif @@ -499,39 +603,76 @@ int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,  		struct timespec *timeout)  {  	struct msm_drm_private *priv = dev->dev_private; -	unsigned long timeout_jiffies = timespec_to_jiffies(timeout); -	unsigned long start_jiffies = jiffies; -	unsigned long remaining_jiffies;  	int ret; -	if (time_after(start_jiffies, timeout_jiffies)) -		remaining_jiffies = 0; -	else -		remaining_jiffies = timeout_jiffies - start_jiffies; - -	ret = wait_event_interruptible_timeout(priv->fence_event, -			priv->completed_fence >= fence, -			remaining_jiffies); -	if (ret == 0) { -		DBG("timeout waiting for fence: %u (completed: %u)", -				fence, priv->completed_fence); -		ret = -ETIMEDOUT; -	} else if (ret != -ERESTARTSYS) { -		ret = 0; +	if (!priv->gpu) +		return 0; + +	if (fence > priv->gpu->submitted_fence) { +		DRM_ERROR("waiting on invalid fence: %u (of %u)\n", +				fence, priv->gpu->submitted_fence); +		return -EINVAL; +	} + +	if (!timeout) { +		/* no-wait: */ +		ret = fence_completed(dev, fence) ? 0 : -EBUSY; +	} else { +		unsigned long timeout_jiffies = timespec_to_jiffies(timeout); +		unsigned long start_jiffies = jiffies; +		unsigned long remaining_jiffies; + +		if (time_after(start_jiffies, timeout_jiffies)) +			remaining_jiffies = 0; +		else +			remaining_jiffies = timeout_jiffies - start_jiffies; + +		ret = wait_event_interruptible_timeout(priv->fence_event, +				fence_completed(dev, fence), +				remaining_jiffies); + +		if (ret == 0) { +			DBG("timeout waiting for fence: %u (completed: %u)", +					fence, priv->completed_fence); +			ret = -ETIMEDOUT; +		} else if (ret != -ERESTARTSYS) { +			ret = 0; +		}  	}  	return ret;  } -/* call under struct_mutex */ +/* called from workqueue */  void msm_update_fence(struct drm_device *dev, uint32_t fence)  {  	struct msm_drm_private *priv = dev->dev_private; -	if (fence > priv->completed_fence) { -		priv->completed_fence = fence; -		wake_up_all(&priv->fence_event); +	mutex_lock(&dev->struct_mutex); +	priv->completed_fence = max(fence, priv->completed_fence); + +	while (!list_empty(&priv->fence_cbs)) { +		struct msm_fence_cb *cb; + +		cb = list_first_entry(&priv->fence_cbs, +				struct msm_fence_cb, work.entry); + +		if (cb->fence > priv->completed_fence) +			break; + +		list_del_init(&cb->work.entry); +		queue_work(priv->wq, &cb->work);  	} + +	mutex_unlock(&dev->struct_mutex); + +	wake_up_all(&priv->fence_event); +} + +void __msm_fence_worker(struct work_struct *work) +{ +	struct msm_fence_cb *cb = container_of(work, struct msm_fence_cb, work); +	cb->func(cb);  }  /* @@ -563,6 +704,12 @@ static int msm_ioctl_gem_new(struct drm_device *dev, void *data,  		struct drm_file *file)  {  	struct drm_msm_gem_new *args = data; + +	if (args->flags & ~MSM_BO_FLAGS) { +		DRM_ERROR("invalid flags: %08x\n", args->flags); +		return -EINVAL; +	} +  	return msm_gem_new_handle(dev, file, args->size,  			args->flags, &args->handle);  } @@ -576,6 +723,11 @@ static int msm_ioctl_gem_cpu_prep(struct drm_device *dev, void *data,  	struct drm_gem_object *obj;  	int ret; +	if (args->op & ~MSM_PREP_FLAGS) { +		DRM_ERROR("invalid op: %08x\n", args->op); +		return -EINVAL; +	} +  	obj = drm_gem_object_lookup(dev, file, args->handle);  	if (!obj)  		return -ENOENT; @@ -630,17 +782,24 @@ static int msm_ioctl_wait_fence(struct drm_device *dev, void *data,  		struct drm_file *file)  {  	struct drm_msm_wait_fence *args = data; -	return msm_wait_fence_interruptable(dev, args->fence, &TS(args->timeout)); + +	if (args->pad) { +		DRM_ERROR("invalid pad: %08x\n", args->pad); +		return -EINVAL; +	} + +	return msm_wait_fence_interruptable(dev, args->fence, +			&TS(args->timeout));  }  static const struct drm_ioctl_desc msm_ioctls[] = { -	DRM_IOCTL_DEF_DRV(MSM_GET_PARAM,    msm_ioctl_get_param,    DRM_UNLOCKED|DRM_AUTH), -	DRM_IOCTL_DEF_DRV(MSM_GEM_NEW,      msm_ioctl_gem_new,      DRM_UNLOCKED|DRM_AUTH), -	DRM_IOCTL_DEF_DRV(MSM_GEM_INFO,     msm_ioctl_gem_info,     DRM_UNLOCKED|DRM_AUTH), -	DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH), -	DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH), -	DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT,   msm_ioctl_gem_submit,   DRM_UNLOCKED|DRM_AUTH), -	DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE,   msm_ioctl_wait_fence,   DRM_UNLOCKED|DRM_AUTH), +	DRM_IOCTL_DEF_DRV(MSM_GET_PARAM,    msm_ioctl_get_param,    DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(MSM_GEM_NEW,      msm_ioctl_gem_new,      DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(MSM_GEM_INFO,     msm_ioctl_gem_info,     DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT,   msm_ioctl_gem_submit,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE,   msm_ioctl_wait_fence,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),  };  static const struct vm_operations_struct vm_ops = { @@ -664,7 +823,11 @@ static const struct file_operations fops = {  };  static struct drm_driver msm_driver = { -	.driver_features    = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, +	.driver_features    = DRIVER_HAVE_IRQ | +				DRIVER_GEM | +				DRIVER_PRIME | +				DRIVER_RENDER | +				DRIVER_MODESET,  	.load               = msm_load,  	.unload             = msm_unload,  	.open               = msm_open, @@ -681,7 +844,17 @@ static struct drm_driver msm_driver = {  	.gem_vm_ops         = &vm_ops,  	.dumb_create        = msm_gem_dumb_create,  	.dumb_map_offset    = msm_gem_dumb_map_offset, -	.dumb_destroy       = msm_gem_dumb_destroy, +	.dumb_destroy       = 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   = drm_gem_prime_export, +	.gem_prime_import   = drm_gem_prime_import, +	.gem_prime_pin      = msm_gem_prime_pin, +	.gem_prime_unpin    = msm_gem_prime_unpin, +	.gem_prime_get_sg_table = msm_gem_prime_get_sg_table, +	.gem_prime_import_sg_table = msm_gem_prime_import_sg_table, +	.gem_prime_vmap     = msm_gem_prime_vmap, +	.gem_prime_vunmap   = msm_gem_prime_vunmap,  #ifdef CONFIG_DEBUG_FS  	.debugfs_init       = msm_debugfs_init,  	.debugfs_cleanup    = msm_debugfs_cleanup, @@ -721,17 +894,110 @@ static const struct dev_pm_ops msm_pm_ops = {  };  /* + * Componentized driver support: + */ + +#ifdef CONFIG_OF +/* NOTE: the CONFIG_OF case duplicates the same code as exynos or imx + * (or probably any other).. so probably some room for some helpers + */ +static int compare_of(struct device *dev, void *data) +{ +	return dev->of_node == data; +} + +static int msm_drm_add_components(struct device *master, struct master *m) +{ +	struct device_node *np = master->of_node; +	unsigned i; +	int ret; + +	for (i = 0; ; i++) { +		struct device_node *node; + +		node = of_parse_phandle(np, "connectors", i); +		if (!node) +			break; + +		ret = component_master_add_child(m, compare_of, node); +		of_node_put(node); + +		if (ret) +			return ret; +	} +	return 0; +} +#else +static int compare_dev(struct device *dev, void *data) +{ +	return dev == data; +} + +static int msm_drm_add_components(struct device *master, struct master *m) +{ +	/* For non-DT case, it kinda sucks.  We don't actually have a way +	 * to know whether or not we are waiting for certain devices (or if +	 * they are simply not present).  But for non-DT we only need to +	 * care about apq8064/apq8060/etc (all mdp4/a3xx): +	 */ +	static const char *devnames[] = { +			"hdmi_msm.0", "kgsl-3d0.0", +	}; +	int i; + +	DBG("Adding components.."); + +	for (i = 0; i < ARRAY_SIZE(devnames); i++) { +		struct device *dev; +		int ret; + +		dev = bus_find_device_by_name(&platform_bus_type, +				NULL, devnames[i]); +		if (!dev) { +			dev_info(master, "still waiting for %s\n", devnames[i]); +			return -EPROBE_DEFER; +		} + +		ret = component_master_add_child(m, compare_dev, dev); +		if (ret) { +			DBG("could not add child: %d", ret); +			return ret; +		} +	} + +	return 0; +} +#endif + +static int msm_drm_bind(struct device *dev) +{ +	return drm_platform_init(&msm_driver, to_platform_device(dev)); +} + +static void msm_drm_unbind(struct device *dev) +{ +	drm_put_dev(platform_get_drvdata(to_platform_device(dev))); +} + +static const struct component_master_ops msm_drm_ops = { +		.add_components = msm_drm_add_components, +		.bind = msm_drm_bind, +		.unbind = msm_drm_unbind, +}; + +/*   * Platform driver:   */  static int msm_pdev_probe(struct platform_device *pdev)  { -	return drm_platform_init(&msm_driver, pdev); +	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); +	return component_master_add(&pdev->dev, &msm_drm_ops);  }  static int msm_pdev_remove(struct platform_device *pdev)  { -	drm_platform_exit(&msm_driver, pdev); +	component_master_del(&pdev->dev, &msm_drm_ops);  	return 0;  } @@ -741,12 +1007,19 @@ static const struct platform_device_id msm_id[] = {  	{ }  }; +static const struct of_device_id dt_match[] = { +	{ .compatible = "qcom,mdss_mdp" }, +	{} +}; +MODULE_DEVICE_TABLE(of, dt_match); +  static struct platform_driver msm_platform_driver = {  	.probe      = msm_pdev_probe,  	.remove     = msm_pdev_remove,  	.driver     = {  		.owner  = THIS_MODULE,  		.name   = "msm", +		.of_match_table = dt_match,  		.pm     = &msm_pm_ops,  	},  	.id_table   = msm_id, diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 80d75094bf0..8a2c5fd0893 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -22,6 +22,7 @@  #include <linux/clk.h>  #include <linux/cpufreq.h>  #include <linux/module.h> +#include <linux/component.h>  #include <linux/platform_device.h>  #include <linux/pm.h>  #include <linux/pm_runtime.h> @@ -31,6 +32,15 @@  #include <linux/types.h>  #include <asm/sizes.h> + +#if defined(CONFIG_COMPILE_TEST) && !defined(CONFIG_ARCH_QCOM) +/* stubs we need for compile-test: */ +static inline struct device *msm_iommu_get_ctx(const char *ctx_name) +{ +	return NULL; +} +#endif +  #ifndef CONFIG_OF  #include <mach/board.h>  #include <mach/socinfo.h> @@ -44,6 +54,10 @@  struct msm_kms;  struct msm_gpu; +struct msm_mmu; +struct msm_rd_state; +struct msm_perf_state; +struct msm_gem_submit;  #define NUM_DOMAINS 2    /* one for KMS, then one per gpu core (?) */ @@ -59,6 +73,9 @@ struct msm_drm_private {  	struct msm_kms *kms; +	/* subordinate devices, if present: */ +	struct platform_device *hdmi_pdev, *gpu_pdev; +  	/* when we have more than one 'msm_gpu' these need to be an array: */  	struct msm_gpu *gpu;  	struct msm_file_private *lastctx; @@ -68,14 +85,23 @@ struct msm_drm_private {  	uint32_t next_fence, completed_fence;  	wait_queue_head_t fence_event; +	struct msm_rd_state *rd; +	struct msm_perf_state *perf; +  	/* list of GEM objects: */  	struct list_head inactive_list;  	struct workqueue_struct *wq; -	/* registered IOMMU domains: */ -	unsigned int num_iommus; -	struct iommu_domain *iommus[NUM_DOMAINS]; +	/* callbacks deferred until bo is inactive: */ +	struct list_head fence_cbs; + +	/* registered MMUs: */ +	unsigned int num_mmus; +	struct msm_mmu *mmus[NUM_DOMAINS]; + +	unsigned int num_planes; +	struct drm_plane *planes[8];  	unsigned int num_crtcs;  	struct drm_crtc *crtcs[8]; @@ -88,45 +114,37 @@ struct msm_drm_private {  	unsigned int num_connectors;  	struct drm_connector *connectors[8]; + +	/* VRAM carveout, used when no IOMMU: */ +	struct { +		unsigned long size; +		dma_addr_t paddr; +		/* NOTE: mm managed at the page level, size is in # of pages +		 * and position mm_node->start is in # of pages: +		 */ +		struct drm_mm mm; +	} vram;  };  struct msm_format {  	uint32_t pixel_format;  }; -/* As there are different display controller blocks depending on the - * snapdragon version, the kms support is split out and the appropriate - * implementation is loaded at runtime.  The kms module is responsible - * for constructing the appropriate planes/crtcs/encoders/connectors. - */ -struct msm_kms_funcs { -	/* hw initialization: */ -	int (*hw_init)(struct msm_kms *kms); -	/* irq handling: */ -	void (*irq_preinstall)(struct msm_kms *kms); -	int (*irq_postinstall)(struct msm_kms *kms); -	void (*irq_uninstall)(struct msm_kms *kms); -	irqreturn_t (*irq)(struct msm_kms *kms); -	int (*enable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc); -	void (*disable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc); -	/* misc: */ -	const struct msm_format *(*get_format)(struct msm_kms *kms, uint32_t format); -	long (*round_pixclk)(struct msm_kms *kms, unsigned long rate, -			struct drm_encoder *encoder); -	/* cleanup: */ -	void (*preclose)(struct msm_kms *kms, struct drm_file *file); -	void (*destroy)(struct msm_kms *kms); +/* callback from wq once fence has passed: */ +struct msm_fence_cb { +	struct work_struct work; +	uint32_t fence; +	void (*func)(struct msm_fence_cb *cb);  }; -struct msm_kms { -	const struct msm_kms_funcs *funcs; -}; +void __msm_fence_worker(struct work_struct *work); -struct msm_kms *mdp4_kms_init(struct drm_device *dev); +#define INIT_FENCE_CB(_cb, _func)  do {                     \ +		INIT_WORK(&(_cb)->work, __msm_fence_worker); \ +		(_cb)->func = _func;                         \ +	} while (0) -int msm_register_iommu(struct drm_device *dev, struct iommu_domain *iommu); -int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu, -		const char **names, int cnt); +int msm_register_mmu(struct drm_device *dev, struct msm_mmu *mmu);  int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,  		struct timespec *timeout); @@ -141,19 +159,26 @@ uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj);  int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id,  		uint32_t *iova);  int msm_gem_get_iova(struct drm_gem_object *obj, int id, uint32_t *iova); +struct page **msm_gem_get_pages(struct drm_gem_object *obj); +void msm_gem_put_pages(struct drm_gem_object *obj);  void msm_gem_put_iova(struct drm_gem_object *obj, int id);  int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,  		struct drm_mode_create_dumb *args); -int msm_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, -		uint32_t handle);  int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,  		uint32_t handle, uint64_t *offset); +struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj); +void *msm_gem_prime_vmap(struct drm_gem_object *obj); +void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); +struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev, +		size_t size, struct sg_table *sg); +int msm_gem_prime_pin(struct drm_gem_object *obj); +void msm_gem_prime_unpin(struct drm_gem_object *obj);  void *msm_gem_vaddr_locked(struct drm_gem_object *obj);  void *msm_gem_vaddr(struct drm_gem_object *obj); -int msm_gem_queue_inactive_work(struct drm_gem_object *obj, -		struct work_struct *work); +int msm_gem_queue_inactive_cb(struct drm_gem_object *obj, +		struct msm_fence_cb *cb);  void msm_gem_move_to_active(struct drm_gem_object *obj, -		struct msm_gpu *gpu, uint32_t fence); +		struct msm_gpu *gpu, bool write, uint32_t fence);  void msm_gem_move_to_inactive(struct drm_gem_object *obj);  int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,  		struct timespec *timeout); @@ -163,6 +188,8 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,  		uint32_t size, uint32_t flags, uint32_t *handle);  struct drm_gem_object *msm_gem_new(struct drm_device *dev,  		uint32_t size, uint32_t flags); +struct drm_gem_object *msm_gem_import(struct drm_device *dev, +		uint32_t size, struct sg_table *sgt);  struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane);  const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb); @@ -173,7 +200,9 @@ struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,  struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev); -int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder); +struct hdmi; +struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder); +irqreturn_t hdmi_irq(int irq, void *dev_id);  void __init hdmi_register(void);  void __exit hdmi_unregister(void); @@ -181,6 +210,15 @@ void __exit hdmi_unregister(void);  void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m);  void msm_gem_describe_objects(struct list_head *list, struct seq_file *m);  void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m); +int msm_debugfs_late_init(struct drm_device *dev); +int msm_rd_debugfs_init(struct drm_minor *minor); +void msm_rd_debugfs_cleanup(struct drm_minor *minor); +void msm_rd_dump_submit(struct msm_gem_submit *submit); +int msm_perf_debugfs_init(struct drm_minor *minor); +void msm_perf_debugfs_cleanup(struct drm_minor *minor); +#else +static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; } +static inline void msm_rd_dump_submit(struct msm_gem_submit *submit) {}  #endif  void __iomem *msm_ioremap(struct platform_device *pdev, const char *name, @@ -191,6 +229,12 @@ u32 msm_readl(const void __iomem *addr);  #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)  #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) +static inline bool fence_completed(struct drm_device *dev, uint32_t fence) +{ +	struct msm_drm_private *priv = dev->dev_private; +	return priv->completed_fence >= fence; +} +  static inline int align_pitch(int width, int bpp)  {  	int bytespp = (bpp + 7) / 8; diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index 0286c0eeb10..81bafdf19ab 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -16,6 +16,7 @@   */  #include "msm_drv.h" +#include "msm_kms.h"  #include "drm_crtc.h"  #include "drm_crtc_helper.h" diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index 6c6d7d4c9b4..5107fc4826b 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -59,14 +59,11 @@ static int msm_fbdev_create(struct drm_fb_helper *helper,  	struct drm_framebuffer *fb = NULL;  	struct fb_info *fbi = NULL;  	struct drm_mode_fb_cmd2 mode_cmd = {0}; -	dma_addr_t paddr; +	uint32_t paddr;  	int ret, size; -	/* only doing ARGB32 since this is what is needed to alpha-blend -	 * with video overlays: -	 */  	sizes->surface_bpp = 32; -	sizes->surface_depth = 32; +	sizes->surface_depth = 24;  	DBG("create fbdev: %dx%d@%d (%dx%d)", sizes->surface_width,  			sizes->surface_height, sizes->surface_bpp, diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 6b5a6c8c765..690d7e7b6d1 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -17,11 +17,50 @@  #include <linux/spinlock.h>  #include <linux/shmem_fs.h> +#include <linux/dma-buf.h>  #include "msm_drv.h"  #include "msm_gem.h"  #include "msm_gpu.h" +#include "msm_mmu.h" +static dma_addr_t physaddr(struct drm_gem_object *obj) +{ +	struct msm_gem_object *msm_obj = to_msm_bo(obj); +	struct msm_drm_private *priv = obj->dev->dev_private; +	return (((dma_addr_t)msm_obj->vram_node->start) << PAGE_SHIFT) + +			priv->vram.paddr; +} + +/* allocate pages from VRAM carveout, used when no IOMMU: */ +static struct page **get_pages_vram(struct drm_gem_object *obj, +		int npages) +{ +	struct msm_gem_object *msm_obj = to_msm_bo(obj); +	struct msm_drm_private *priv = obj->dev->dev_private; +	dma_addr_t paddr; +	struct page **p; +	int ret, i; + +	p = drm_malloc_ab(npages, sizeof(struct page *)); +	if (!p) +		return ERR_PTR(-ENOMEM); + +	ret = drm_mm_insert_node(&priv->vram.mm, msm_obj->vram_node, +			npages, 0, DRM_MM_SEARCH_DEFAULT); +	if (ret) { +		drm_free_large(p); +		return ERR_PTR(ret); +	} + +	paddr = physaddr(obj); +	for (i = 0; i < npages; i++) { +		p[i] = phys_to_page(paddr); +		paddr += PAGE_SIZE; +	} + +	return p; +}  /* called with dev->struct_mutex held */  static struct page **get_pages(struct drm_gem_object *obj) @@ -30,9 +69,14 @@ static struct page **get_pages(struct drm_gem_object *obj)  	if (!msm_obj->pages) {  		struct drm_device *dev = obj->dev; -		struct page **p = drm_gem_get_pages(obj, 0); +		struct page **p;  		int npages = obj->size >> PAGE_SHIFT; +		if (iommu_present(&platform_bus_type)) +			p = drm_gem_get_pages(obj, 0); +		else +			p = get_pages_vram(obj, npages); +  		if (IS_ERR(p)) {  			dev_err(dev->dev, "could not get pages: %ld\n",  					PTR_ERR(p)); @@ -40,9 +84,9 @@ static struct page **get_pages(struct drm_gem_object *obj)  		}  		msm_obj->sgt = drm_prime_pages_to_sg(p, npages); -		if (!msm_obj->sgt) { +		if (IS_ERR(msm_obj->sgt)) {  			dev_err(dev->dev, "failed to allocate sgt\n"); -			return ERR_PTR(-ENOMEM); +			return ERR_CAST(msm_obj->sgt);  		}  		msm_obj->pages = p; @@ -72,11 +116,32 @@ static void put_pages(struct drm_gem_object *obj)  		sg_free_table(msm_obj->sgt);  		kfree(msm_obj->sgt); -		drm_gem_put_pages(obj, msm_obj->pages, true, false); +		if (iommu_present(&platform_bus_type)) +			drm_gem_put_pages(obj, msm_obj->pages, true, false); +		else { +			drm_mm_remove_node(msm_obj->vram_node); +			drm_free_large(msm_obj->pages); +		} +  		msm_obj->pages = NULL;  	}  } +struct page **msm_gem_get_pages(struct drm_gem_object *obj) +{ +	struct drm_device *dev = obj->dev; +	struct page **p; +	mutex_lock(&dev->struct_mutex); +	p = get_pages(obj); +	mutex_unlock(&dev->struct_mutex); +	return p; +} + +void msm_gem_put_pages(struct drm_gem_object *obj) +{ +	/* when we start tracking the pin count, then do something here */ +} +  int msm_gem_mmap_obj(struct drm_gem_object *obj,  		struct vm_area_struct *vma)  { @@ -122,7 +187,6 @@ int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma)  int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)  {  	struct drm_gem_object *obj = vma->vm_private_data; -	struct msm_gem_object *msm_obj = to_msm_bo(obj);  	struct drm_device *dev = obj->dev;  	struct page **pages;  	unsigned long pfn; @@ -147,7 +211,7 @@ int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)  	pgoff = ((unsigned long)vmf->virtual_address -  			vma->vm_start) >> PAGE_SHIFT; -	pfn = page_to_pfn(msm_obj->pages[pgoff]); +	pfn = page_to_pfn(pages[pgoff]);  	VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,  			pfn, pfn << PAGE_SHIFT); @@ -159,10 +223,14 @@ out_unlock:  out:  	switch (ret) {  	case -EAGAIN: -		set_need_resched();  	case 0:  	case -ERESTARTSYS:  	case -EINTR: +	case -EBUSY: +		/* +		 * EBUSY is ok: this just means that another thread +		 * already did the job. +		 */  		return VM_FAULT_NOPAGE;  	case -ENOMEM:  		return VM_FAULT_OOM; @@ -199,67 +267,6 @@ uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj)  	return offset;  } -/* helpers for dealing w/ iommu: */ -static int map_range(struct iommu_domain *domain, unsigned int iova, -		struct sg_table *sgt, unsigned int len, int prot) -{ -	struct scatterlist *sg; -	unsigned int da = iova; -	unsigned int i, j; -	int ret; - -	if (!domain || !sgt) -		return -EINVAL; - -	for_each_sg(sgt->sgl, sg, sgt->nents, i) { -		u32 pa = sg_phys(sg) - sg->offset; -		size_t bytes = sg->length + sg->offset; - -		VERB("map[%d]: %08x %08x(%x)", i, iova, pa, bytes); - -		ret = iommu_map(domain, da, pa, bytes, prot); -		if (ret) -			goto fail; - -		da += bytes; -	} - -	return 0; - -fail: -	da = iova; - -	for_each_sg(sgt->sgl, sg, i, j) { -		size_t bytes = sg->length + sg->offset; -		iommu_unmap(domain, da, bytes); -		da += bytes; -	} -	return ret; -} - -static void unmap_range(struct iommu_domain *domain, unsigned int iova, -		struct sg_table *sgt, unsigned int len) -{ -	struct scatterlist *sg; -	unsigned int da = iova; -	int i; - -	for_each_sg(sgt->sgl, sg, sgt->nents, i) { -		size_t bytes = sg->length + sg->offset; -		size_t unmapped; - -		unmapped = iommu_unmap(domain, da, bytes); -		if (unmapped < bytes) -			break; - -		VERB("unmap[%d]: %08x(%x)", i, iova, bytes); - -		BUG_ON(!IS_ALIGNED(bytes, PAGE_SIZE)); - -		da += bytes; -	} -} -  /* should be called under struct_mutex.. although it can be called   * from atomic context without struct_mutex to acquire an extra   * iova ref if you know one is already held. @@ -271,19 +278,30 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id,  		uint32_t *iova)  {  	struct msm_gem_object *msm_obj = to_msm_bo(obj); +	struct drm_device *dev = obj->dev;  	int ret = 0;  	if (!msm_obj->domain[id].iova) {  		struct msm_drm_private *priv = obj->dev->dev_private; -		uint32_t offset = (uint32_t)mmap_offset(obj); -		struct page **pages; -		pages = get_pages(obj); +		struct msm_mmu *mmu = priv->mmus[id]; +		struct page **pages = get_pages(obj); + +		if (!mmu) { +			dev_err(dev->dev, "null MMU pointer\n"); +			return -EINVAL; +		} +  		if (IS_ERR(pages))  			return PTR_ERR(pages); -		// XXX ideally we would not map buffers writable when not needed... -		ret = map_range(priv->iommus[id], offset, msm_obj->sgt, -				obj->size, IOMMU_READ | IOMMU_WRITE); -		msm_obj->domain[id].iova = offset; + +		if (iommu_present(&platform_bus_type)) { +			uint32_t offset = (uint32_t)mmap_offset(obj); +			ret = mmu->funcs->map(mmu, offset, msm_obj->sgt, +					obj->size, IOMMU_READ | IOMMU_WRITE); +			msm_obj->domain[id].iova = offset; +		} else { +			msm_obj->domain[id].iova = physaddr(obj); +		}  	}  	if (!ret) @@ -294,7 +312,17 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id,  int msm_gem_get_iova(struct drm_gem_object *obj, int id, uint32_t *iova)  { +	struct msm_gem_object *msm_obj = to_msm_bo(obj);  	int ret; + +	/* this is safe right now because we don't unmap until the +	 * bo is deleted: +	 */ +	if (msm_obj->domain[id].iova) { +		*iova = msm_obj->domain[id].iova; +		return 0; +	} +  	mutex_lock(&obj->dev->struct_mutex);  	ret = msm_gem_get_iova_locked(obj, id, iova);  	mutex_unlock(&obj->dev->struct_mutex); @@ -320,13 +348,6 @@ int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,  			MSM_BO_SCANOUT | MSM_BO_WC, &args->handle);  } -int msm_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, -		uint32_t handle) -{ -	/* No special work needed, drop the reference and see what falls out */ -	return drm_gem_handle_delete(file, handle); -} -  int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,  		uint32_t handle, uint64_t *offset)  { @@ -371,8 +392,11 @@ void *msm_gem_vaddr(struct drm_gem_object *obj)  	return ret;  } -int msm_gem_queue_inactive_work(struct drm_gem_object *obj, -		struct work_struct *work) +/* setup callback for when bo is no longer busy.. + * TODO probably want to differentiate read vs write.. + */ +int msm_gem_queue_inactive_cb(struct drm_gem_object *obj, +		struct msm_fence_cb *cb)  {  	struct drm_device *dev = obj->dev;  	struct msm_drm_private *priv = dev->dev_private; @@ -380,12 +404,13 @@ int msm_gem_queue_inactive_work(struct drm_gem_object *obj,  	int ret = 0;  	mutex_lock(&dev->struct_mutex); -	if (!list_empty(&work->entry)) { +	if (!list_empty(&cb->work.entry)) {  		ret = -EINVAL;  	} else if (is_active(msm_obj)) { -		list_add_tail(&work->entry, &msm_obj->inactive_work); +		cb->fence = max(msm_obj->read_fence, msm_obj->write_fence); +		list_add_tail(&cb->work.entry, &priv->fence_cbs);  	} else { -		queue_work(priv->wq, work); +		queue_work(priv->wq, &cb->work);  	}  	mutex_unlock(&dev->struct_mutex); @@ -393,11 +418,14 @@ int msm_gem_queue_inactive_work(struct drm_gem_object *obj,  }  void msm_gem_move_to_active(struct drm_gem_object *obj, -		struct msm_gpu *gpu, uint32_t fence) +		struct msm_gpu *gpu, bool write, uint32_t fence)  {  	struct msm_gem_object *msm_obj = to_msm_bo(obj);  	msm_obj->gpu = gpu; -	msm_obj->fence = fence; +	if (write) +		msm_obj->write_fence = fence; +	else +		msm_obj->read_fence = fence;  	list_del_init(&msm_obj->mm_list);  	list_add_tail(&msm_obj->mm_list, &gpu->active_list);  } @@ -411,19 +439,10 @@ void msm_gem_move_to_inactive(struct drm_gem_object *obj)  	WARN_ON(!mutex_is_locked(&dev->struct_mutex));  	msm_obj->gpu = NULL; -	msm_obj->fence = 0; +	msm_obj->read_fence = 0; +	msm_obj->write_fence = 0;  	list_del_init(&msm_obj->mm_list);  	list_add_tail(&msm_obj->mm_list, &priv->inactive_list); - -	while (!list_empty(&msm_obj->inactive_work)) { -		struct work_struct *work; - -		work = list_first_entry(&msm_obj->inactive_work, -				struct work_struct, entry); - -		list_del_init(&work->entry); -		queue_work(priv->wq, work); -	}  }  int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, @@ -433,8 +452,18 @@ int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,  	struct msm_gem_object *msm_obj = to_msm_bo(obj);  	int ret = 0; -	if (is_active(msm_obj) && !(op & MSM_PREP_NOSYNC)) -		ret = msm_wait_fence_interruptable(dev, msm_obj->fence, timeout); +	if (is_active(msm_obj)) { +		uint32_t fence = 0; + +		if (op & MSM_PREP_READ) +			fence = msm_obj->write_fence; +		if (op & MSM_PREP_WRITE) +			fence = max(fence, msm_obj->read_fence); +		if (op & MSM_PREP_NOSYNC) +			timeout = NULL; + +		ret = msm_wait_fence_interruptable(dev, fence, timeout); +	}  	/* TODO cache maintenance */ @@ -455,9 +484,10 @@ void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m)  	uint64_t off = drm_vma_node_start(&obj->vma_node);  	WARN_ON(!mutex_is_locked(&dev->struct_mutex)); -	seq_printf(m, "%08x: %c(%d) %2d (%2d) %08llx %p %d\n", +	seq_printf(m, "%08x: %c(r=%u,w=%u) %2d (%2d) %08llx %p %d\n",  			msm_obj->flags, is_active(msm_obj) ? 'A' : 'I', -			msm_obj->fence, obj->name, obj->refcount.refcount.counter, +			msm_obj->read_fence, msm_obj->write_fence, +			obj->name, obj->refcount.refcount.counter,  			off, msm_obj->vaddr, obj->size);  } @@ -482,6 +512,7 @@ void msm_gem_describe_objects(struct list_head *list, struct seq_file *m)  void msm_gem_free_object(struct drm_gem_object *obj)  {  	struct drm_device *dev = obj->dev; +	struct msm_drm_private *priv = obj->dev->dev_private;  	struct msm_gem_object *msm_obj = to_msm_bo(obj);  	int id; @@ -493,20 +524,30 @@ void msm_gem_free_object(struct drm_gem_object *obj)  	list_del(&msm_obj->mm_list);  	for (id = 0; id < ARRAY_SIZE(msm_obj->domain); id++) { -		if (msm_obj->domain[id].iova) { -			struct msm_drm_private *priv = obj->dev->dev_private; +		struct msm_mmu *mmu = priv->mmus[id]; +		if (mmu && msm_obj->domain[id].iova) {  			uint32_t offset = (uint32_t)mmap_offset(obj); -			unmap_range(priv->iommus[id], offset, -					msm_obj->sgt, obj->size); +			mmu->funcs->unmap(mmu, offset, msm_obj->sgt, obj->size);  		}  	}  	drm_gem_free_mmap_offset(obj); -	if (msm_obj->vaddr) -		vunmap(msm_obj->vaddr); +	if (obj->import_attach) { +		if (msm_obj->vaddr) +			dma_buf_vunmap(obj->import_attach->dmabuf, msm_obj->vaddr); + +		/* Don't drop the pages for imported dmabuf, as they are not +		 * ours, just free the array we allocated: +		 */ +		if (msm_obj->pages) +			drm_free_large(msm_obj->pages); -	put_pages(obj); +	} else { +		if (msm_obj->vaddr) +			vunmap(msm_obj->vaddr); +		put_pages(obj); +	}  	if (msm_obj->resv == &msm_obj->_resv)  		reservation_object_fini(msm_obj->resv); @@ -542,17 +583,13 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,  	return ret;  } -struct drm_gem_object *msm_gem_new(struct drm_device *dev, -		uint32_t size, uint32_t flags) +static int msm_gem_new_impl(struct drm_device *dev, +		uint32_t size, uint32_t flags, +		struct drm_gem_object **obj)  {  	struct msm_drm_private *priv = dev->dev_private;  	struct msm_gem_object *msm_obj; -	struct drm_gem_object *obj = NULL; -	int ret; - -	WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - -	size = PAGE_ALIGN(size); +	unsigned sz;  	switch (flags & MSM_BO_CACHE_MASK) {  	case MSM_BO_UNCACHED: @@ -562,21 +599,19 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev,  	default:  		dev_err(dev->dev, "invalid cache flag: %x\n",  				(flags & MSM_BO_CACHE_MASK)); -		ret = -EINVAL; -		goto fail; +		return -EINVAL;  	} -	msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL); -	if (!msm_obj) { -		ret = -ENOMEM; -		goto fail; -	} +	sz = sizeof(*msm_obj); +	if (!iommu_present(&platform_bus_type)) +		sz += sizeof(struct drm_mm_node); -	obj = &msm_obj->base; +	msm_obj = kzalloc(sz, GFP_KERNEL); +	if (!msm_obj) +		return -ENOMEM; -	ret = drm_gem_object_init(dev, obj, size); -	if (ret) -		goto fail; +	if (!iommu_present(&platform_bus_type)) +		msm_obj->vram_node = (void *)&msm_obj[1];  	msm_obj->flags = flags; @@ -584,9 +619,79 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev,  	reservation_object_init(msm_obj->resv);  	INIT_LIST_HEAD(&msm_obj->submit_entry); -	INIT_LIST_HEAD(&msm_obj->inactive_work);  	list_add_tail(&msm_obj->mm_list, &priv->inactive_list); +	*obj = &msm_obj->base; + +	return 0; +} + +struct drm_gem_object *msm_gem_new(struct drm_device *dev, +		uint32_t size, uint32_t flags) +{ +	struct drm_gem_object *obj = NULL; +	int ret; + +	WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + +	size = PAGE_ALIGN(size); + +	ret = msm_gem_new_impl(dev, size, flags, &obj); +	if (ret) +		goto fail; + +	if (iommu_present(&platform_bus_type)) { +		ret = drm_gem_object_init(dev, obj, size); +		if (ret) +			goto fail; +	} else { +		drm_gem_private_object_init(dev, obj, size); +	} + +	return obj; + +fail: +	if (obj) +		drm_gem_object_unreference(obj); + +	return ERR_PTR(ret); +} + +struct drm_gem_object *msm_gem_import(struct drm_device *dev, +		uint32_t size, struct sg_table *sgt) +{ +	struct msm_gem_object *msm_obj; +	struct drm_gem_object *obj; +	int ret, npages; + +	/* if we don't have IOMMU, don't bother pretending we can import: */ +	if (!iommu_present(&platform_bus_type)) { +		dev_err(dev->dev, "cannot import without IOMMU\n"); +		return ERR_PTR(-EINVAL); +	} + +	size = PAGE_ALIGN(size); + +	ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj); +	if (ret) +		goto fail; + +	drm_gem_private_object_init(dev, obj, size); + +	npages = size / PAGE_SIZE; + +	msm_obj = to_msm_bo(obj); +	msm_obj->sgt = sgt; +	msm_obj->pages = drm_malloc_ab(npages, sizeof(struct page *)); +	if (!msm_obj->pages) { +		ret = -ENOMEM; +		goto fail; +	} + +	ret = drm_prime_sg_to_page_addr_arrays(sgt, msm_obj->pages, NULL, npages); +	if (ret) +		goto fail; +  	return obj;  fail: diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index d746f13d283..bfb052688f8 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -36,7 +36,7 @@ struct msm_gem_object {  	 */  	struct list_head mm_list;  	struct msm_gpu *gpu;     /* non-null if active */ -	uint32_t fence; +	uint32_t read_fence, write_fence;  	/* Transiently in the process of submit ioctl, objects associated  	 * with the submit are on submit->bo_list.. this only lasts for @@ -45,9 +45,6 @@ struct msm_gem_object {  	 */  	struct list_head submit_entry; -	/* work defered until bo is inactive: */ -	struct list_head inactive_work; -  	struct page **pages;  	struct sg_table *sgt;  	void *vaddr; @@ -60,6 +57,11 @@ struct msm_gem_object {  	/* normally (resv == &_resv) except for imported bo's */  	struct reservation_object *resv;  	struct reservation_object _resv; + +	/* For physically contiguous buffers.  Used when we don't have +	 * an IOMMU. +	 */ +	struct drm_mm_node *vram_node;  };  #define to_msm_bo(x) container_of(x, struct msm_gem_object, base) @@ -88,6 +90,7 @@ struct msm_gem_submit {  		uint32_t type;  		uint32_t size;  /* in dwords */  		uint32_t iova; +		uint32_t idx;   /* cmdstream buffer idx in bos[] */  	} cmd[MAX_CMDS];  	struct {  		uint32_t flags; diff --git a/drivers/gpu/drm/msm/msm_gem_prime.c b/drivers/gpu/drm/msm/msm_gem_prime.c new file mode 100644 index 00000000000..d48f9fc5129 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_gem_prime.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "msm_drv.h" +#include "msm_gem.h" + + +struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj) +{ +	struct msm_gem_object *msm_obj = to_msm_bo(obj); +	BUG_ON(!msm_obj->sgt);  /* should have already pinned! */ +	return msm_obj->sgt; +} + +void *msm_gem_prime_vmap(struct drm_gem_object *obj) +{ +	return msm_gem_vaddr(obj); +} + +void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) +{ +	/* TODO msm_gem_vunmap() */ +} + +struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev, +		size_t size, struct sg_table *sg) +{ +	return msm_gem_import(dev, size, sg); +} + +int msm_gem_prime_pin(struct drm_gem_object *obj) +{ +	if (!obj->import_attach) +		msm_gem_get_pages(obj); +	return 0; +} + +void msm_gem_prime_unpin(struct drm_gem_object *obj) +{ +	if (!obj->import_attach) +		msm_gem_put_pages(obj); +} diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 3e1ef3a00f6..cd0554f6831 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -23,7 +23,6 @@   * Cmdstream submission:   */ -#define BO_INVALID_FLAGS ~(MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)  /* make sure these don't conflict w/ MSM_SUBMIT_BO_x */  #define BO_VALID    0x8000  #define BO_LOCKED   0x4000 @@ -77,8 +76,8 @@ static int submit_lookup_objects(struct msm_gem_submit *submit,  			goto out_unlock;  		} -		if (submit_bo.flags & BO_INVALID_FLAGS) { -			DBG("invalid flags: %x", submit_bo.flags); +		if (submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) { +			DRM_ERROR("invalid flags: %x\n", submit_bo.flags);  			ret = -EINVAL;  			goto out_unlock;  		} @@ -92,7 +91,7 @@ static int submit_lookup_objects(struct msm_gem_submit *submit,  		 */  		obj = idr_find(&file->object_idr, submit_bo.handle);  		if (!obj) { -			DBG("invalid handle %u at index %u", submit_bo.handle, i); +			DRM_ERROR("invalid handle %u at index %u\n", submit_bo.handle, i);  			ret = -EINVAL;  			goto out_unlock;  		} @@ -100,7 +99,7 @@ static int submit_lookup_objects(struct msm_gem_submit *submit,  		msm_obj = to_msm_bo(obj);  		if (!list_empty(&msm_obj->submit_entry)) { -			DBG("handle %u at index %u already on submit list", +			DRM_ERROR("handle %u at index %u already on submit list\n",  					submit_bo.handle, i);  			ret = -EINVAL;  			goto out_unlock; @@ -163,7 +162,7 @@ retry:  		/* if locking succeeded, pin bo: */ -		ret = msm_gem_get_iova(&msm_obj->base, +		ret = msm_gem_get_iova_locked(&msm_obj->base,  				submit->gpu->id, &iova);  		/* this would break the logic in the fail path.. there is no @@ -216,8 +215,9 @@ static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,  		struct msm_gem_object **obj, uint32_t *iova, bool *valid)  {  	if (idx >= submit->nr_bos) { -		DBG("invalid buffer index: %u (out of %u)", idx, submit->nr_bos); -		return EINVAL; +		DRM_ERROR("invalid buffer index: %u (out of %u)\n", +				idx, submit->nr_bos); +		return -EINVAL;  	}  	if (obj) @@ -239,14 +239,14 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob  	int ret;  	if (offset % 4) { -		DBG("non-aligned cmdstream buffer: %u", offset); +		DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset);  		return -EINVAL;  	}  	/* For now, just map the entire thing.  Eventually we probably  	 * to do it page-by-page, w/ kmap() if not vmap()d..  	 */ -	ptr = msm_gem_vaddr(&obj->base); +	ptr = msm_gem_vaddr_locked(&obj->base);  	if (IS_ERR(ptr)) {  		ret = PTR_ERR(ptr); @@ -266,7 +266,7 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob  			return -EFAULT;  		if (submit_reloc.submit_offset % 4) { -			DBG("non-aligned reloc offset: %u", +			DRM_ERROR("non-aligned reloc offset: %u\n",  					submit_reloc.submit_offset);  			return -EINVAL;  		} @@ -276,7 +276,7 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob  		if ((off >= (obj->base.size / 4)) ||  				(off < last_offset)) { -			DBG("invalid offset %u at reloc %u", off, i); +			DRM_ERROR("invalid offset %u at reloc %u\n", off, i);  			return -EINVAL;  		} @@ -306,14 +306,12 @@ static void submit_cleanup(struct msm_gem_submit *submit, bool fail)  {  	unsigned i; -	mutex_lock(&submit->dev->struct_mutex);  	for (i = 0; i < submit->nr_bos; i++) {  		struct msm_gem_object *msm_obj = submit->bos[i].obj;  		submit_unlock_unpin_bo(submit, i);  		list_del_init(&msm_obj->submit_entry);  		drm_gem_object_unreference(&msm_obj->base);  	} -	mutex_unlock(&submit->dev->struct_mutex);  	ww_acquire_fini(&submit->ticket);  	kfree(submit); @@ -341,6 +339,8 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,  	if (args->nr_cmds > MAX_CMDS)  		return -EINVAL; +	mutex_lock(&dev->struct_mutex); +  	submit = submit_create(dev, gpu, args->nr_bos);  	if (!submit) {  		ret = -ENOMEM; @@ -368,20 +368,33 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,  			goto out;  		} +		/* validate input from userspace: */ +		switch (submit_cmd.type) { +		case MSM_SUBMIT_CMD_BUF: +		case MSM_SUBMIT_CMD_IB_TARGET_BUF: +		case MSM_SUBMIT_CMD_CTX_RESTORE_BUF: +			break; +		default: +			DRM_ERROR("invalid type: %08x\n", submit_cmd.type); +			ret = -EINVAL; +			goto out; +		} +  		ret = submit_bo(submit, submit_cmd.submit_idx,  				&msm_obj, &iova, NULL);  		if (ret)  			goto out;  		if (submit_cmd.size % 4) { -			DBG("non-aligned cmdstream buffer size: %u", +			DRM_ERROR("non-aligned cmdstream buffer size: %u\n",  					submit_cmd.size);  			ret = -EINVAL;  			goto out;  		} -		if (submit_cmd.size >= msm_obj->base.size) { -			DBG("invalid cmdstream size: %u", submit_cmd.size); +		if ((submit_cmd.size + submit_cmd.submit_offset) >= +				msm_obj->base.size) { +			DRM_ERROR("invalid cmdstream size: %u\n", submit_cmd.size);  			ret = -EINVAL;  			goto out;  		} @@ -389,6 +402,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,  		submit->cmd[i].type = submit_cmd.type;  		submit->cmd[i].size = submit_cmd.size / 4;  		submit->cmd[i].iova = iova + submit_cmd.submit_offset; +		submit->cmd[i].idx  = submit_cmd.submit_idx;  		if (submit->valid)  			continue; @@ -408,5 +422,6 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,  out:  	if (submit)  		submit_cleanup(submit, !!ret); +	mutex_unlock(&dev->struct_mutex);  	return ret;  } diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index e1e1ec9321f..c6322197db8 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -17,6 +17,7 @@  #include "msm_gpu.h"  #include "msm_gem.h" +#include "msm_mmu.h"  /* @@ -25,19 +26,10 @@  #ifdef CONFIG_MSM_BUS_SCALING  #include <mach/board.h> -#include <mach/kgsl.h> -static void bs_init(struct msm_gpu *gpu, struct platform_device *pdev) +static void bs_init(struct msm_gpu *gpu)  { -	struct drm_device *dev = gpu->dev; -	struct kgsl_device_platform_data *pdata = pdev->dev.platform_data; - -	if (!pdev) { -		dev_err(dev->dev, "could not find dtv pdata\n"); -		return; -	} - -	if (pdata->bus_scale_table) { -		gpu->bsc = msm_bus_scale_register_client(pdata->bus_scale_table); +	if (gpu->bus_scale_table) { +		gpu->bsc = msm_bus_scale_register_client(gpu->bus_scale_table);  		DBG("bus scale client: %08x", gpu->bsc);  	}  } @@ -58,7 +50,7 @@ static void bs_set(struct msm_gpu *gpu, int idx)  	}  }  #else -static void bs_init(struct msm_gpu *gpu, struct platform_device *pdev) {} +static void bs_init(struct msm_gpu *gpu) {}  static void bs_fini(struct msm_gpu *gpu) {}  static void bs_set(struct msm_gpu *gpu, int idx) {}  #endif @@ -162,9 +154,18 @@ static int disable_axi(struct msm_gpu *gpu)  int msm_gpu_pm_resume(struct msm_gpu *gpu)  { +	struct drm_device *dev = gpu->dev;  	int ret; -	DBG("%s", gpu->name); +	DBG("%s: active_cnt=%d", gpu->name, gpu->active_cnt); + +	WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + +	if (gpu->active_cnt++ > 0) +		return 0; + +	if (WARN_ON(gpu->active_cnt <= 0)) +		return -EINVAL;  	ret = enable_pwrrail(gpu);  	if (ret) @@ -183,9 +184,18 @@ int msm_gpu_pm_resume(struct msm_gpu *gpu)  int msm_gpu_pm_suspend(struct msm_gpu *gpu)  { +	struct drm_device *dev = gpu->dev;  	int ret; -	DBG("%s", gpu->name); +	DBG("%s: active_cnt=%d", gpu->name, gpu->active_cnt); + +	WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + +	if (--gpu->active_cnt > 0) +		return 0; + +	if (WARN_ON(gpu->active_cnt < 0)) +		return -EINVAL;  	ret = disable_axi(gpu);  	if (ret) @@ -203,6 +213,55 @@ int msm_gpu_pm_suspend(struct msm_gpu *gpu)  }  /* + * Inactivity detection (for suspend): + */ + +static void inactive_worker(struct work_struct *work) +{ +	struct msm_gpu *gpu = container_of(work, struct msm_gpu, inactive_work); +	struct drm_device *dev = gpu->dev; + +	if (gpu->inactive) +		return; + +	DBG("%s: inactive!\n", gpu->name); +	mutex_lock(&dev->struct_mutex); +	if (!(msm_gpu_active(gpu) || gpu->inactive)) { +		disable_axi(gpu); +		disable_clk(gpu); +		gpu->inactive = true; +	} +	mutex_unlock(&dev->struct_mutex); +} + +static void inactive_handler(unsigned long data) +{ +	struct msm_gpu *gpu = (struct msm_gpu *)data; +	struct msm_drm_private *priv = gpu->dev->dev_private; + +	queue_work(priv->wq, &gpu->inactive_work); +} + +/* cancel inactive timer and make sure we are awake: */ +static void inactive_cancel(struct msm_gpu *gpu) +{ +	DBG("%s", gpu->name); +	del_timer(&gpu->inactive_timer); +	if (gpu->inactive) { +		enable_clk(gpu); +		enable_axi(gpu); +		gpu->inactive = false; +	} +} + +static void inactive_start(struct msm_gpu *gpu) +{ +	DBG("%s", gpu->name); +	mod_timer(&gpu->inactive_timer, +			round_jiffies_up(jiffies + DRM_MSM_INACTIVE_JIFFIES)); +} + +/*   * Hangcheck detection for locked gpu:   */ @@ -214,7 +273,10 @@ static void recover_worker(struct work_struct *work)  	dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);  	mutex_lock(&dev->struct_mutex); -	gpu->funcs->recover(gpu); +	if (msm_gpu_active(gpu)) { +		inactive_cancel(gpu); +		gpu->funcs->recover(gpu); +	}  	mutex_unlock(&dev->struct_mutex);  	msm_gpu_retire(gpu); @@ -230,6 +292,8 @@ static void hangcheck_timer_reset(struct msm_gpu *gpu)  static void hangcheck_handler(unsigned long data)  {  	struct msm_gpu *gpu = (struct msm_gpu *)data; +	struct drm_device *dev = gpu->dev; +	struct msm_drm_private *priv = dev->dev_private;  	uint32_t fence = gpu->funcs->last_fence(gpu);  	if (fence != gpu->hangcheck_fence) { @@ -237,14 +301,117 @@ static void hangcheck_handler(unsigned long data)  		gpu->hangcheck_fence = fence;  	} else if (fence < gpu->submitted_fence) {  		/* no progress and not done.. hung! */ -		struct msm_drm_private *priv = gpu->dev->dev_private;  		gpu->hangcheck_fence = fence; +		dev_err(dev->dev, "%s: hangcheck detected gpu lockup!\n", +				gpu->name); +		dev_err(dev->dev, "%s:     completed fence: %u\n", +				gpu->name, fence); +		dev_err(dev->dev, "%s:     submitted fence: %u\n", +				gpu->name, gpu->submitted_fence);  		queue_work(priv->wq, &gpu->recover_work);  	}  	/* if still more pending work, reset the hangcheck timer: */  	if (gpu->submitted_fence > gpu->hangcheck_fence)  		hangcheck_timer_reset(gpu); + +	/* workaround for missing irq: */ +	queue_work(priv->wq, &gpu->retire_work); +} + +/* + * Performance Counters: + */ + +/* called under perf_lock */ +static int update_hw_cntrs(struct msm_gpu *gpu, uint32_t ncntrs, uint32_t *cntrs) +{ +	uint32_t current_cntrs[ARRAY_SIZE(gpu->last_cntrs)]; +	int i, n = min(ncntrs, gpu->num_perfcntrs); + +	/* read current values: */ +	for (i = 0; i < gpu->num_perfcntrs; i++) +		current_cntrs[i] = gpu_read(gpu, gpu->perfcntrs[i].sample_reg); + +	/* update cntrs: */ +	for (i = 0; i < n; i++) +		cntrs[i] = current_cntrs[i] - gpu->last_cntrs[i]; + +	/* save current values: */ +	for (i = 0; i < gpu->num_perfcntrs; i++) +		gpu->last_cntrs[i] = current_cntrs[i]; + +	return n; +} + +static void update_sw_cntrs(struct msm_gpu *gpu) +{ +	ktime_t time; +	uint32_t elapsed; +	unsigned long flags; + +	spin_lock_irqsave(&gpu->perf_lock, flags); +	if (!gpu->perfcntr_active) +		goto out; + +	time = ktime_get(); +	elapsed = ktime_to_us(ktime_sub(time, gpu->last_sample.time)); + +	gpu->totaltime += elapsed; +	if (gpu->last_sample.active) +		gpu->activetime += elapsed; + +	gpu->last_sample.active = msm_gpu_active(gpu); +	gpu->last_sample.time = time; + +out: +	spin_unlock_irqrestore(&gpu->perf_lock, flags); +} + +void msm_gpu_perfcntr_start(struct msm_gpu *gpu) +{ +	unsigned long flags; + +	spin_lock_irqsave(&gpu->perf_lock, flags); +	/* we could dynamically enable/disable perfcntr registers too.. */ +	gpu->last_sample.active = msm_gpu_active(gpu); +	gpu->last_sample.time = ktime_get(); +	gpu->activetime = gpu->totaltime = 0; +	gpu->perfcntr_active = true; +	update_hw_cntrs(gpu, 0, NULL); +	spin_unlock_irqrestore(&gpu->perf_lock, flags); +} + +void msm_gpu_perfcntr_stop(struct msm_gpu *gpu) +{ +	gpu->perfcntr_active = false; +} + +/* returns -errno or # of cntrs sampled */ +int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime, +		uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs) +{ +	unsigned long flags; +	int ret; + +	spin_lock_irqsave(&gpu->perf_lock, flags); + +	if (!gpu->perfcntr_active) { +		ret = -EINVAL; +		goto out; +	} + +	*activetime = gpu->activetime; +	*totaltime = gpu->totaltime; + +	gpu->activetime = gpu->totaltime = 0; + +	ret = update_hw_cntrs(gpu, ncntrs, cntrs); + +out: +	spin_unlock_irqrestore(&gpu->perf_lock, flags); + +	return ret;  }  /* @@ -257,6 +424,8 @@ static void retire_worker(struct work_struct *work)  	struct drm_device *dev = gpu->dev;  	uint32_t fence = gpu->funcs->last_fence(gpu); +	msm_update_fence(gpu->dev, fence); +  	mutex_lock(&dev->struct_mutex);  	while (!list_empty(&gpu->active_list)) { @@ -265,7 +434,8 @@ static void retire_worker(struct work_struct *work)  		obj = list_first_entry(&gpu->active_list,  				struct msm_gem_object, mm_list); -		if (obj->fence <= fence) { +		if ((obj->read_fence <= fence) && +				(obj->write_fence <= fence)) {  			/* move to inactive: */  			msm_gem_move_to_inactive(&obj->base);  			msm_gem_put_iova(&obj->base, gpu->id); @@ -275,9 +445,10 @@ static void retire_worker(struct work_struct *work)  		}  	} -	msm_update_fence(gpu->dev, fence); -  	mutex_unlock(&dev->struct_mutex); + +	if (!msm_gpu_active(gpu)) +		inactive_start(gpu);  }  /* call from irq handler to schedule work to retire bo's */ @@ -285,6 +456,7 @@ void msm_gpu_retire(struct msm_gpu *gpu)  {  	struct msm_drm_private *priv = gpu->dev->dev_private;  	queue_work(priv->wq, &gpu->retire_work); +	update_sw_cntrs(gpu);  }  /* add bo's to gpu's ring, and kick gpu: */ @@ -295,12 +467,18 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,  	struct msm_drm_private *priv = dev->dev_private;  	int i, ret; -	mutex_lock(&dev->struct_mutex); -  	submit->fence = ++priv->next_fence;  	gpu->submitted_fence = submit->fence; +	inactive_cancel(gpu); + +	msm_rd_dump_submit(submit); + +	gpu->submitted_fence = submit->fence; + +	update_sw_cntrs(gpu); +  	ret = gpu->funcs->submit(gpu, submit, ctx);  	priv->lastctx = ctx; @@ -321,10 +499,13 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,  					submit->gpu->id, &iova);  		} -		msm_gem_move_to_active(&msm_obj->base, gpu, submit->fence); +		if (submit->bos[i].flags & MSM_SUBMIT_BO_READ) +			msm_gem_move_to_active(&msm_obj->base, gpu, false, submit->fence); + +		if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE) +			msm_gem_move_to_active(&msm_obj->base, gpu, true, submit->fence);  	}  	hangcheck_timer_reset(gpu); -	mutex_unlock(&dev->struct_mutex);  	return ret;  } @@ -347,19 +528,29 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,  		struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs,  		const char *name, const char *ioname, const char *irqname, int ringsz)  { +	struct iommu_domain *iommu;  	int i, ret; +	if (WARN_ON(gpu->num_perfcntrs > ARRAY_SIZE(gpu->last_cntrs))) +		gpu->num_perfcntrs = ARRAY_SIZE(gpu->last_cntrs); +  	gpu->dev = drm;  	gpu->funcs = funcs;  	gpu->name = name; +	gpu->inactive = true;  	INIT_LIST_HEAD(&gpu->active_list);  	INIT_WORK(&gpu->retire_work, retire_worker); +	INIT_WORK(&gpu->inactive_work, inactive_worker);  	INIT_WORK(&gpu->recover_work, recover_worker); +	setup_timer(&gpu->inactive_timer, inactive_handler, +			(unsigned long)gpu);  	setup_timer(&gpu->hangcheck_timer, hangcheck_handler,  			(unsigned long)gpu); +	spin_lock_init(&gpu->perf_lock); +  	BUG_ON(ARRAY_SIZE(clk_names) != ARRAY_SIZE(gpu->grp_clks));  	/* Map registers: */ @@ -412,13 +603,14 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,  	 * and have separate page tables per context.  For now, to keep things  	 * simple and to get something working, just use a single address space:  	 */ -	gpu->iommu = iommu_domain_alloc(&platform_bus_type); -	if (!gpu->iommu) { -		dev_err(drm->dev, "failed to allocate IOMMU\n"); -		ret = -ENOMEM; -		goto fail; +	iommu = iommu_domain_alloc(&platform_bus_type); +	if (iommu) { +		dev_info(drm->dev, "%s: using IOMMU\n", name); +		gpu->mmu = msm_iommu_new(drm, iommu); +	} else { +		dev_info(drm->dev, "%s: no IOMMU, fallback to VRAM carveout!\n", name);  	} -	gpu->id = msm_register_iommu(drm, gpu->iommu); +	gpu->id = msm_register_mmu(drm, gpu->mmu);  	/* Create ringbuffer: */  	gpu->rb = msm_ringbuffer_new(gpu, ringsz); @@ -436,7 +628,7 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,  		goto fail;  	} -	bs_init(gpu, pdev); +	bs_init(gpu);  	return 0; @@ -458,6 +650,6 @@ void msm_gpu_cleanup(struct msm_gpu *gpu)  		msm_ringbuffer_destroy(gpu->rb);  	} -	if (gpu->iommu) -		iommu_domain_free(gpu->iommu); +	if (gpu->mmu) +		gpu->mmu->funcs->destroy(gpu->mmu);  } diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 8cd829e520b..9b579b79284 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -25,6 +25,7 @@  #include "msm_ringbuffer.h"  struct msm_gem_submit; +struct msm_gpu_perfcntr;  /* So far, with hardware that I've seen to date, we can have:   *  + zero, one, or two z180 2d cores @@ -64,6 +65,18 @@ struct msm_gpu {  	struct drm_device *dev;  	const struct msm_gpu_funcs *funcs; +	/* performance counters (hw & sw): */ +	spinlock_t perf_lock; +	bool perfcntr_active; +	struct { +		bool active; +		ktime_t time; +	} last_sample; +	uint32_t totaltime, activetime;    /* sw counters */ +	uint32_t last_cntrs[5];            /* hw counters */ +	const struct msm_gpu_perfcntr *perfcntrs; +	uint32_t num_perfcntrs; +  	struct msm_ringbuffer *rb;  	uint32_t rb_iova; @@ -72,22 +85,35 @@ struct msm_gpu {  	uint32_t submitted_fence; +	/* is gpu powered/active? */ +	int active_cnt; +	bool inactive; +  	/* worker for handling active-list retiring: */  	struct work_struct retire_work;  	void __iomem *mmio;  	int irq; -	struct iommu_domain *iommu; +	struct msm_mmu *mmu;  	int id;  	/* Power Control: */  	struct regulator *gpu_reg, *gpu_cx;  	struct clk *ebi1_clk, *grp_clks[5];  	uint32_t fast_rate, slow_rate, bus_freq; + +#ifdef CONFIG_MSM_BUS_SCALING +	struct msm_bus_scale_pdata *bus_scale_table;  	uint32_t bsc; +#endif -	/* Hang Detction: */ +	/* Hang and Inactivity Detection: +	 */ +#define DRM_MSM_INACTIVE_PERIOD   66 /* in ms (roughly four frames) */ +#define DRM_MSM_INACTIVE_JIFFIES  msecs_to_jiffies(DRM_MSM_INACTIVE_PERIOD) +	struct timer_list inactive_timer; +	struct work_struct inactive_work;  #define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */  #define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)  	struct timer_list hangcheck_timer; @@ -95,6 +121,24 @@ struct msm_gpu {  	struct work_struct recover_work;  }; +static inline bool msm_gpu_active(struct msm_gpu *gpu) +{ +	return gpu->submitted_fence > gpu->funcs->last_fence(gpu); +} + +/* Perf-Counters: + * The select_reg and select_val are just there for the benefit of the child + * class that actually enables the perf counter..  but msm_gpu base class + * will handle sampling/displaying the counters. + */ + +struct msm_gpu_perfcntr { +	uint32_t select_reg; +	uint32_t sample_reg; +	uint32_t select_val; +	const char *name; +}; +  static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)  {  	msm_writel(data, gpu->mmio + (reg << 2)); @@ -108,6 +152,11 @@ static inline u32 gpu_read(struct msm_gpu *gpu, u32 reg)  int msm_gpu_pm_suspend(struct msm_gpu *gpu);  int msm_gpu_pm_resume(struct msm_gpu *gpu); +void msm_gpu_perfcntr_start(struct msm_gpu *gpu); +void msm_gpu_perfcntr_stop(struct msm_gpu *gpu); +int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime, +		uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs); +  void msm_gpu_retire(struct msm_gpu *gpu);  int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,  		struct msm_file_private *ctx); diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c new file mode 100644 index 00000000000..4b2ad9181ed --- /dev/null +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "msm_drv.h" +#include "msm_mmu.h" + +struct msm_iommu { +	struct msm_mmu base; +	struct iommu_domain *domain; +}; +#define to_msm_iommu(x) container_of(x, struct msm_iommu, base) + +static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev, +		unsigned long iova, int flags, void *arg) +{ +	DBG("*** fault: iova=%08lx, flags=%d", iova, flags); +	return -ENOSYS; +} + +static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt) +{ +	struct drm_device *dev = mmu->dev; +	struct msm_iommu *iommu = to_msm_iommu(mmu); +	int i, ret; + +	for (i = 0; i < cnt; i++) { +		struct device *msm_iommu_get_ctx(const char *ctx_name); +		struct device *ctx = msm_iommu_get_ctx(names[i]); +		if (IS_ERR_OR_NULL(ctx)) { +			dev_warn(dev->dev, "couldn't get %s context", names[i]); +			continue; +		} +		ret = iommu_attach_device(iommu->domain, ctx); +		if (ret) { +			dev_warn(dev->dev, "could not attach iommu to %s", names[i]); +			return ret; +		} +	} + +	return 0; +} + +static void msm_iommu_detach(struct msm_mmu *mmu, const char **names, int cnt) +{ +	struct msm_iommu *iommu = to_msm_iommu(mmu); +	int i; + +	for (i = 0; i < cnt; i++) { +		struct device *msm_iommu_get_ctx(const char *ctx_name); +		struct device *ctx = msm_iommu_get_ctx(names[i]); +		if (IS_ERR_OR_NULL(ctx)) +			continue; +		iommu_detach_device(iommu->domain, ctx); +	} +} + +static int msm_iommu_map(struct msm_mmu *mmu, uint32_t iova, +		struct sg_table *sgt, unsigned len, int prot) +{ +	struct msm_iommu *iommu = to_msm_iommu(mmu); +	struct iommu_domain *domain = iommu->domain; +	struct scatterlist *sg; +	unsigned int da = iova; +	unsigned int i, j; +	int ret; + +	if (!domain || !sgt) +		return -EINVAL; + +	for_each_sg(sgt->sgl, sg, sgt->nents, i) { +		u32 pa = sg_phys(sg) - sg->offset; +		size_t bytes = sg->length + sg->offset; + +		VERB("map[%d]: %08x %08x(%x)", i, iova, pa, bytes); + +		ret = iommu_map(domain, da, pa, bytes, prot); +		if (ret) +			goto fail; + +		da += bytes; +	} + +	return 0; + +fail: +	da = iova; + +	for_each_sg(sgt->sgl, sg, i, j) { +		size_t bytes = sg->length + sg->offset; +		iommu_unmap(domain, da, bytes); +		da += bytes; +	} +	return ret; +} + +static int msm_iommu_unmap(struct msm_mmu *mmu, uint32_t iova, +		struct sg_table *sgt, unsigned len) +{ +	struct msm_iommu *iommu = to_msm_iommu(mmu); +	struct iommu_domain *domain = iommu->domain; +	struct scatterlist *sg; +	unsigned int da = iova; +	int i; + +	for_each_sg(sgt->sgl, sg, sgt->nents, i) { +		size_t bytes = sg->length + sg->offset; +		size_t unmapped; + +		unmapped = iommu_unmap(domain, da, bytes); +		if (unmapped < bytes) +			return unmapped; + +		VERB("unmap[%d]: %08x(%x)", i, iova, bytes); + +		BUG_ON(!PAGE_ALIGNED(bytes)); + +		da += bytes; +	} + +	return 0; +} + +static void msm_iommu_destroy(struct msm_mmu *mmu) +{ +	struct msm_iommu *iommu = to_msm_iommu(mmu); +	iommu_domain_free(iommu->domain); +	kfree(iommu); +} + +static const struct msm_mmu_funcs funcs = { +		.attach = msm_iommu_attach, +		.detach = msm_iommu_detach, +		.map = msm_iommu_map, +		.unmap = msm_iommu_unmap, +		.destroy = msm_iommu_destroy, +}; + +struct msm_mmu *msm_iommu_new(struct drm_device *dev, struct iommu_domain *domain) +{ +	struct msm_iommu *iommu; + +	iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); +	if (!iommu) +		return ERR_PTR(-ENOMEM); + +	iommu->domain = domain; +	msm_mmu_init(&iommu->base, dev, &funcs); +	iommu_set_fault_handler(domain, msm_fault_handler, dev); + +	return &iommu->base; +} diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h new file mode 100644 index 00000000000..06437745bc2 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __MSM_KMS_H__ +#define __MSM_KMS_H__ + +#include <linux/clk.h> +#include <linux/regulator/consumer.h> + +#include "msm_drv.h" + +/* As there are different display controller blocks depending on the + * snapdragon version, the kms support is split out and the appropriate + * implementation is loaded at runtime.  The kms module is responsible + * for constructing the appropriate planes/crtcs/encoders/connectors. + */ +struct msm_kms_funcs { +	/* hw initialization: */ +	int (*hw_init)(struct msm_kms *kms); +	/* irq handling: */ +	void (*irq_preinstall)(struct msm_kms *kms); +	int (*irq_postinstall)(struct msm_kms *kms); +	void (*irq_uninstall)(struct msm_kms *kms); +	irqreturn_t (*irq)(struct msm_kms *kms); +	int (*enable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc); +	void (*disable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc); +	/* misc: */ +	const struct msm_format *(*get_format)(struct msm_kms *kms, uint32_t format); +	long (*round_pixclk)(struct msm_kms *kms, unsigned long rate, +			struct drm_encoder *encoder); +	/* cleanup: */ +	void (*preclose)(struct msm_kms *kms, struct drm_file *file); +	void (*destroy)(struct msm_kms *kms); +}; + +struct msm_kms { +	const struct msm_kms_funcs *funcs; + +	/* irq handling: */ +	bool in_irq; +	struct list_head irq_list;    /* list of mdp4_irq */ +	uint32_t vblank_mask;         /* irq bits set for userspace vblank */ +}; + +static inline void msm_kms_init(struct msm_kms *kms, +		const struct msm_kms_funcs *funcs) +{ +	kms->funcs = funcs; +} + +struct msm_kms *mdp4_kms_init(struct drm_device *dev); +struct msm_kms *mdp5_kms_init(struct drm_device *dev); + +#endif /* __MSM_KMS_H__ */ diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h new file mode 100644 index 00000000000..21da6d154f7 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_mmu.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __MSM_MMU_H__ +#define __MSM_MMU_H__ + +#include <linux/iommu.h> + +struct msm_mmu_funcs { +	int (*attach)(struct msm_mmu *mmu, const char **names, int cnt); +	void (*detach)(struct msm_mmu *mmu, const char **names, int cnt); +	int (*map)(struct msm_mmu *mmu, uint32_t iova, struct sg_table *sgt, +			unsigned len, int prot); +	int (*unmap)(struct msm_mmu *mmu, uint32_t iova, struct sg_table *sgt, +			unsigned len); +	void (*destroy)(struct msm_mmu *mmu); +}; + +struct msm_mmu { +	const struct msm_mmu_funcs *funcs; +	struct drm_device *dev; +}; + +static inline void msm_mmu_init(struct msm_mmu *mmu, struct drm_device *dev, +		const struct msm_mmu_funcs *funcs) +{ +	mmu->dev = dev; +	mmu->funcs = funcs; +} + +struct msm_mmu *msm_iommu_new(struct drm_device *dev, struct iommu_domain *domain); +struct msm_mmu *msm_gpummu_new(struct drm_device *dev, struct msm_gpu *gpu); + +#endif /* __MSM_MMU_H__ */ diff --git a/drivers/gpu/drm/msm/msm_perf.c b/drivers/gpu/drm/msm/msm_perf.c new file mode 100644 index 00000000000..830857c47c8 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_perf.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +/* For profiling, userspace can: + * + *   tail -f /sys/kernel/debug/dri/<minor>/gpu + * + * This will enable performance counters/profiling to track the busy time + * and any gpu specific performance counters that are supported. + */ + +#ifdef CONFIG_DEBUG_FS + +#include <linux/debugfs.h> + +#include "msm_drv.h" +#include "msm_gpu.h" + +struct msm_perf_state { +	struct drm_device *dev; + +	bool open; +	int cnt; +	struct mutex read_lock; + +	char buf[256]; +	int buftot, bufpos; + +	unsigned long next_jiffies; + +	struct dentry *ent; +	struct drm_info_node *node; +}; + +#define SAMPLE_TIME (HZ/4) + +/* wait for next sample time: */ +static int wait_sample(struct msm_perf_state *perf) +{ +	unsigned long start_jiffies = jiffies; + +	if (time_after(perf->next_jiffies, start_jiffies)) { +		unsigned long remaining_jiffies = +			perf->next_jiffies - start_jiffies; +		int ret = schedule_timeout_interruptible(remaining_jiffies); +		if (ret > 0) { +			/* interrupted */ +			return -ERESTARTSYS; +		} +	} +	perf->next_jiffies += SAMPLE_TIME; +	return 0; +} + +static int refill_buf(struct msm_perf_state *perf) +{ +	struct msm_drm_private *priv = perf->dev->dev_private; +	struct msm_gpu *gpu = priv->gpu; +	char *ptr = perf->buf; +	int rem = sizeof(perf->buf); +	int i, n; + +	if ((perf->cnt++ % 32) == 0) { +		/* Header line: */ +		n = snprintf(ptr, rem, "%%BUSY"); +		ptr += n; +		rem -= n; + +		for (i = 0; i < gpu->num_perfcntrs; i++) { +			const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i]; +			n = snprintf(ptr, rem, "\t%s", perfcntr->name); +			ptr += n; +			rem -= n; +		} +	} else { +		/* Sample line: */ +		uint32_t activetime = 0, totaltime = 0; +		uint32_t cntrs[5]; +		uint32_t val; +		int ret; + +		/* sleep until next sample time: */ +		ret = wait_sample(perf); +		if (ret) +			return ret; + +		ret = msm_gpu_perfcntr_sample(gpu, &activetime, &totaltime, +				ARRAY_SIZE(cntrs), cntrs); +		if (ret < 0) +			return ret; + +		val = totaltime ? 1000 * activetime / totaltime : 0; +		n = snprintf(ptr, rem, "%3d.%d%%", val / 10, val % 10); +		ptr += n; +		rem -= n; + +		for (i = 0; i < ret; i++) { +			/* cycle counters (I think).. convert to MHz.. */ +			val = cntrs[i] / 10000; +			n = snprintf(ptr, rem, "\t%5d.%02d", +					val / 100, val % 100); +			ptr += n; +			rem -= n; +		} +	} + +	n = snprintf(ptr, rem, "\n"); +	ptr += n; +	rem -= n; + +	perf->bufpos = 0; +	perf->buftot = ptr - perf->buf; + +	return 0; +} + +static ssize_t perf_read(struct file *file, char __user *buf, +		size_t sz, loff_t *ppos) +{ +	struct msm_perf_state *perf = file->private_data; +	int n = 0, ret; + +	mutex_lock(&perf->read_lock); + +	if (perf->bufpos >= perf->buftot) { +		ret = refill_buf(perf); +		if (ret) +			goto out; +	} + +	n = min((int)sz, perf->buftot - perf->bufpos); +	ret = copy_to_user(buf, &perf->buf[perf->bufpos], n); +	if (ret) +		goto out; + +	perf->bufpos += n; +	*ppos += n; + +out: +	mutex_unlock(&perf->read_lock); +	if (ret) +		return ret; +	return n; +} + +static int perf_open(struct inode *inode, struct file *file) +{ +	struct msm_perf_state *perf = inode->i_private; +	struct drm_device *dev = perf->dev; +	struct msm_drm_private *priv = dev->dev_private; +	struct msm_gpu *gpu = priv->gpu; +	int ret = 0; + +	mutex_lock(&dev->struct_mutex); + +	if (perf->open || !gpu) { +		ret = -EBUSY; +		goto out; +	} + +	file->private_data = perf; +	perf->open = true; +	perf->cnt = 0; +	perf->buftot = 0; +	perf->bufpos = 0; +	msm_gpu_perfcntr_start(gpu); +	perf->next_jiffies = jiffies + SAMPLE_TIME; + +out: +	mutex_unlock(&dev->struct_mutex); +	return ret; +} + +static int perf_release(struct inode *inode, struct file *file) +{ +	struct msm_perf_state *perf = inode->i_private; +	struct msm_drm_private *priv = perf->dev->dev_private; +	msm_gpu_perfcntr_stop(priv->gpu); +	perf->open = false; +	return 0; +} + + +static const struct file_operations perf_debugfs_fops = { +	.owner = THIS_MODULE, +	.open = perf_open, +	.read = perf_read, +	.llseek = no_llseek, +	.release = perf_release, +}; + +int msm_perf_debugfs_init(struct drm_minor *minor) +{ +	struct msm_drm_private *priv = minor->dev->dev_private; +	struct msm_perf_state *perf; + +	/* only create on first minor: */ +	if (priv->perf) +		return 0; + +	perf = kzalloc(sizeof(*perf), GFP_KERNEL); +	if (!perf) +		return -ENOMEM; + +	perf->dev = minor->dev; + +	mutex_init(&perf->read_lock); +	priv->perf = perf; + +	perf->node = kzalloc(sizeof(*perf->node), GFP_KERNEL); +	if (!perf->node) +		goto fail; + +	perf->ent = debugfs_create_file("perf", S_IFREG | S_IRUGO, +			minor->debugfs_root, perf, &perf_debugfs_fops); +	if (!perf->ent) { +		DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/perf\n", +				minor->debugfs_root->d_name.name); +		goto fail; +	} + +	perf->node->minor = minor; +	perf->node->dent  = perf->ent; +	perf->node->info_ent = NULL; + +	mutex_lock(&minor->debugfs_lock); +	list_add(&perf->node->list, &minor->debugfs_list); +	mutex_unlock(&minor->debugfs_lock); + +	return 0; + +fail: +	msm_perf_debugfs_cleanup(minor); +	return -1; +} + +void msm_perf_debugfs_cleanup(struct drm_minor *minor) +{ +	struct msm_drm_private *priv = minor->dev->dev_private; +	struct msm_perf_state *perf = priv->perf; + +	if (!perf) +		return; + +	priv->perf = NULL; + +	debugfs_remove(perf->ent); + +	if (perf->node) { +		mutex_lock(&minor->debugfs_lock); +		list_del(&perf->node->list); +		mutex_unlock(&minor->debugfs_lock); +		kfree(perf->node); +	} + +	mutex_destroy(&perf->read_lock); + +	kfree(perf); +} + +#endif diff --git a/drivers/gpu/drm/msm/msm_rd.c b/drivers/gpu/drm/msm/msm_rd.c new file mode 100644 index 00000000000..9a78c48817c --- /dev/null +++ b/drivers/gpu/drm/msm/msm_rd.c @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +/* For debugging crashes, userspace can: + * + *   tail -f /sys/kernel/debug/dri/<minor>/rd > logfile.rd + * + * To log the cmdstream in a format that is understood by freedreno/cffdump + * utility.  By comparing the last successfully completed fence #, to the + * cmdstream for the next fence, you can narrow down which process and submit + * caused the gpu crash/lockup. + * + * This bypasses drm_debugfs_create_files() mainly because we need to use + * our own fops for a bit more control.  In particular, we don't want to + * do anything if userspace doesn't have the debugfs file open. + */ + +#ifdef CONFIG_DEBUG_FS + +#include <linux/kfifo.h> +#include <linux/debugfs.h> +#include <linux/circ_buf.h> +#include <linux/wait.h> + +#include "msm_drv.h" +#include "msm_gpu.h" +#include "msm_gem.h" + +enum rd_sect_type { +	RD_NONE, +	RD_TEST,       /* ascii text */ +	RD_CMD,        /* ascii text */ +	RD_GPUADDR,    /* u32 gpuaddr, u32 size */ +	RD_CONTEXT,    /* raw dump */ +	RD_CMDSTREAM,  /* raw dump */ +	RD_CMDSTREAM_ADDR, /* gpu addr of cmdstream */ +	RD_PARAM,      /* u32 param_type, u32 param_val, u32 bitlen */ +	RD_FLUSH,      /* empty, clear previous params */ +	RD_PROGRAM,    /* shader program, raw dump */ +	RD_VERT_SHADER, +	RD_FRAG_SHADER, +	RD_BUFFER_CONTENTS, +	RD_GPU_ID, +}; + +#define BUF_SZ 512  /* should be power of 2 */ + +/* space used: */ +#define circ_count(circ) \ +	(CIRC_CNT((circ)->head, (circ)->tail, BUF_SZ)) +#define circ_count_to_end(circ) \ +	(CIRC_CNT_TO_END((circ)->head, (circ)->tail, BUF_SZ)) +/* space available: */ +#define circ_space(circ) \ +	(CIRC_SPACE((circ)->head, (circ)->tail, BUF_SZ)) +#define circ_space_to_end(circ) \ +	(CIRC_SPACE_TO_END((circ)->head, (circ)->tail, BUF_SZ)) + +struct msm_rd_state { +	struct drm_device *dev; + +	bool open; + +	struct dentry *ent; +	struct drm_info_node *node; + +	/* current submit to read out: */ +	struct msm_gem_submit *submit; + +	/* fifo access is synchronized on the producer side by +	 * struct_mutex held by submit code (otherwise we could +	 * end up w/ cmds logged in different order than they +	 * were executed).  And read_lock synchronizes the reads +	 */ +	struct mutex read_lock; + +	wait_queue_head_t fifo_event; +	struct circ_buf fifo; + +	char buf[BUF_SZ]; +}; + +static void rd_write(struct msm_rd_state *rd, const void *buf, int sz) +{ +	struct circ_buf *fifo = &rd->fifo; +	const char *ptr = buf; + +	while (sz > 0) { +		char *fptr = &fifo->buf[fifo->head]; +		int n; + +		wait_event(rd->fifo_event, circ_space(&rd->fifo) > 0); + +		n = min(sz, circ_space_to_end(&rd->fifo)); +		memcpy(fptr, ptr, n); + +		fifo->head = (fifo->head + n) & (BUF_SZ - 1); +		sz  -= n; +		ptr += n; + +		wake_up_all(&rd->fifo_event); +	} +} + +static void rd_write_section(struct msm_rd_state *rd, +		enum rd_sect_type type, const void *buf, int sz) +{ +	rd_write(rd, &type, 4); +	rd_write(rd, &sz, 4); +	rd_write(rd, buf, sz); +} + +static ssize_t rd_read(struct file *file, char __user *buf, +		size_t sz, loff_t *ppos) +{ +	struct msm_rd_state *rd = file->private_data; +	struct circ_buf *fifo = &rd->fifo; +	const char *fptr = &fifo->buf[fifo->tail]; +	int n = 0, ret = 0; + +	mutex_lock(&rd->read_lock); + +	ret = wait_event_interruptible(rd->fifo_event, +			circ_count(&rd->fifo) > 0); +	if (ret) +		goto out; + +	n = min_t(int, sz, circ_count_to_end(&rd->fifo)); +	ret = copy_to_user(buf, fptr, n); +	if (ret) +		goto out; + +	fifo->tail = (fifo->tail + n) & (BUF_SZ - 1); +	*ppos += n; + +	wake_up_all(&rd->fifo_event); + +out: +	mutex_unlock(&rd->read_lock); +	if (ret) +		return ret; +	return n; +} + +static int rd_open(struct inode *inode, struct file *file) +{ +	struct msm_rd_state *rd = inode->i_private; +	struct drm_device *dev = rd->dev; +	struct msm_drm_private *priv = dev->dev_private; +	struct msm_gpu *gpu = priv->gpu; +	uint64_t val; +	uint32_t gpu_id; +	int ret = 0; + +	mutex_lock(&dev->struct_mutex); + +	if (rd->open || !gpu) { +		ret = -EBUSY; +		goto out; +	} + +	file->private_data = rd; +	rd->open = true; + +	/* the parsing tools need to know gpu-id to know which +	 * register database to load. +	 */ +	gpu->funcs->get_param(gpu, MSM_PARAM_GPU_ID, &val); +	gpu_id = val; + +	rd_write_section(rd, RD_GPU_ID, &gpu_id, sizeof(gpu_id)); + +out: +	mutex_unlock(&dev->struct_mutex); +	return ret; +} + +static int rd_release(struct inode *inode, struct file *file) +{ +	struct msm_rd_state *rd = inode->i_private; +	rd->open = false; +	return 0; +} + + +static const struct file_operations rd_debugfs_fops = { +	.owner = THIS_MODULE, +	.open = rd_open, +	.read = rd_read, +	.llseek = no_llseek, +	.release = rd_release, +}; + +int msm_rd_debugfs_init(struct drm_minor *minor) +{ +	struct msm_drm_private *priv = minor->dev->dev_private; +	struct msm_rd_state *rd; + +	/* only create on first minor: */ +	if (priv->rd) +		return 0; + +	rd = kzalloc(sizeof(*rd), GFP_KERNEL); +	if (!rd) +		return -ENOMEM; + +	rd->dev = minor->dev; +	rd->fifo.buf = rd->buf; + +	mutex_init(&rd->read_lock); +	priv->rd = rd; + +	init_waitqueue_head(&rd->fifo_event); + +	rd->node = kzalloc(sizeof(*rd->node), GFP_KERNEL); +	if (!rd->node) +		goto fail; + +	rd->ent = debugfs_create_file("rd", S_IFREG | S_IRUGO, +			minor->debugfs_root, rd, &rd_debugfs_fops); +	if (!rd->ent) { +		DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/rd\n", +				minor->debugfs_root->d_name.name); +		goto fail; +	} + +	rd->node->minor = minor; +	rd->node->dent  = rd->ent; +	rd->node->info_ent = NULL; + +	mutex_lock(&minor->debugfs_lock); +	list_add(&rd->node->list, &minor->debugfs_list); +	mutex_unlock(&minor->debugfs_lock); + +	return 0; + +fail: +	msm_rd_debugfs_cleanup(minor); +	return -1; +} + +void msm_rd_debugfs_cleanup(struct drm_minor *minor) +{ +	struct msm_drm_private *priv = minor->dev->dev_private; +	struct msm_rd_state *rd = priv->rd; + +	if (!rd) +		return; + +	priv->rd = NULL; + +	debugfs_remove(rd->ent); + +	if (rd->node) { +		mutex_lock(&minor->debugfs_lock); +		list_del(&rd->node->list); +		mutex_unlock(&minor->debugfs_lock); +		kfree(rd->node); +	} + +	mutex_destroy(&rd->read_lock); + +	kfree(rd); +} + +/* called under struct_mutex */ +void msm_rd_dump_submit(struct msm_gem_submit *submit) +{ +	struct drm_device *dev = submit->dev; +	struct msm_drm_private *priv = dev->dev_private; +	struct msm_rd_state *rd = priv->rd; +	char msg[128]; +	int i, n; + +	if (!rd->open) +		return; + +	/* writing into fifo is serialized by caller, and +	 * rd->read_lock is used to serialize the reads +	 */ +	WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + +	n = snprintf(msg, sizeof(msg), "%.*s/%d: fence=%u", +			TASK_COMM_LEN, current->comm, task_pid_nr(current), +			submit->fence); + +	rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4)); + +	/* could be nice to have an option (module-param?) to snapshot +	 * all the bo's associated with the submit.  Handy to see vtx +	 * buffers, etc.  For now just the cmdstream bo's is enough. +	 */ + +	for (i = 0; i < submit->nr_cmds; i++) { +		uint32_t idx  = submit->cmd[i].idx; +		uint32_t iova = submit->cmd[i].iova; +		uint32_t szd  = submit->cmd[i].size; /* in dwords */ +		struct msm_gem_object *obj = submit->bos[idx].obj; +		const char *buf = msm_gem_vaddr_locked(&obj->base); + +		buf += iova - submit->bos[idx].iova; + +		rd_write_section(rd, RD_GPUADDR, +				(uint32_t[2]){ iova, szd * 4 }, 8); +		rd_write_section(rd, RD_BUFFER_CONTENTS, +				buf, szd * 4); + +		switch (submit->cmd[i].type) { +		case MSM_SUBMIT_CMD_IB_TARGET_BUF: +			/* ignore IB-targets, we've logged the buffer, the +			 * parser tool will follow the IB based on the logged +			 * buffer/gpuaddr, so nothing more to do. +			 */ +			break; +		case MSM_SUBMIT_CMD_CTX_RESTORE_BUF: +		case MSM_SUBMIT_CMD_BUF: +			rd_write_section(rd, RD_CMDSTREAM_ADDR, +					(uint32_t[2]){ iova, szd }, 8); +			break; +		} +	} +} +#endif  | 
