diff options
Diffstat (limited to 'drivers/gpu/drm/msm/mdp')
20 files changed, 6689 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h new file mode 100644 index 00000000000..416a26e1e58 --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h @@ -0,0 +1,1033 @@ +#ifndef MDP4_XML +#define MDP4_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 mdp4_pipe { +	VG1 = 0, +	VG2 = 1, +	RGB1 = 2, +	RGB2 = 3, +	RGB3 = 4, +	VG3 = 5, +	VG4 = 6, +}; + +enum mdp4_mixer { +	MIXER0 = 0, +	MIXER1 = 1, +	MIXER2 = 2, +}; + +enum mdp4_intf { +	INTF_LCDC_DTV = 0, +	INTF_DSI_VIDEO = 1, +	INTF_DSI_CMD = 2, +	INTF_EBI2_TV = 3, +}; + +enum mdp4_cursor_format { +	CURSOR_ARGB = 1, +	CURSOR_XRGB = 2, +}; + +enum mdp4_dma { +	DMA_P = 0, +	DMA_S = 1, +	DMA_E = 2, +}; + +#define MDP4_IRQ_OVERLAY0_DONE					0x00000001 +#define MDP4_IRQ_OVERLAY1_DONE					0x00000002 +#define MDP4_IRQ_DMA_S_DONE					0x00000004 +#define MDP4_IRQ_DMA_E_DONE					0x00000008 +#define MDP4_IRQ_DMA_P_DONE					0x00000010 +#define MDP4_IRQ_VG1_HISTOGRAM					0x00000020 +#define MDP4_IRQ_VG2_HISTOGRAM					0x00000040 +#define MDP4_IRQ_PRIMARY_VSYNC					0x00000080 +#define MDP4_IRQ_PRIMARY_INTF_UDERRUN				0x00000100 +#define MDP4_IRQ_EXTERNAL_VSYNC					0x00000200 +#define MDP4_IRQ_EXTERNAL_INTF_UDERRUN				0x00000400 +#define MDP4_IRQ_PRIMARY_RDPTR					0x00000800 +#define MDP4_IRQ_DMA_P_HISTOGRAM				0x00020000 +#define MDP4_IRQ_DMA_S_HISTOGRAM				0x04000000 +#define MDP4_IRQ_OVERLAY2_DONE					0x40000000 +#define REG_MDP4_VERSION					0x00000000 +#define MDP4_VERSION_MINOR__MASK				0x00ff0000 +#define MDP4_VERSION_MINOR__SHIFT				16 +static inline uint32_t MDP4_VERSION_MINOR(uint32_t val) +{ +	return ((val) << MDP4_VERSION_MINOR__SHIFT) & MDP4_VERSION_MINOR__MASK; +} +#define MDP4_VERSION_MAJOR__MASK				0xff000000 +#define MDP4_VERSION_MAJOR__SHIFT				24 +static inline uint32_t MDP4_VERSION_MAJOR(uint32_t val) +{ +	return ((val) << MDP4_VERSION_MAJOR__SHIFT) & MDP4_VERSION_MAJOR__MASK; +} + +#define REG_MDP4_OVLP0_KICK					0x00000004 + +#define REG_MDP4_OVLP1_KICK					0x00000008 + +#define REG_MDP4_OVLP2_KICK					0x000000d0 + +#define REG_MDP4_DMA_P_KICK					0x0000000c + +#define REG_MDP4_DMA_S_KICK					0x00000010 + +#define REG_MDP4_DMA_E_KICK					0x00000014 + +#define REG_MDP4_DISP_STATUS					0x00000018 + +#define REG_MDP4_DISP_INTF_SEL					0x00000038 +#define MDP4_DISP_INTF_SEL_PRIM__MASK				0x00000003 +#define MDP4_DISP_INTF_SEL_PRIM__SHIFT				0 +static inline uint32_t MDP4_DISP_INTF_SEL_PRIM(enum mdp4_intf val) +{ +	return ((val) << MDP4_DISP_INTF_SEL_PRIM__SHIFT) & MDP4_DISP_INTF_SEL_PRIM__MASK; +} +#define MDP4_DISP_INTF_SEL_SEC__MASK				0x0000000c +#define MDP4_DISP_INTF_SEL_SEC__SHIFT				2 +static inline uint32_t MDP4_DISP_INTF_SEL_SEC(enum mdp4_intf val) +{ +	return ((val) << MDP4_DISP_INTF_SEL_SEC__SHIFT) & MDP4_DISP_INTF_SEL_SEC__MASK; +} +#define MDP4_DISP_INTF_SEL_EXT__MASK				0x00000030 +#define MDP4_DISP_INTF_SEL_EXT__SHIFT				4 +static inline uint32_t MDP4_DISP_INTF_SEL_EXT(enum mdp4_intf val) +{ +	return ((val) << MDP4_DISP_INTF_SEL_EXT__SHIFT) & MDP4_DISP_INTF_SEL_EXT__MASK; +} +#define MDP4_DISP_INTF_SEL_DSI_VIDEO				0x00000040 +#define MDP4_DISP_INTF_SEL_DSI_CMD				0x00000080 + +#define REG_MDP4_RESET_STATUS					0x0000003c + +#define REG_MDP4_READ_CNFG					0x0000004c + +#define REG_MDP4_INTR_ENABLE					0x00000050 + +#define REG_MDP4_INTR_STATUS					0x00000054 + +#define REG_MDP4_INTR_CLEAR					0x00000058 + +#define REG_MDP4_EBI2_LCD0					0x00000060 + +#define REG_MDP4_EBI2_LCD1					0x00000064 + +#define REG_MDP4_PORTMAP_MODE					0x00000070 + +#define REG_MDP4_CS_CONTROLLER0					0x000000c0 + +#define REG_MDP4_CS_CONTROLLER1					0x000000c4 + +#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 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 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 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 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 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 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 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 mdp_mixer_stage_id val) +{ +	return ((val) << MDP4_LAYERMIXER2_IN_CFG_PIPE7__SHIFT) & MDP4_LAYERMIXER2_IN_CFG_PIPE7__MASK; +} +#define MDP4_LAYERMIXER2_IN_CFG_PIPE7_MIXER1			0x80000000 + +#define REG_MDP4_LAYERMIXER_IN_CFG_UPDATE_METHOD		0x000100fc + +#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 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 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 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 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 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 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 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 mdp_mixer_stage_id val) +{ +	return ((val) << MDP4_LAYERMIXER_IN_CFG_PIPE7__SHIFT) & MDP4_LAYERMIXER_IN_CFG_PIPE7__MASK; +} +#define MDP4_LAYERMIXER_IN_CFG_PIPE7_MIXER1			0x80000000 + +#define REG_MDP4_VG2_SRC_FORMAT					0x00030050 + +#define REG_MDP4_VG2_CONST_COLOR				0x00031008 + +#define REG_MDP4_OVERLAY_FLUSH					0x00018000 +#define MDP4_OVERLAY_FLUSH_OVLP0				0x00000001 +#define MDP4_OVERLAY_FLUSH_OVLP1				0x00000002 +#define MDP4_OVERLAY_FLUSH_VG1					0x00000004 +#define MDP4_OVERLAY_FLUSH_VG2					0x00000008 +#define MDP4_OVERLAY_FLUSH_RGB1					0x00000010 +#define MDP4_OVERLAY_FLUSH_RGB2					0x00000020 + +static inline uint32_t __offset_OVLP(uint32_t idx) +{ +	switch (idx) { +		case 0: return 0x00010000; +		case 1: return 0x00018000; +		case 2: return 0x00088000; +		default: return INVALID_IDX(idx); +	} +} +static inline uint32_t REG_MDP4_OVLP(uint32_t i0) { return 0x00000000 + __offset_OVLP(i0); } + +static inline uint32_t REG_MDP4_OVLP_CFG(uint32_t i0) { return 0x00000004 + __offset_OVLP(i0); } + +static inline uint32_t REG_MDP4_OVLP_SIZE(uint32_t i0) { return 0x00000008 + __offset_OVLP(i0); } +#define MDP4_OVLP_SIZE_HEIGHT__MASK				0xffff0000 +#define MDP4_OVLP_SIZE_HEIGHT__SHIFT				16 +static inline uint32_t MDP4_OVLP_SIZE_HEIGHT(uint32_t val) +{ +	return ((val) << MDP4_OVLP_SIZE_HEIGHT__SHIFT) & MDP4_OVLP_SIZE_HEIGHT__MASK; +} +#define MDP4_OVLP_SIZE_WIDTH__MASK				0x0000ffff +#define MDP4_OVLP_SIZE_WIDTH__SHIFT				0 +static inline uint32_t MDP4_OVLP_SIZE_WIDTH(uint32_t val) +{ +	return ((val) << MDP4_OVLP_SIZE_WIDTH__SHIFT) & MDP4_OVLP_SIZE_WIDTH__MASK; +} + +static inline uint32_t REG_MDP4_OVLP_BASE(uint32_t i0) { return 0x0000000c + __offset_OVLP(i0); } + +static inline uint32_t REG_MDP4_OVLP_STRIDE(uint32_t i0) { return 0x00000010 + __offset_OVLP(i0); } + +static inline uint32_t REG_MDP4_OVLP_OPMODE(uint32_t i0) { return 0x00000014 + __offset_OVLP(i0); } + +static inline uint32_t __offset_STAGE(uint32_t idx) +{ +	switch (idx) { +		case 0: return 0x00000104; +		case 1: return 0x00000124; +		case 2: return 0x00000144; +		case 3: return 0x00000160; +		default: return INVALID_IDX(idx); +	} +} +static inline uint32_t REG_MDP4_OVLP_STAGE(uint32_t i0, uint32_t i1) { return 0x00000000 + __offset_OVLP(i0) + __offset_STAGE(i1); } + +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 mdp_alpha_type val) +{ +	return ((val) << MDP4_OVLP_STAGE_OP_FG_ALPHA__SHIFT) & MDP4_OVLP_STAGE_OP_FG_ALPHA__MASK; +} +#define MDP4_OVLP_STAGE_OP_FG_INV_ALPHA				0x00000004 +#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 mdp_alpha_type val) +{ +	return ((val) << MDP4_OVLP_STAGE_OP_BG_ALPHA__SHIFT) & MDP4_OVLP_STAGE_OP_BG_ALPHA__MASK; +} +#define MDP4_OVLP_STAGE_OP_BG_INV_ALPHA				0x00000040 +#define MDP4_OVLP_STAGE_OP_BG_MOD_ALPHA				0x00000080 +#define MDP4_OVLP_STAGE_OP_FG_TRANSP				0x00000100 +#define MDP4_OVLP_STAGE_OP_BG_TRANSP				0x00000200 + +static inline uint32_t REG_MDP4_OVLP_STAGE_FG_ALPHA(uint32_t i0, uint32_t i1) { return 0x00000004 + __offset_OVLP(i0) + __offset_STAGE(i1); } + +static inline uint32_t REG_MDP4_OVLP_STAGE_BG_ALPHA(uint32_t i0, uint32_t i1) { return 0x00000008 + __offset_OVLP(i0) + __offset_STAGE(i1); } + +static inline uint32_t REG_MDP4_OVLP_STAGE_TRANSP_LOW0(uint32_t i0, uint32_t i1) { return 0x0000000c + __offset_OVLP(i0) + __offset_STAGE(i1); } + +static inline uint32_t REG_MDP4_OVLP_STAGE_TRANSP_LOW1(uint32_t i0, uint32_t i1) { return 0x00000010 + __offset_OVLP(i0) + __offset_STAGE(i1); } + +static inline uint32_t REG_MDP4_OVLP_STAGE_TRANSP_HIGH0(uint32_t i0, uint32_t i1) { return 0x00000014 + __offset_OVLP(i0) + __offset_STAGE(i1); } + +static inline uint32_t REG_MDP4_OVLP_STAGE_TRANSP_HIGH1(uint32_t i0, uint32_t i1) { return 0x00000018 + __offset_OVLP(i0) + __offset_STAGE(i1); } + +static inline uint32_t __offset_STAGE_CO3(uint32_t idx) +{ +	switch (idx) { +		case 0: return 0x00001004; +		case 1: return 0x00001404; +		case 2: return 0x00001804; +		case 3: return 0x00001b84; +		default: return INVALID_IDX(idx); +	} +} +static inline uint32_t REG_MDP4_OVLP_STAGE_CO3(uint32_t i0, uint32_t i1) { return 0x00000000 + __offset_OVLP(i0) + __offset_STAGE_CO3(i1); } + +static inline uint32_t REG_MDP4_OVLP_STAGE_CO3_SEL(uint32_t i0, uint32_t i1) { return 0x00000000 + __offset_OVLP(i0) + __offset_STAGE_CO3(i1); } +#define MDP4_OVLP_STAGE_CO3_SEL_FG_ALPHA			0x00000001 + +static inline uint32_t REG_MDP4_OVLP_TRANSP_LOW0(uint32_t i0) { return 0x00000180 + __offset_OVLP(i0); } + +static inline uint32_t REG_MDP4_OVLP_TRANSP_LOW1(uint32_t i0) { return 0x00000184 + __offset_OVLP(i0); } + +static inline uint32_t REG_MDP4_OVLP_TRANSP_HIGH0(uint32_t i0) { return 0x00000188 + __offset_OVLP(i0); } + +static inline uint32_t REG_MDP4_OVLP_TRANSP_HIGH1(uint32_t i0) { return 0x0000018c + __offset_OVLP(i0); } + +static inline uint32_t REG_MDP4_OVLP_CSC_CONFIG(uint32_t i0) { return 0x00000200 + __offset_OVLP(i0); } + +static inline uint32_t REG_MDP4_OVLP_CSC(uint32_t i0) { return 0x00002000 + __offset_OVLP(i0); } + + +static inline uint32_t REG_MDP4_OVLP_CSC_MV(uint32_t i0, uint32_t i1) { return 0x00002400 + __offset_OVLP(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_OVLP_CSC_MV_VAL(uint32_t i0, uint32_t i1) { return 0x00002400 + __offset_OVLP(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_OVLP_CSC_PRE_BV(uint32_t i0, uint32_t i1) { return 0x00002500 + __offset_OVLP(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_OVLP_CSC_PRE_BV_VAL(uint32_t i0, uint32_t i1) { return 0x00002500 + __offset_OVLP(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_OVLP_CSC_POST_BV(uint32_t i0, uint32_t i1) { return 0x00002580 + __offset_OVLP(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_OVLP_CSC_POST_BV_VAL(uint32_t i0, uint32_t i1) { return 0x00002580 + __offset_OVLP(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_OVLP_CSC_PRE_LV(uint32_t i0, uint32_t i1) { return 0x00002600 + __offset_OVLP(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_OVLP_CSC_PRE_LV_VAL(uint32_t i0, uint32_t i1) { return 0x00002600 + __offset_OVLP(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_OVLP_CSC_POST_LV(uint32_t i0, uint32_t i1) { return 0x00002680 + __offset_OVLP(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_OVLP_CSC_POST_LV_VAL(uint32_t i0, uint32_t i1) { return 0x00002680 + __offset_OVLP(i0) + 0x4*i1; } + +#define REG_MDP4_DMA_P_OP_MODE					0x00090070 + +static inline uint32_t REG_MDP4_LUTN(uint32_t i0) { return 0x00094800 + 0x400*i0; } + +static inline uint32_t REG_MDP4_LUTN_LUT(uint32_t i0, uint32_t i1) { return 0x00094800 + 0x400*i0 + 0x4*i1; } + +static inline uint32_t REG_MDP4_LUTN_LUT_VAL(uint32_t i0, uint32_t i1) { return 0x00094800 + 0x400*i0 + 0x4*i1; } + +#define REG_MDP4_DMA_S_OP_MODE					0x000a0028 + +static inline uint32_t REG_MDP4_DMA_E_QUANT(uint32_t i0) { return 0x000b0070 + 0x4*i0; } + +static inline uint32_t __offset_DMA(enum mdp4_dma idx) +{ +	switch (idx) { +		case DMA_P: return 0x00090000; +		case DMA_S: return 0x000a0000; +		case DMA_E: return 0x000b0000; +		default: return INVALID_IDX(idx); +	} +} +static inline uint32_t REG_MDP4_DMA(enum mdp4_dma i0) { return 0x00000000 + __offset_DMA(i0); } + +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 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 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 mdp_bpc val) +{ +	return ((val) << MDP4_DMA_CONFIG_R_BPC__SHIFT) & MDP4_DMA_CONFIG_R_BPC__MASK; +} +#define MDP4_DMA_CONFIG_PACK_ALIGN_MSB				0x00000080 +#define MDP4_DMA_CONFIG_PACK__MASK				0x0000ff00 +#define MDP4_DMA_CONFIG_PACK__SHIFT				8 +static inline uint32_t MDP4_DMA_CONFIG_PACK(uint32_t val) +{ +	return ((val) << MDP4_DMA_CONFIG_PACK__SHIFT) & MDP4_DMA_CONFIG_PACK__MASK; +} +#define MDP4_DMA_CONFIG_DEFLKR_EN				0x01000000 +#define MDP4_DMA_CONFIG_DITHER_EN				0x01000000 + +static inline uint32_t REG_MDP4_DMA_SRC_SIZE(enum mdp4_dma i0) { return 0x00000004 + __offset_DMA(i0); } +#define MDP4_DMA_SRC_SIZE_HEIGHT__MASK				0xffff0000 +#define MDP4_DMA_SRC_SIZE_HEIGHT__SHIFT				16 +static inline uint32_t MDP4_DMA_SRC_SIZE_HEIGHT(uint32_t val) +{ +	return ((val) << MDP4_DMA_SRC_SIZE_HEIGHT__SHIFT) & MDP4_DMA_SRC_SIZE_HEIGHT__MASK; +} +#define MDP4_DMA_SRC_SIZE_WIDTH__MASK				0x0000ffff +#define MDP4_DMA_SRC_SIZE_WIDTH__SHIFT				0 +static inline uint32_t MDP4_DMA_SRC_SIZE_WIDTH(uint32_t val) +{ +	return ((val) << MDP4_DMA_SRC_SIZE_WIDTH__SHIFT) & MDP4_DMA_SRC_SIZE_WIDTH__MASK; +} + +static inline uint32_t REG_MDP4_DMA_SRC_BASE(enum mdp4_dma i0) { return 0x00000008 + __offset_DMA(i0); } + +static inline uint32_t REG_MDP4_DMA_SRC_STRIDE(enum mdp4_dma i0) { return 0x0000000c + __offset_DMA(i0); } + +static inline uint32_t REG_MDP4_DMA_DST_SIZE(enum mdp4_dma i0) { return 0x00000010 + __offset_DMA(i0); } +#define MDP4_DMA_DST_SIZE_HEIGHT__MASK				0xffff0000 +#define MDP4_DMA_DST_SIZE_HEIGHT__SHIFT				16 +static inline uint32_t MDP4_DMA_DST_SIZE_HEIGHT(uint32_t val) +{ +	return ((val) << MDP4_DMA_DST_SIZE_HEIGHT__SHIFT) & MDP4_DMA_DST_SIZE_HEIGHT__MASK; +} +#define MDP4_DMA_DST_SIZE_WIDTH__MASK				0x0000ffff +#define MDP4_DMA_DST_SIZE_WIDTH__SHIFT				0 +static inline uint32_t MDP4_DMA_DST_SIZE_WIDTH(uint32_t val) +{ +	return ((val) << MDP4_DMA_DST_SIZE_WIDTH__SHIFT) & MDP4_DMA_DST_SIZE_WIDTH__MASK; +} + +static inline uint32_t REG_MDP4_DMA_CURSOR_SIZE(enum mdp4_dma i0) { return 0x00000044 + __offset_DMA(i0); } +#define MDP4_DMA_CURSOR_SIZE_WIDTH__MASK			0x0000007f +#define MDP4_DMA_CURSOR_SIZE_WIDTH__SHIFT			0 +static inline uint32_t MDP4_DMA_CURSOR_SIZE_WIDTH(uint32_t val) +{ +	return ((val) << MDP4_DMA_CURSOR_SIZE_WIDTH__SHIFT) & MDP4_DMA_CURSOR_SIZE_WIDTH__MASK; +} +#define MDP4_DMA_CURSOR_SIZE_HEIGHT__MASK			0x007f0000 +#define MDP4_DMA_CURSOR_SIZE_HEIGHT__SHIFT			16 +static inline uint32_t MDP4_DMA_CURSOR_SIZE_HEIGHT(uint32_t val) +{ +	return ((val) << MDP4_DMA_CURSOR_SIZE_HEIGHT__SHIFT) & MDP4_DMA_CURSOR_SIZE_HEIGHT__MASK; +} + +static inline uint32_t REG_MDP4_DMA_CURSOR_BASE(enum mdp4_dma i0) { return 0x00000048 + __offset_DMA(i0); } + +static inline uint32_t REG_MDP4_DMA_CURSOR_POS(enum mdp4_dma i0) { return 0x0000004c + __offset_DMA(i0); } +#define MDP4_DMA_CURSOR_POS_X__MASK				0x0000ffff +#define MDP4_DMA_CURSOR_POS_X__SHIFT				0 +static inline uint32_t MDP4_DMA_CURSOR_POS_X(uint32_t val) +{ +	return ((val) << MDP4_DMA_CURSOR_POS_X__SHIFT) & MDP4_DMA_CURSOR_POS_X__MASK; +} +#define MDP4_DMA_CURSOR_POS_Y__MASK				0xffff0000 +#define MDP4_DMA_CURSOR_POS_Y__SHIFT				16 +static inline uint32_t MDP4_DMA_CURSOR_POS_Y(uint32_t val) +{ +	return ((val) << MDP4_DMA_CURSOR_POS_Y__SHIFT) & MDP4_DMA_CURSOR_POS_Y__MASK; +} + +static inline uint32_t REG_MDP4_DMA_CURSOR_BLEND_CONFIG(enum mdp4_dma i0) { return 0x00000060 + __offset_DMA(i0); } +#define MDP4_DMA_CURSOR_BLEND_CONFIG_CURSOR_EN			0x00000001 +#define MDP4_DMA_CURSOR_BLEND_CONFIG_FORMAT__MASK		0x00000006 +#define MDP4_DMA_CURSOR_BLEND_CONFIG_FORMAT__SHIFT		1 +static inline uint32_t MDP4_DMA_CURSOR_BLEND_CONFIG_FORMAT(enum mdp4_cursor_format val) +{ +	return ((val) << MDP4_DMA_CURSOR_BLEND_CONFIG_FORMAT__SHIFT) & MDP4_DMA_CURSOR_BLEND_CONFIG_FORMAT__MASK; +} +#define MDP4_DMA_CURSOR_BLEND_CONFIG_TRANSP_EN			0x00000008 + +static inline uint32_t REG_MDP4_DMA_CURSOR_BLEND_PARAM(enum mdp4_dma i0) { return 0x00000064 + __offset_DMA(i0); } + +static inline uint32_t REG_MDP4_DMA_BLEND_TRANS_LOW(enum mdp4_dma i0) { return 0x00000068 + __offset_DMA(i0); } + +static inline uint32_t REG_MDP4_DMA_BLEND_TRANS_HIGH(enum mdp4_dma i0) { return 0x0000006c + __offset_DMA(i0); } + +static inline uint32_t REG_MDP4_DMA_FETCH_CONFIG(enum mdp4_dma i0) { return 0x00001004 + __offset_DMA(i0); } + +static inline uint32_t REG_MDP4_DMA_CSC(enum mdp4_dma i0) { return 0x00003000 + __offset_DMA(i0); } + + +static inline uint32_t REG_MDP4_DMA_CSC_MV(enum mdp4_dma i0, uint32_t i1) { return 0x00003400 + __offset_DMA(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_DMA_CSC_MV_VAL(enum mdp4_dma i0, uint32_t i1) { return 0x00003400 + __offset_DMA(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_DMA_CSC_PRE_BV(enum mdp4_dma i0, uint32_t i1) { return 0x00003500 + __offset_DMA(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_DMA_CSC_PRE_BV_VAL(enum mdp4_dma i0, uint32_t i1) { return 0x00003500 + __offset_DMA(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_DMA_CSC_POST_BV(enum mdp4_dma i0, uint32_t i1) { return 0x00003580 + __offset_DMA(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_DMA_CSC_POST_BV_VAL(enum mdp4_dma i0, uint32_t i1) { return 0x00003580 + __offset_DMA(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_DMA_CSC_PRE_LV(enum mdp4_dma i0, uint32_t i1) { return 0x00003600 + __offset_DMA(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_DMA_CSC_PRE_LV_VAL(enum mdp4_dma i0, uint32_t i1) { return 0x00003600 + __offset_DMA(i0) + 0x4*i1; } + +static inline uint32_t REG_MDP4_DMA_CSC_POST_LV(enum mdp4_dma i0, uint32_t i1) { return 0x00003680 + __offset_DMA(i0) + 0x4*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 mdp4_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) +{ +	return ((val) << MDP4_PIPE_SRC_SIZE_HEIGHT__SHIFT) & MDP4_PIPE_SRC_SIZE_HEIGHT__MASK; +} +#define MDP4_PIPE_SRC_SIZE_WIDTH__MASK				0x0000ffff +#define MDP4_PIPE_SRC_SIZE_WIDTH__SHIFT				0 +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 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) +{ +	return ((val) << MDP4_PIPE_SRC_XY_Y__SHIFT) & MDP4_PIPE_SRC_XY_Y__MASK; +} +#define MDP4_PIPE_SRC_XY_X__MASK				0x0000ffff +#define MDP4_PIPE_SRC_XY_X__SHIFT				0 +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 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) +{ +	return ((val) << MDP4_PIPE_DST_SIZE_HEIGHT__SHIFT) & MDP4_PIPE_DST_SIZE_HEIGHT__MASK; +} +#define MDP4_PIPE_DST_SIZE_WIDTH__MASK				0x0000ffff +#define MDP4_PIPE_DST_SIZE_WIDTH__SHIFT				0 +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 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) +{ +	return ((val) << MDP4_PIPE_DST_XY_Y__SHIFT) & MDP4_PIPE_DST_XY_Y__MASK; +} +#define MDP4_PIPE_DST_XY_X__MASK				0x0000ffff +#define MDP4_PIPE_DST_XY_X__SHIFT				0 +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 mdp4_pipe i0) { return 0x00020010 + 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 mdp4_pipe i0) { return 0x00020018 + 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) +{ +	return ((val) << MDP4_PIPE_SRC_STRIDE_A_P0__SHIFT) & MDP4_PIPE_SRC_STRIDE_A_P0__MASK; +} +#define MDP4_PIPE_SRC_STRIDE_A_P1__MASK				0xffff0000 +#define MDP4_PIPE_SRC_STRIDE_A_P1__SHIFT			16 +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 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) +{ +	return ((val) << MDP4_PIPE_SRC_STRIDE_B_P2__SHIFT) & MDP4_PIPE_SRC_STRIDE_B_P2__MASK; +} +#define MDP4_PIPE_SRC_STRIDE_B_P3__MASK				0xffff0000 +#define MDP4_PIPE_SRC_STRIDE_B_P3__SHIFT			16 +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 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) +{ +	return ((val) << MDP4_PIPE_FRAME_SIZE_HEIGHT__SHIFT) & MDP4_PIPE_FRAME_SIZE_HEIGHT__MASK; +} +#define MDP4_PIPE_FRAME_SIZE_WIDTH__MASK			0x0000ffff +#define MDP4_PIPE_FRAME_SIZE_WIDTH__SHIFT			0 +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 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 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 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 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 mdp_bpc_alpha val) +{ +	return ((val) << MDP4_PIPE_SRC_FORMAT_A_BPC__SHIFT) & MDP4_PIPE_SRC_FORMAT_A_BPC__MASK; +} +#define MDP4_PIPE_SRC_FORMAT_ALPHA_ENABLE			0x00000100 +#define MDP4_PIPE_SRC_FORMAT_CPP__MASK				0x00000600 +#define MDP4_PIPE_SRC_FORMAT_CPP__SHIFT				9 +static inline uint32_t MDP4_PIPE_SRC_FORMAT_CPP(uint32_t val) +{ +	return ((val) << MDP4_PIPE_SRC_FORMAT_CPP__SHIFT) & MDP4_PIPE_SRC_FORMAT_CPP__MASK; +} +#define MDP4_PIPE_SRC_FORMAT_ROTATED_90				0x00001000 +#define MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT__MASK			0x00006000 +#define MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT__SHIFT		13 +static inline uint32_t MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT(uint32_t val) +{ +	return ((val) << MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT__SHIFT) & MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT__MASK; +} +#define MDP4_PIPE_SRC_FORMAT_UNPACK_TIGHT			0x00020000 +#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 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) +{ +	return ((val) << MDP4_PIPE_SRC_UNPACK_ELEM0__SHIFT) & MDP4_PIPE_SRC_UNPACK_ELEM0__MASK; +} +#define MDP4_PIPE_SRC_UNPACK_ELEM1__MASK			0x0000ff00 +#define MDP4_PIPE_SRC_UNPACK_ELEM1__SHIFT			8 +static inline uint32_t MDP4_PIPE_SRC_UNPACK_ELEM1(uint32_t val) +{ +	return ((val) << MDP4_PIPE_SRC_UNPACK_ELEM1__SHIFT) & MDP4_PIPE_SRC_UNPACK_ELEM1__MASK; +} +#define MDP4_PIPE_SRC_UNPACK_ELEM2__MASK			0x00ff0000 +#define MDP4_PIPE_SRC_UNPACK_ELEM2__SHIFT			16 +static inline uint32_t MDP4_PIPE_SRC_UNPACK_ELEM2(uint32_t val) +{ +	return ((val) << MDP4_PIPE_SRC_UNPACK_ELEM2__SHIFT) & MDP4_PIPE_SRC_UNPACK_ELEM2__MASK; +} +#define MDP4_PIPE_SRC_UNPACK_ELEM3__MASK			0xff000000 +#define MDP4_PIPE_SRC_UNPACK_ELEM3__SHIFT			24 +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 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 +#define MDP4_PIPE_OP_MODE_DST_YCBCR				0x00000400 +#define MDP4_PIPE_OP_MODE_CSC_EN				0x00000800 +#define MDP4_PIPE_OP_MODE_FLIP_LR				0x00002000 +#define MDP4_PIPE_OP_MODE_FLIP_UD				0x00004000 +#define MDP4_PIPE_OP_MODE_DITHER_EN				0x00008000 +#define MDP4_PIPE_OP_MODE_IGC_LUT_EN				0x00010000 +#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 mdp4_pipe i0) { return 0x0002005c + 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 mdp4_pipe i0) { return 0x00021004 + 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 mdp4_pipe i0) { return 0x00024000 + 0x10000*i0; } + + +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 mdp4_pipe i0, uint32_t i1) { return 0x00024400 + 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 mdp4_pipe i0, uint32_t i1) { return 0x00024500 + 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 mdp4_pipe i0, uint32_t i1) { return 0x00024580 + 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 mdp4_pipe i0, uint32_t i1) { return 0x00024600 + 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 mdp4_pipe i0, uint32_t i1) { return 0x00024680 + 0x10000*i0 + 0x4*i1; } + +#define REG_MDP4_LCDC						0x000c0000 + +#define REG_MDP4_LCDC_ENABLE					0x000c0000 + +#define REG_MDP4_LCDC_HSYNC_CTRL				0x000c0004 +#define MDP4_LCDC_HSYNC_CTRL_PULSEW__MASK			0x0000ffff +#define MDP4_LCDC_HSYNC_CTRL_PULSEW__SHIFT			0 +static inline uint32_t MDP4_LCDC_HSYNC_CTRL_PULSEW(uint32_t val) +{ +	return ((val) << MDP4_LCDC_HSYNC_CTRL_PULSEW__SHIFT) & MDP4_LCDC_HSYNC_CTRL_PULSEW__MASK; +} +#define MDP4_LCDC_HSYNC_CTRL_PERIOD__MASK			0xffff0000 +#define MDP4_LCDC_HSYNC_CTRL_PERIOD__SHIFT			16 +static inline uint32_t MDP4_LCDC_HSYNC_CTRL_PERIOD(uint32_t val) +{ +	return ((val) << MDP4_LCDC_HSYNC_CTRL_PERIOD__SHIFT) & MDP4_LCDC_HSYNC_CTRL_PERIOD__MASK; +} + +#define REG_MDP4_LCDC_VSYNC_PERIOD				0x000c0008 + +#define REG_MDP4_LCDC_VSYNC_LEN					0x000c000c + +#define REG_MDP4_LCDC_DISPLAY_HCTRL				0x000c0010 +#define MDP4_LCDC_DISPLAY_HCTRL_START__MASK			0x0000ffff +#define MDP4_LCDC_DISPLAY_HCTRL_START__SHIFT			0 +static inline uint32_t MDP4_LCDC_DISPLAY_HCTRL_START(uint32_t val) +{ +	return ((val) << MDP4_LCDC_DISPLAY_HCTRL_START__SHIFT) & MDP4_LCDC_DISPLAY_HCTRL_START__MASK; +} +#define MDP4_LCDC_DISPLAY_HCTRL_END__MASK			0xffff0000 +#define MDP4_LCDC_DISPLAY_HCTRL_END__SHIFT			16 +static inline uint32_t MDP4_LCDC_DISPLAY_HCTRL_END(uint32_t val) +{ +	return ((val) << MDP4_LCDC_DISPLAY_HCTRL_END__SHIFT) & MDP4_LCDC_DISPLAY_HCTRL_END__MASK; +} + +#define REG_MDP4_LCDC_DISPLAY_VSTART				0x000c0014 + +#define REG_MDP4_LCDC_DISPLAY_VEND				0x000c0018 + +#define REG_MDP4_LCDC_ACTIVE_HCTL				0x000c001c +#define MDP4_LCDC_ACTIVE_HCTL_START__MASK			0x00007fff +#define MDP4_LCDC_ACTIVE_HCTL_START__SHIFT			0 +static inline uint32_t MDP4_LCDC_ACTIVE_HCTL_START(uint32_t val) +{ +	return ((val) << MDP4_LCDC_ACTIVE_HCTL_START__SHIFT) & MDP4_LCDC_ACTIVE_HCTL_START__MASK; +} +#define MDP4_LCDC_ACTIVE_HCTL_END__MASK				0x7fff0000 +#define MDP4_LCDC_ACTIVE_HCTL_END__SHIFT			16 +static inline uint32_t MDP4_LCDC_ACTIVE_HCTL_END(uint32_t val) +{ +	return ((val) << MDP4_LCDC_ACTIVE_HCTL_END__SHIFT) & MDP4_LCDC_ACTIVE_HCTL_END__MASK; +} +#define MDP4_LCDC_ACTIVE_HCTL_ACTIVE_START_X			0x80000000 + +#define REG_MDP4_LCDC_ACTIVE_VSTART				0x000c0020 + +#define REG_MDP4_LCDC_ACTIVE_VEND				0x000c0024 + +#define REG_MDP4_LCDC_BORDER_CLR				0x000c0028 + +#define REG_MDP4_LCDC_UNDERFLOW_CLR				0x000c002c +#define MDP4_LCDC_UNDERFLOW_CLR_COLOR__MASK			0x00ffffff +#define MDP4_LCDC_UNDERFLOW_CLR_COLOR__SHIFT			0 +static inline uint32_t MDP4_LCDC_UNDERFLOW_CLR_COLOR(uint32_t val) +{ +	return ((val) << MDP4_LCDC_UNDERFLOW_CLR_COLOR__SHIFT) & MDP4_LCDC_UNDERFLOW_CLR_COLOR__MASK; +} +#define MDP4_LCDC_UNDERFLOW_CLR_ENABLE_RECOVERY			0x80000000 + +#define REG_MDP4_LCDC_HSYNC_SKEW				0x000c0030 + +#define REG_MDP4_LCDC_TEST_CNTL					0x000c0034 + +#define REG_MDP4_LCDC_CTRL_POLARITY				0x000c0038 +#define MDP4_LCDC_CTRL_POLARITY_HSYNC_LOW			0x00000001 +#define MDP4_LCDC_CTRL_POLARITY_VSYNC_LOW			0x00000002 +#define MDP4_LCDC_CTRL_POLARITY_DATA_EN_LOW			0x00000004 + +#define REG_MDP4_DTV						0x000d0000 + +#define REG_MDP4_DTV_ENABLE					0x000d0000 + +#define REG_MDP4_DTV_HSYNC_CTRL					0x000d0004 +#define MDP4_DTV_HSYNC_CTRL_PULSEW__MASK			0x0000ffff +#define MDP4_DTV_HSYNC_CTRL_PULSEW__SHIFT			0 +static inline uint32_t MDP4_DTV_HSYNC_CTRL_PULSEW(uint32_t val) +{ +	return ((val) << MDP4_DTV_HSYNC_CTRL_PULSEW__SHIFT) & MDP4_DTV_HSYNC_CTRL_PULSEW__MASK; +} +#define MDP4_DTV_HSYNC_CTRL_PERIOD__MASK			0xffff0000 +#define MDP4_DTV_HSYNC_CTRL_PERIOD__SHIFT			16 +static inline uint32_t MDP4_DTV_HSYNC_CTRL_PERIOD(uint32_t val) +{ +	return ((val) << MDP4_DTV_HSYNC_CTRL_PERIOD__SHIFT) & MDP4_DTV_HSYNC_CTRL_PERIOD__MASK; +} + +#define REG_MDP4_DTV_VSYNC_PERIOD				0x000d0008 + +#define REG_MDP4_DTV_VSYNC_LEN					0x000d000c + +#define REG_MDP4_DTV_DISPLAY_HCTRL				0x000d0018 +#define MDP4_DTV_DISPLAY_HCTRL_START__MASK			0x0000ffff +#define MDP4_DTV_DISPLAY_HCTRL_START__SHIFT			0 +static inline uint32_t MDP4_DTV_DISPLAY_HCTRL_START(uint32_t val) +{ +	return ((val) << MDP4_DTV_DISPLAY_HCTRL_START__SHIFT) & MDP4_DTV_DISPLAY_HCTRL_START__MASK; +} +#define MDP4_DTV_DISPLAY_HCTRL_END__MASK			0xffff0000 +#define MDP4_DTV_DISPLAY_HCTRL_END__SHIFT			16 +static inline uint32_t MDP4_DTV_DISPLAY_HCTRL_END(uint32_t val) +{ +	return ((val) << MDP4_DTV_DISPLAY_HCTRL_END__SHIFT) & MDP4_DTV_DISPLAY_HCTRL_END__MASK; +} + +#define REG_MDP4_DTV_DISPLAY_VSTART				0x000d001c + +#define REG_MDP4_DTV_DISPLAY_VEND				0x000d0020 + +#define REG_MDP4_DTV_ACTIVE_HCTL				0x000d002c +#define MDP4_DTV_ACTIVE_HCTL_START__MASK			0x00007fff +#define MDP4_DTV_ACTIVE_HCTL_START__SHIFT			0 +static inline uint32_t MDP4_DTV_ACTIVE_HCTL_START(uint32_t val) +{ +	return ((val) << MDP4_DTV_ACTIVE_HCTL_START__SHIFT) & MDP4_DTV_ACTIVE_HCTL_START__MASK; +} +#define MDP4_DTV_ACTIVE_HCTL_END__MASK				0x7fff0000 +#define MDP4_DTV_ACTIVE_HCTL_END__SHIFT				16 +static inline uint32_t MDP4_DTV_ACTIVE_HCTL_END(uint32_t val) +{ +	return ((val) << MDP4_DTV_ACTIVE_HCTL_END__SHIFT) & MDP4_DTV_ACTIVE_HCTL_END__MASK; +} +#define MDP4_DTV_ACTIVE_HCTL_ACTIVE_START_X			0x80000000 + +#define REG_MDP4_DTV_ACTIVE_VSTART				0x000d0030 + +#define REG_MDP4_DTV_ACTIVE_VEND				0x000d0038 + +#define REG_MDP4_DTV_BORDER_CLR					0x000d0040 + +#define REG_MDP4_DTV_UNDERFLOW_CLR				0x000d0044 +#define MDP4_DTV_UNDERFLOW_CLR_COLOR__MASK			0x00ffffff +#define MDP4_DTV_UNDERFLOW_CLR_COLOR__SHIFT			0 +static inline uint32_t MDP4_DTV_UNDERFLOW_CLR_COLOR(uint32_t val) +{ +	return ((val) << MDP4_DTV_UNDERFLOW_CLR_COLOR__SHIFT) & MDP4_DTV_UNDERFLOW_CLR_COLOR__MASK; +} +#define MDP4_DTV_UNDERFLOW_CLR_ENABLE_RECOVERY			0x80000000 + +#define REG_MDP4_DTV_HSYNC_SKEW					0x000d0048 + +#define REG_MDP4_DTV_TEST_CNTL					0x000d004c + +#define REG_MDP4_DTV_CTRL_POLARITY				0x000d0050 +#define MDP4_DTV_CTRL_POLARITY_HSYNC_LOW			0x00000001 +#define MDP4_DTV_CTRL_POLARITY_VSYNC_LOW			0x00000002 +#define MDP4_DTV_CTRL_POLARITY_DATA_EN_LOW			0x00000004 + +#define REG_MDP4_DSI						0x000e0000 + +#define REG_MDP4_DSI_ENABLE					0x000e0000 + +#define REG_MDP4_DSI_HSYNC_CTRL					0x000e0004 +#define MDP4_DSI_HSYNC_CTRL_PULSEW__MASK			0x0000ffff +#define MDP4_DSI_HSYNC_CTRL_PULSEW__SHIFT			0 +static inline uint32_t MDP4_DSI_HSYNC_CTRL_PULSEW(uint32_t val) +{ +	return ((val) << MDP4_DSI_HSYNC_CTRL_PULSEW__SHIFT) & MDP4_DSI_HSYNC_CTRL_PULSEW__MASK; +} +#define MDP4_DSI_HSYNC_CTRL_PERIOD__MASK			0xffff0000 +#define MDP4_DSI_HSYNC_CTRL_PERIOD__SHIFT			16 +static inline uint32_t MDP4_DSI_HSYNC_CTRL_PERIOD(uint32_t val) +{ +	return ((val) << MDP4_DSI_HSYNC_CTRL_PERIOD__SHIFT) & MDP4_DSI_HSYNC_CTRL_PERIOD__MASK; +} + +#define REG_MDP4_DSI_VSYNC_PERIOD				0x000e0008 + +#define REG_MDP4_DSI_VSYNC_LEN					0x000e000c + +#define REG_MDP4_DSI_DISPLAY_HCTRL				0x000e0010 +#define MDP4_DSI_DISPLAY_HCTRL_START__MASK			0x0000ffff +#define MDP4_DSI_DISPLAY_HCTRL_START__SHIFT			0 +static inline uint32_t MDP4_DSI_DISPLAY_HCTRL_START(uint32_t val) +{ +	return ((val) << MDP4_DSI_DISPLAY_HCTRL_START__SHIFT) & MDP4_DSI_DISPLAY_HCTRL_START__MASK; +} +#define MDP4_DSI_DISPLAY_HCTRL_END__MASK			0xffff0000 +#define MDP4_DSI_DISPLAY_HCTRL_END__SHIFT			16 +static inline uint32_t MDP4_DSI_DISPLAY_HCTRL_END(uint32_t val) +{ +	return ((val) << MDP4_DSI_DISPLAY_HCTRL_END__SHIFT) & MDP4_DSI_DISPLAY_HCTRL_END__MASK; +} + +#define REG_MDP4_DSI_DISPLAY_VSTART				0x000e0014 + +#define REG_MDP4_DSI_DISPLAY_VEND				0x000e0018 + +#define REG_MDP4_DSI_ACTIVE_HCTL				0x000e001c +#define MDP4_DSI_ACTIVE_HCTL_START__MASK			0x00007fff +#define MDP4_DSI_ACTIVE_HCTL_START__SHIFT			0 +static inline uint32_t MDP4_DSI_ACTIVE_HCTL_START(uint32_t val) +{ +	return ((val) << MDP4_DSI_ACTIVE_HCTL_START__SHIFT) & MDP4_DSI_ACTIVE_HCTL_START__MASK; +} +#define MDP4_DSI_ACTIVE_HCTL_END__MASK				0x7fff0000 +#define MDP4_DSI_ACTIVE_HCTL_END__SHIFT				16 +static inline uint32_t MDP4_DSI_ACTIVE_HCTL_END(uint32_t val) +{ +	return ((val) << MDP4_DSI_ACTIVE_HCTL_END__SHIFT) & MDP4_DSI_ACTIVE_HCTL_END__MASK; +} +#define MDP4_DSI_ACTIVE_HCTL_ACTIVE_START_X			0x80000000 + +#define REG_MDP4_DSI_ACTIVE_VSTART				0x000e0020 + +#define REG_MDP4_DSI_ACTIVE_VEND				0x000e0024 + +#define REG_MDP4_DSI_BORDER_CLR					0x000e0028 + +#define REG_MDP4_DSI_UNDERFLOW_CLR				0x000e002c +#define MDP4_DSI_UNDERFLOW_CLR_COLOR__MASK			0x00ffffff +#define MDP4_DSI_UNDERFLOW_CLR_COLOR__SHIFT			0 +static inline uint32_t MDP4_DSI_UNDERFLOW_CLR_COLOR(uint32_t val) +{ +	return ((val) << MDP4_DSI_UNDERFLOW_CLR_COLOR__SHIFT) & MDP4_DSI_UNDERFLOW_CLR_COLOR__MASK; +} +#define MDP4_DSI_UNDERFLOW_CLR_ENABLE_RECOVERY			0x80000000 + +#define REG_MDP4_DSI_HSYNC_SKEW					0x000e0030 + +#define REG_MDP4_DSI_TEST_CNTL					0x000e0034 + +#define REG_MDP4_DSI_CTRL_POLARITY				0x000e0038 +#define MDP4_DSI_CTRL_POLARITY_HSYNC_LOW			0x00000001 +#define MDP4_DSI_CTRL_POLARITY_VSYNC_LOW			0x00000002 +#define MDP4_DSI_CTRL_POLARITY_DATA_EN_LOW			0x00000004 + + +#endif /* MDP4_XML */ diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c new file mode 100644 index 00000000000..74cebb51e8c --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -0,0 +1,804 @@ +/* + * 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 "mdp4_kms.h" + +#include <drm/drm_mode.h> +#include "drm_crtc.h" +#include "drm_crtc_helper.h" +#include "drm_flip_work.h" + +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; +	bool enabled; + +	/* which mixer/encoder we route output to: */ +	int mixer; + +	struct { +		spinlock_t lock; +		bool stale; +		uint32_t width, height; +		uint32_t x, y; + +		/* next cursor to scan-out: */ +		uint32_t next_iova; +		struct drm_gem_object *next_bo; + +		/* current cursor being scanned out: */ +		struct drm_gem_object *scanout_bo; +	} cursor; + + +	/* 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; + +	/* for unref'ing cursor bo's after scanout completes: */ +	struct drm_flip_work unref_cursor_work; + +	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(to_mdp_kms(priv->kms)); +} + +static void request_pending(struct drm_crtc *crtc, uint32_t pending) +{ +	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); + +	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.primary->fb = new_fb; +	mdp4_crtc->fb = new_fb; + +	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); +} + +/* 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; +	struct drm_pending_vblank_event *event; +	unsigned long flags; + +	spin_lock_irqsave(&dev->event_lock, flags); +	event = mdp4_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)) { +			mdp4_crtc->event = NULL; +			drm_send_vblank_event(dev, mdp4_crtc->id, event); +		} +	} +	spin_unlock_irqrestore(&dev->event_lock, flags); +} + +static void pageflip_cb(struct msm_fence_cb *cb) +{ +	struct mdp4_crtc *mdp4_crtc = +		container_of(cb, struct mdp4_crtc, pageflip_cb); +	struct drm_crtc *crtc = &mdp4_crtc->base; +	struct drm_framebuffer *fb = crtc->primary->fb; + +	if (!fb) +		return; + +	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) +{ +	struct mdp4_crtc *mdp4_crtc = +		container_of(work, struct mdp4_crtc, unref_fb_work); +	struct drm_device *dev = mdp4_crtc->base.dev; + +	mutex_lock(&dev->mode_config.mutex); +	drm_framebuffer_unreference(val); +	mutex_unlock(&dev->mode_config.mutex); +} + +static void unref_cursor_worker(struct drm_flip_work *work, void *val) +{ +	struct mdp4_crtc *mdp4_crtc = +		container_of(work, struct mdp4_crtc, unref_cursor_work); +	struct mdp4_kms *mdp4_kms = get_kms(&mdp4_crtc->base); + +	msm_gem_put_iova(val, mdp4_kms->id); +	drm_gem_object_unreference_unlocked(val); +} + +static void mdp4_crtc_destroy(struct drm_crtc *crtc) +{ +	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); + +	drm_crtc_cleanup(crtc); +	drm_flip_work_cleanup(&mdp4_crtc->unref_fb_work); +	drm_flip_work_cleanup(&mdp4_crtc->unref_cursor_work); + +	kfree(mdp4_crtc); +} + +static void mdp4_crtc_dpms(struct drm_crtc *crtc, int mode) +{ +	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); +	struct mdp4_kms *mdp4_kms = get_kms(crtc); +	bool enabled = (mode == DRM_MODE_DPMS_ON); + +	DBG("%s: mode=%d", mdp4_crtc->name, mode); + +	if (enabled != mdp4_crtc->enabled) { +		if (enabled) { +			mdp4_enable(mdp4_kms); +			mdp_irq_register(&mdp4_kms->base, &mdp4_crtc->err); +		} else { +			mdp_irq_unregister(&mdp4_kms->base, &mdp4_crtc->err); +			mdp4_disable(mdp4_kms); +		} +		mdp4_crtc->enabled = enabled; +	} +} + +static bool mdp4_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 mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); +	struct mdp4_kms *mdp4_kms = get_kms(crtc); +	int i, ovlp = mdp4_crtc->ovlp; +	uint32_t mixer_cfg = 0; +	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++) { +		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); +	} + +	mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG, mixer_cfg); +} + +static int mdp4_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 mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); +	struct mdp4_kms *mdp4_kms = get_kms(crtc); +	enum mdp4_dma dma = mdp4_crtc->dma; +	int ret, ovlp = mdp4_crtc->ovlp; + +	mode = adjusted_mode; + +	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", +			mdp4_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 = 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)); + +	/* 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->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)); + +	mdp4_write(mdp4_kms, REG_MDP4_OVLP_BASE(ovlp), 0); +	mdp4_write(mdp4_kms, REG_MDP4_OVLP_SIZE(ovlp), +			MDP4_OVLP_SIZE_WIDTH(mode->hdisplay) | +			MDP4_OVLP_SIZE_HEIGHT(mode->vdisplay)); +	mdp4_write(mdp4_kms, REG_MDP4_OVLP_STRIDE(ovlp), +			crtc->primary->fb->pitches[0]); + +	mdp4_write(mdp4_kms, REG_MDP4_OVLP_CFG(ovlp), 1); + +	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; +} + +static void mdp4_crtc_prepare(struct drm_crtc *crtc) +{ +	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); +	DBG("%s", mdp4_crtc->name); +	/* make sure we hold a ref to mdp clks while setting up mode: */ +	mdp4_enable(get_kms(crtc)); +	mdp4_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void mdp4_crtc_commit(struct drm_crtc *crtc) +{ +	mdp4_crtc_dpms(crtc, DRM_MODE_DPMS_ON); +	crtc_flush(crtc); +	/* drop the ref to mdp clk's that we got in prepare: */ +	mdp4_disable(get_kms(crtc)); +} + +static int mdp4_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, +		struct drm_framebuffer *old_fb) +{ +	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; + +	/* grab extra ref for update_scanout() */ +	drm_framebuffer_reference(crtc->primary->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) +{ +} + +static int mdp4_crtc_page_flip(struct drm_crtc *crtc, +		struct drm_framebuffer *new_fb, +		struct drm_pending_vblank_event *event, +		uint32_t page_flip_flags) +{ +	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"); +		return -EBUSY; +	} + +	obj = msm_framebuffer_bo(new_fb, 0); + +	spin_lock_irqsave(&dev->event_lock, flags); +	mdp4_crtc->event = event; +	spin_unlock_irqrestore(&dev->event_lock, flags); + +	update_fb(crtc, new_fb); + +	return msm_gem_queue_inactive_cb(obj, &mdp4_crtc->pageflip_cb); +} + +static int mdp4_crtc_set_property(struct drm_crtc *crtc, +		struct drm_property *property, uint64_t val) +{ +	// XXX +	return -EINVAL; +} + +#define CURSOR_WIDTH 64 +#define CURSOR_HEIGHT 64 + +/* called from IRQ to update cursor related registers (if needed).  The + * cursor registers, other than x/y position, appear not to be double + * buffered, and changing them other than from vblank seems to trigger + * underflow. + */ +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 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; + +		if (next_bo) { +			/* take a obj ref + iova ref when we start scanning out: */ +			drm_gem_object_reference(next_bo); +			msm_gem_get_iova_locked(next_bo, mdp4_kms->id, &iova); + +			/* enable cursor: */ +			mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_SIZE(dma), +					MDP4_DMA_CURSOR_SIZE_WIDTH(mdp4_crtc->cursor.width) | +					MDP4_DMA_CURSOR_SIZE_HEIGHT(mdp4_crtc->cursor.height)); +			mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BASE(dma), iova); +			mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BLEND_CONFIG(dma), +					MDP4_DMA_CURSOR_BLEND_CONFIG_FORMAT(CURSOR_ARGB) | +					MDP4_DMA_CURSOR_BLEND_CONFIG_CURSOR_EN); +		} else { +			/* disable cursor: */ +			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: */ +		if (prev_bo) +			drm_flip_work_queue(&mdp4_crtc->unref_cursor_work, prev_bo); + +		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); +} + +static int mdp4_crtc_cursor_set(struct drm_crtc *crtc, +		struct drm_file *file_priv, uint32_t handle, +		uint32_t width, uint32_t height) +{ +	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); +	struct mdp4_kms *mdp4_kms = get_kms(crtc); +	struct drm_device *dev = crtc->dev; +	struct drm_gem_object *cursor_bo, *old_bo; +	unsigned long flags; +	uint32_t iova; +	int ret; + +	if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) { +		dev_err(dev->dev, "bad cursor size: %dx%d\n", width, height); +		return -EINVAL; +	} + +	if (handle) { +		cursor_bo = drm_gem_object_lookup(dev, file_priv, handle); +		if (!cursor_bo) +			return -ENOENT; +	} else { +		cursor_bo = NULL; +	} + +	if (cursor_bo) { +		ret = msm_gem_get_iova(cursor_bo, mdp4_kms->id, &iova); +		if (ret) +			goto fail; +	} else { +		iova = 0; +	} + +	spin_lock_irqsave(&mdp4_crtc->cursor.lock, flags); +	old_bo = mdp4_crtc->cursor.next_bo; +	mdp4_crtc->cursor.next_bo   = cursor_bo; +	mdp4_crtc->cursor.next_iova = iova; +	mdp4_crtc->cursor.width     = width; +	mdp4_crtc->cursor.height    = height; +	mdp4_crtc->cursor.stale     = true; +	spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags); + +	if (old_bo) { +		/* drop our previous reference: */ +		drm_flip_work_queue(&mdp4_crtc->unref_cursor_work, old_bo); +	} + +	request_pending(crtc, PENDING_CURSOR); + +	return 0; + +fail: +	drm_gem_object_unreference_unlocked(cursor_bo); +	return ret; +} + +static int mdp4_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ +	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); +	unsigned long flags; + +	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; +} + +static const struct drm_crtc_funcs mdp4_crtc_funcs = { +	.set_config = drm_crtc_helper_set_config, +	.destroy = mdp4_crtc_destroy, +	.page_flip = mdp4_crtc_page_flip, +	.set_property = mdp4_crtc_set_property, +	.cursor_set = mdp4_crtc_cursor_set, +	.cursor_move = mdp4_crtc_cursor_move, +}; + +static const struct drm_crtc_helper_funcs mdp4_crtc_helper_funcs = { +	.dpms = mdp4_crtc_dpms, +	.mode_fixup = mdp4_crtc_mode_fixup, +	.mode_set = mdp4_crtc_mode_set, +	.prepare = mdp4_crtc_prepare, +	.commit = mdp4_crtc_commit, +	.mode_set_base = mdp4_crtc_mode_set_base, +	.load_lut = mdp4_crtc_load_lut, +}; + +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; + +	mdp_irq_unregister(&get_kms(crtc)->base, &mdp4_crtc->vblank); + +	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 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; +	DBG("%s: error: %08x", mdp4_crtc->name, irqstatus); +	crtc_flush(crtc); +} + +uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc) +{ +	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); +	return mdp4_crtc->vblank.irqmask; +} + +void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file) +{ +	DBG("cancel: %p", file); +	complete_flip(crtc, file); +} + +/* set dma config, ie. the format the encoder wants. */ +void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config) +{ +	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); +	struct mdp4_kms *mdp4_kms = get_kms(crtc); + +	mdp4_write(mdp4_kms, REG_MDP4_DMA_CONFIG(mdp4_crtc->dma), config); +} + +/* set interface for routing crtc->encoder: */ +void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf) +{ +	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); +	struct mdp4_kms *mdp4_kms = get_kms(crtc); +	uint32_t intf_sel; + +	intf_sel = mdp4_read(mdp4_kms, REG_MDP4_DISP_INTF_SEL); + +	switch (mdp4_crtc->dma) { +	case DMA_P: +		intf_sel &= ~MDP4_DISP_INTF_SEL_PRIM__MASK; +		intf_sel |= MDP4_DISP_INTF_SEL_PRIM(intf); +		break; +	case DMA_S: +		intf_sel &= ~MDP4_DISP_INTF_SEL_SEC__MASK; +		intf_sel |= MDP4_DISP_INTF_SEL_SEC(intf); +		break; +	case DMA_E: +		intf_sel &= ~MDP4_DISP_INTF_SEL_EXT__MASK; +		intf_sel |= MDP4_DISP_INTF_SEL_EXT(intf); +		break; +	} + +	if (intf == INTF_DSI_VIDEO) { +		intf_sel &= ~MDP4_DISP_INTF_SEL_DSI_CMD; +		intf_sel |= MDP4_DISP_INTF_SEL_DSI_VIDEO; +		mdp4_crtc->mixer = 0; +	} else if (intf == INTF_DSI_CMD) { +		intf_sel &= ~MDP4_DISP_INTF_SEL_DSI_VIDEO; +		intf_sel |= MDP4_DISP_INTF_SEL_DSI_CMD; +		mdp4_crtc->mixer = 0; +	} else if (intf == INTF_LCDC_DTV){ +		mdp4_crtc->mixer = 1; +	} + +	blend_setup(crtc); + +	DBG("%s: intf_sel=%08x", mdp4_crtc->name, intf_sel); + +	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", +}; + +/* initialize crtc */ +struct drm_crtc *mdp4_crtc_init(struct drm_device *dev, +		struct drm_plane *plane, int id, int ovlp_id, +		enum mdp4_dma dma_id) +{ +	struct drm_crtc *crtc = NULL; +	struct mdp4_crtc *mdp4_crtc; +	int ret; + +	mdp4_crtc = kzalloc(sizeof(*mdp4_crtc), GFP_KERNEL); +	if (!mdp4_crtc) { +		ret = -ENOMEM; +		goto fail; +	} + +	crtc = &mdp4_crtc->base; + +	mdp4_crtc->plane = plane; +	mdp4_crtc->id = id; + +	mdp4_crtc->ovlp = ovlp_id; +	mdp4_crtc->dma = dma_id; + +	mdp4_crtc->vblank.irqmask = dma2irq(mdp4_crtc->dma); +	mdp4_crtc->vblank.irq = mdp4_crtc_vblank_irq; + +	mdp4_crtc->err.irqmask = dma2err(mdp4_crtc->dma); +	mdp4_crtc->err.irq = mdp4_crtc_err_irq; + +	snprintf(mdp4_crtc->name, sizeof(mdp4_crtc->name), "%s:%d", +			dma_names[dma_id], ovlp_id); + +	spin_lock_init(&mdp4_crtc->cursor.lock); + +	ret = drm_flip_work_init(&mdp4_crtc->unref_fb_work, 16, +			"unref fb", unref_fb_worker); +	if (ret) +		goto fail; + +	ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64, +			"unref cursor", unref_cursor_worker); + +	INIT_FENCE_CB(&mdp4_crtc->pageflip_cb, pageflip_cb); + +	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); + +	return crtc; + +fail: +	if (crtc) +		mdp4_crtc_destroy(crtc); + +	return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c new file mode 100644 index 00000000000..067ed03b35f --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c @@ -0,0 +1,303 @@ +/* + * 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 "mdp4_kms.h" + +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + + +struct mdp4_dtv_encoder { +	struct drm_encoder base; +	struct clk *src_clk; +	struct clk *hdmi_clk; +	struct clk *mdp_clk; +	unsigned long int pixclock; +	bool enabled; +	uint32_t bsc; +}; +#define to_mdp4_dtv_encoder(x) container_of(x, struct mdp4_dtv_encoder, base) + +static struct mdp4_kms *get_kms(struct drm_encoder *encoder) +{ +	struct msm_drm_private *priv = encoder->dev->dev_private; +	return to_mdp4_kms(to_mdp_kms(priv->kms)); +} + +#ifdef CONFIG_MSM_BUS_SCALING +#include <mach/board.h> +/* not ironically named at all.. no, really.. */ +static void bs_init(struct mdp4_dtv_encoder *mdp4_dtv_encoder) +{ +	struct drm_device *dev = mdp4_dtv_encoder->base.dev; +	struct lcdc_platform_data *dtv_pdata = mdp4_find_pdata("dtv.0"); + +	if (!dtv_pdata) { +		dev_err(dev->dev, "could not find dtv pdata\n"); +		return; +	} + +	if (dtv_pdata->bus_scale_table) { +		mdp4_dtv_encoder->bsc = msm_bus_scale_register_client( +				dtv_pdata->bus_scale_table); +		DBG("bus scale client: %08x", mdp4_dtv_encoder->bsc); +		DBG("lcdc_power_save: %p", dtv_pdata->lcdc_power_save); +		if (dtv_pdata->lcdc_power_save) +			dtv_pdata->lcdc_power_save(1); +	} +} + +static void bs_fini(struct mdp4_dtv_encoder *mdp4_dtv_encoder) +{ +	if (mdp4_dtv_encoder->bsc) { +		msm_bus_scale_unregister_client(mdp4_dtv_encoder->bsc); +		mdp4_dtv_encoder->bsc = 0; +	} +} + +static void bs_set(struct mdp4_dtv_encoder *mdp4_dtv_encoder, int idx) +{ +	if (mdp4_dtv_encoder->bsc) { +		DBG("set bus scaling: %d", idx); +		msm_bus_scale_client_update_request(mdp4_dtv_encoder->bsc, idx); +	} +} +#else +static void bs_init(struct mdp4_dtv_encoder *mdp4_dtv_encoder) {} +static void bs_fini(struct mdp4_dtv_encoder *mdp4_dtv_encoder) {} +static void bs_set(struct mdp4_dtv_encoder *mdp4_dtv_encoder, int idx) {} +#endif + +static void mdp4_dtv_encoder_destroy(struct drm_encoder *encoder) +{ +	struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder); +	bs_fini(mdp4_dtv_encoder); +	drm_encoder_cleanup(encoder); +	kfree(mdp4_dtv_encoder); +} + +static const struct drm_encoder_funcs mdp4_dtv_encoder_funcs = { +	.destroy = mdp4_dtv_encoder_destroy, +}; + +static void mdp4_dtv_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +	struct drm_device *dev = encoder->dev; +	struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder); +	struct mdp4_kms *mdp4_kms = get_kms(encoder); +	bool enabled = (mode == DRM_MODE_DPMS_ON); + +	DBG("mode=%d", mode); + +	if (enabled == mdp4_dtv_encoder->enabled) +		return; + +	if (enabled) { +		unsigned long pc = mdp4_dtv_encoder->pixclock; +		int ret; + +		bs_set(mdp4_dtv_encoder, 1); + +		DBG("setting src_clk=%lu", pc); + +		ret = clk_set_rate(mdp4_dtv_encoder->src_clk, pc); +		if (ret) +			dev_err(dev->dev, "failed to set src_clk to %lu: %d\n", pc, ret); +		clk_prepare_enable(mdp4_dtv_encoder->src_clk); +		ret = clk_prepare_enable(mdp4_dtv_encoder->hdmi_clk); +		if (ret) +			dev_err(dev->dev, "failed to enable hdmi_clk: %d\n", ret); +		ret = clk_prepare_enable(mdp4_dtv_encoder->mdp_clk); +		if (ret) +			dev_err(dev->dev, "failed to enabled mdp_clk: %d\n", ret); + +		mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 1); +	} else { +		mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0); + +		/* +		 * Wait for a vsync so we know the ENABLE=0 latched before +		 * the (connector) source of the vsync's gets disabled, +		 * otherwise we end up in a funny state if we re-enable +		 * before the disable latches, which results that some of +		 * the settings changes for the new modeset (like new +		 * scanout buffer) don't latch properly.. +		 */ +		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); +		clk_disable_unprepare(mdp4_dtv_encoder->mdp_clk); + +		bs_set(mdp4_dtv_encoder, 0); +	} + +	mdp4_dtv_encoder->enabled = enabled; +} + +static bool mdp4_dtv_encoder_mode_fixup(struct drm_encoder *encoder, +		const struct drm_display_mode *mode, +		struct drm_display_mode *adjusted_mode) +{ +	return true; +} + +static void mdp4_dtv_encoder_mode_set(struct drm_encoder *encoder, +		struct drm_display_mode *mode, +		struct drm_display_mode *adjusted_mode) +{ +	struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder); +	struct mdp4_kms *mdp4_kms = get_kms(encoder); +	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; + +	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); + +	mdp4_dtv_encoder->pixclock = mode->clock * 1000; + +	DBG("pixclock=%lu", mdp4_dtv_encoder->pixclock); + +	ctrl_pol = 0; +	if (mode->flags & DRM_MODE_FLAG_NHSYNC) +		ctrl_pol |= MDP4_DTV_CTRL_POLARITY_HSYNC_LOW; +	if (mode->flags & DRM_MODE_FLAG_NVSYNC) +		ctrl_pol |= MDP4_DTV_CTRL_POLARITY_VSYNC_LOW; +	/* probably need to get DATA_EN polarity from panel.. */ + +	dtv_hsync_skew = 0;  /* 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; + +	mdp4_write(mdp4_kms, REG_MDP4_DTV_HSYNC_CTRL, +			MDP4_DTV_HSYNC_CTRL_PULSEW(mode->hsync_end - mode->hsync_start) | +			MDP4_DTV_HSYNC_CTRL_PERIOD(mode->htotal)); +	mdp4_write(mdp4_kms, REG_MDP4_DTV_VSYNC_PERIOD, vsync_period); +	mdp4_write(mdp4_kms, REG_MDP4_DTV_VSYNC_LEN, vsync_len); +	mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_HCTRL, +			MDP4_DTV_DISPLAY_HCTRL_START(hsync_start_x) | +			MDP4_DTV_DISPLAY_HCTRL_END(hsync_end_x)); +	mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_VSTART, display_v_start); +	mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_VEND, display_v_end); +	mdp4_write(mdp4_kms, REG_MDP4_DTV_BORDER_CLR, 0); +	mdp4_write(mdp4_kms, REG_MDP4_DTV_UNDERFLOW_CLR, +			MDP4_DTV_UNDERFLOW_CLR_ENABLE_RECOVERY | +			MDP4_DTV_UNDERFLOW_CLR_COLOR(0xff)); +	mdp4_write(mdp4_kms, REG_MDP4_DTV_HSYNC_SKEW, dtv_hsync_skew); +	mdp4_write(mdp4_kms, REG_MDP4_DTV_CTRL_POLARITY, ctrl_pol); +	mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_HCTL, +			MDP4_DTV_ACTIVE_HCTL_START(0) | +			MDP4_DTV_ACTIVE_HCTL_END(0)); +	mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_VSTART, 0); +	mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_VEND, 0); +} + +static void mdp4_dtv_encoder_prepare(struct drm_encoder *encoder) +{ +	mdp4_dtv_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void mdp4_dtv_encoder_commit(struct drm_encoder *encoder) +{ +	mdp4_crtc_set_config(encoder->crtc, +			MDP4_DMA_CONFIG_R_BPC(BPC8) | +			MDP4_DMA_CONFIG_G_BPC(BPC8) | +			MDP4_DMA_CONFIG_B_BPC(BPC8) | +			MDP4_DMA_CONFIG_PACK(0x21)); +	mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV); +	mdp4_dtv_encoder_dpms(encoder, DRM_MODE_DPMS_ON); +} + +static const struct drm_encoder_helper_funcs mdp4_dtv_encoder_helper_funcs = { +	.dpms = mdp4_dtv_encoder_dpms, +	.mode_fixup = mdp4_dtv_encoder_mode_fixup, +	.mode_set = mdp4_dtv_encoder_mode_set, +	.prepare = mdp4_dtv_encoder_prepare, +	.commit = mdp4_dtv_encoder_commit, +}; + +long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate) +{ +	struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder); +	return clk_round_rate(mdp4_dtv_encoder->src_clk, rate); +} + +/* initialize encoder */ +struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev) +{ +	struct drm_encoder *encoder = NULL; +	struct mdp4_dtv_encoder *mdp4_dtv_encoder; +	int ret; + +	mdp4_dtv_encoder = kzalloc(sizeof(*mdp4_dtv_encoder), GFP_KERNEL); +	if (!mdp4_dtv_encoder) { +		ret = -ENOMEM; +		goto fail; +	} + +	encoder = &mdp4_dtv_encoder->base; + +	drm_encoder_init(dev, encoder, &mdp4_dtv_encoder_funcs, +			 DRM_MODE_ENCODER_TMDS); +	drm_encoder_helper_add(encoder, &mdp4_dtv_encoder_helper_funcs); + +	mdp4_dtv_encoder->src_clk = devm_clk_get(dev->dev, "src_clk"); +	if (IS_ERR(mdp4_dtv_encoder->src_clk)) { +		dev_err(dev->dev, "failed to get src_clk\n"); +		ret = PTR_ERR(mdp4_dtv_encoder->src_clk); +		goto fail; +	} + +	mdp4_dtv_encoder->hdmi_clk = devm_clk_get(dev->dev, "hdmi_clk"); +	if (IS_ERR(mdp4_dtv_encoder->hdmi_clk)) { +		dev_err(dev->dev, "failed to get hdmi_clk\n"); +		ret = PTR_ERR(mdp4_dtv_encoder->hdmi_clk); +		goto fail; +	} + +	mdp4_dtv_encoder->mdp_clk = devm_clk_get(dev->dev, "mdp_clk"); +	if (IS_ERR(mdp4_dtv_encoder->mdp_clk)) { +		dev_err(dev->dev, "failed to get mdp_clk\n"); +		ret = PTR_ERR(mdp4_dtv_encoder->mdp_clk); +		goto fail; +	} + +	bs_init(mdp4_dtv_encoder); + +	return encoder; + +fail: +	if (encoder) +		mdp4_dtv_encoder_destroy(encoder); + +	return ERR_PTR(ret); +} 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/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c new file mode 100644 index 00000000000..0bb4faa1752 --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -0,0 +1,418 @@ +/* + * 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 "mdp4_kms.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(to_mdp_kms(kms)); +	struct drm_device *dev = mdp4_kms->dev; +	uint32_t version, major, minor, dmap_cfg, vg_cfg; +	unsigned long clk; +	int ret = 0; + +	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 MDP4 version v%d.%d", major, minor); + +	if (major != 4) { +		dev_err(dev->dev, "unexpected MDP version: v%d.%d\n", +				major, minor); +		ret = -ENXIO; +		goto out; +	} + +	mdp4_kms->rev = minor; + +	if (mdp4_kms->dsi_pll_vdda) { +		if ((mdp4_kms->rev == 2) || (mdp4_kms->rev == 4)) { +			ret = regulator_set_voltage(mdp4_kms->dsi_pll_vdda, +					1200000, 1200000); +			if (ret) { +				dev_err(dev->dev, +					"failed to set dsi_pll_vdda voltage: %d\n", ret); +				goto out; +			} +		} +	} + +	if (mdp4_kms->dsi_pll_vddio) { +		if (mdp4_kms->rev == 2) { +			ret = regulator_set_voltage(mdp4_kms->dsi_pll_vddio, +					1800000, 1800000); +			if (ret) { +				dev_err(dev->dev, +					"failed to set dsi_pll_vddio voltage: %d\n", ret); +				goto out; +			} +		} +	} + +	if (mdp4_kms->rev > 1) { +		mdp4_write(mdp4_kms, REG_MDP4_CS_CONTROLLER0, 0x0707ffff); +		mdp4_write(mdp4_kms, REG_MDP4_CS_CONTROLLER1, 0x03073f3f); +	} + +	mdp4_write(mdp4_kms, REG_MDP4_PORTMAP_MODE, 0x3); + +	/* max read pending cmd config, 3 pending requests: */ +	mdp4_write(mdp4_kms, REG_MDP4_READ_CNFG, 0x02222); + +	clk = clk_get_rate(mdp4_kms->clk); + +	if ((mdp4_kms->rev >= 1) || (clk >= 90000000)) { +		dmap_cfg = 0x47;     /* 16 bytes-burst x 8 req */ +		vg_cfg = 0x47;       /* 16 bytes-burs x 8 req */ +	} else { +		dmap_cfg = 0x27;     /* 8 bytes-burst x 8 req */ +		vg_cfg = 0x43;       /* 16 bytes-burst x 4 req */ +	} + +	DBG("fetch config: dmap=%02x, vg=%02x", dmap_cfg, vg_cfg); + +	mdp4_write(mdp4_kms, REG_MDP4_DMA_FETCH_CONFIG(DMA_P), dmap_cfg); +	mdp4_write(mdp4_kms, REG_MDP4_DMA_FETCH_CONFIG(DMA_E), dmap_cfg); + +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(VG1), vg_cfg); +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(VG2), vg_cfg); +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(RGB1), vg_cfg); +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(RGB2), vg_cfg); + +	if (mdp4_kms->rev >= 2) +		mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG_UPDATE_METHOD, 1); + +	/* disable CSC matrix / YUV by default: */ +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG1), 0); +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG2), 0); +	mdp4_write(mdp4_kms, REG_MDP4_DMA_P_OP_MODE, 0); +	mdp4_write(mdp4_kms, REG_MDP4_DMA_S_OP_MODE, 0); +	mdp4_write(mdp4_kms, REG_MDP4_OVLP_CSC_CONFIG(1), 0); +	mdp4_write(mdp4_kms, REG_MDP4_OVLP_CSC_CONFIG(2), 0); + +	if (mdp4_kms->rev > 1) +		mdp4_write(mdp4_kms, REG_MDP4_RESET_STATUS, 1); + +out: +	pm_runtime_put_sync(dev->dev); + +	return ret; +} + +static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate, +		struct drm_encoder *encoder) +{ +	/* if we had >1 encoder, we'd need something more clever: */ +	return mdp4_dtv_round_pixclk(encoder, rate); +} + +static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file) +{ +	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], file); +} + +static void mdp4_destroy(struct msm_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 mdp_kms_funcs kms_funcs = { +	.base = { +		.hw_init         = mdp4_hw_init, +		.irq_preinstall  = mdp4_irq_preinstall, +		.irq_postinstall = mdp4_irq_postinstall, +		.irq_uninstall   = mdp4_irq_uninstall, +		.irq             = mdp4_irq, +		.enable_vblank   = mdp4_enable_vblank, +		.disable_vblank  = mdp4_disable_vblank, +		.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) +{ +	DBG(""); + +	clk_disable_unprepare(mdp4_kms->clk); +	if (mdp4_kms->pclk) +		clk_disable_unprepare(mdp4_kms->pclk); +	clk_disable_unprepare(mdp4_kms->lut_clk); + +	return 0; +} + +int mdp4_enable(struct mdp4_kms *mdp4_kms) +{ +	DBG(""); + +	clk_prepare_enable(mdp4_kms->clk); +	if (mdp4_kms->pclk) +		clk_prepare_enable(mdp4_kms->pclk); +	clk_prepare_enable(mdp4_kms->lut_clk); + +	return 0; +} + +static int modeset_init(struct mdp4_kms *mdp4_kms) +{ +	struct drm_device *dev = mdp4_kms->dev; +	struct msm_drm_private *priv = dev->dev_private; +	struct drm_plane *plane; +	struct drm_crtc *crtc; +	struct drm_encoder *encoder; +	struct hdmi *hdmi; +	int ret; + +	/* +	 *  NOTE: this is a bit simplistic until we add support +	 * 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)) { +		dev_err(dev->dev, "failed to construct plane for RGB1\n"); +		ret = PTR_ERR(plane); +		goto fail; +	} + +	crtc  = mdp4_crtc_init(dev, plane, priv->num_crtcs, 1, DMA_E); +	if (IS_ERR(crtc)) { +		dev_err(dev->dev, "failed to construct crtc for DMA_E\n"); +		ret = PTR_ERR(crtc); +		goto fail; +	} +	priv->crtcs[priv->num_crtcs++] = crtc; + +	encoder = mdp4_dtv_encoder_init(dev); +	if (IS_ERR(encoder)) { +		dev_err(dev->dev, "failed to construct DTV encoder\n"); +		ret = PTR_ERR(encoder); +		goto fail; +	} +	encoder->possible_crtcs = 0x1;     /* DTV can be hooked to DMA_E */ +	priv->encoders[priv->num_encoders++] = encoder; + +	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; +	} + +	return 0; + +fail: +	return ret; +} + +static const char *iommu_ports[] = { +		"mdp_port0_cb0", "mdp_port1_cb0", +}; + +struct msm_kms *mdp4_kms_init(struct drm_device *dev) +{ +	struct platform_device *pdev = dev->platformdev; +	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); +	if (!mdp4_kms) { +		dev_err(dev->dev, "failed to allocate kms\n"); +		ret = -ENOMEM; +		goto fail; +	} + +	mdp_kms_init(&mdp4_kms->base, &kms_funcs); + +	kms = &mdp4_kms->base.base; + +	mdp4_kms->dev = dev; + +	mdp4_kms->mmio = msm_ioremap(pdev, NULL, "MDP4"); +	if (IS_ERR(mdp4_kms->mmio)) { +		ret = PTR_ERR(mdp4_kms->mmio); +		goto fail; +	} + +	mdp4_kms->dsi_pll_vdda = devm_regulator_get(&pdev->dev, "dsi_pll_vdda"); +	if (IS_ERR(mdp4_kms->dsi_pll_vdda)) +		mdp4_kms->dsi_pll_vdda = NULL; + +	mdp4_kms->dsi_pll_vddio = devm_regulator_get(&pdev->dev, "dsi_pll_vddio"); +	if (IS_ERR(mdp4_kms->dsi_pll_vddio)) +		mdp4_kms->dsi_pll_vddio = NULL; + +	mdp4_kms->vdd = devm_regulator_get(&pdev->dev, "vdd"); +	if (IS_ERR(mdp4_kms->vdd)) +		mdp4_kms->vdd = NULL; + +	if (mdp4_kms->vdd) { +		ret = regulator_enable(mdp4_kms->vdd); +		if (ret) { +			dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret); +			goto fail; +		} +	} + +	mdp4_kms->clk = devm_clk_get(&pdev->dev, "core_clk"); +	if (IS_ERR(mdp4_kms->clk)) { +		dev_err(dev->dev, "failed to get core_clk\n"); +		ret = PTR_ERR(mdp4_kms->clk); +		goto fail; +	} + +	mdp4_kms->pclk = devm_clk_get(&pdev->dev, "iface_clk"); +	if (IS_ERR(mdp4_kms->pclk)) +		mdp4_kms->pclk = NULL; + +	// XXX if (rev >= MDP_REV_42) { ??? +	mdp4_kms->lut_clk = devm_clk_get(&pdev->dev, "lut_clk"); +	if (IS_ERR(mdp4_kms->lut_clk)) { +		dev_err(dev->dev, "failed to get lut_clk\n"); +		ret = PTR_ERR(mdp4_kms->lut_clk); +		goto fail; +	} + +	clk_set_rate(mdp4_kms->clk, config->max_clk); +	clk_set_rate(mdp4_kms->lut_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): +	 */ +	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); + +	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_mmu(dev, mmu); +	if (mdp4_kms->id < 0) { +		ret = mdp4_kms->id; +		dev_err(dev->dev, "failed to register mdp4 iommu: %d\n", ret); +		goto fail; +	} + +	ret = modeset_init(mdp4_kms); +	if (ret) { +		dev_err(dev->dev, "modeset_init failed: %d\n", ret); +		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: +	if (kms) +		mdp4_destroy(kms); +	return ERR_PTR(ret); +} + +static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev) +{ +	static struct mdp4_platform_config config = {}; +#ifdef CONFIG_OF +	/* TODO */ +#else +	if (cpu_is_apq8064()) +		config.max_clk = 266667000; +	else +		config.max_clk = 200000000; + +	config.iommu = msm_get_iommu_domain(DISPLAY_READ_DOMAIN); +#endif +	return &config; +} diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h new file mode 100644 index 00000000000..715520c54cd --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h @@ -0,0 +1,218 @@ +/* + * 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 __MDP4_KMS_H__ +#define __MDP4_KMS_H__ + +#include "msm_drv.h" +#include "msm_kms.h" +#include "mdp/mdp_kms.h" +#include "mdp4.xml.h" + +struct mdp4_kms { +	struct mdp_kms base; + +	struct drm_device *dev; + +	int rev; + +	/* mapper-id used to request GEM buffer mapped for scanout: */ +	int id; + +	void __iomem *mmio; + +	struct regulator *dsi_pll_vdda; +	struct regulator *dsi_pll_vddio; +	struct regulator *vdd; + +	struct clk *clk; +	struct clk *pclk; +	struct clk *lut_clk; + +	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) + +/* platform config data (ie. from DT, or pdata) */ +struct mdp4_platform_config { +	struct iommu_domain *iommu; +	uint32_t max_clk; +}; + +static inline void mdp4_write(struct mdp4_kms *mdp4_kms, u32 reg, u32 data) +{ +	msm_writel(data, mdp4_kms->mmio + reg); +} + +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 mdp4_pipe pipe) +{ +	switch (pipe) { +	case VG1:      return MDP4_OVERLAY_FLUSH_VG1; +	case VG2:      return MDP4_OVERLAY_FLUSH_VG2; +	case RGB1:     return MDP4_OVERLAY_FLUSH_RGB1; +	case RGB2:     return MDP4_OVERLAY_FLUSH_RGB1; +	default:       return 0; +	} +} + +static inline uint32_t ovlp2flush(int ovlp) +{ +	switch (ovlp) { +	case 0:        return MDP4_OVERLAY_FLUSH_OVLP0; +	case 1:        return MDP4_OVERLAY_FLUSH_OVLP1; +	default:       return 0; +	} +} + +static inline uint32_t dma2irq(enum mdp4_dma dma) +{ +	switch (dma) { +	case DMA_P:    return MDP4_IRQ_DMA_P_DONE; +	case DMA_S:    return MDP4_IRQ_DMA_S_DONE; +	case DMA_E:    return MDP4_IRQ_DMA_E_DONE; +	default:       return 0; +	} +} + +static inline uint32_t dma2err(enum mdp4_dma dma) +{ +	switch (dma) { +	case DMA_P:    return MDP4_IRQ_PRIMARY_INTF_UDERRUN; +	case DMA_S:    return 0;  // ??? +	case DMA_E:    return MDP4_IRQ_EXTERNAL_INTF_UDERRUN; +	default:       return 0; +	} +} + +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); +int mdp4_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc); +void mdp4_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc); + +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); +void mdp4_plane_set_scanout(struct drm_plane *plane, +		struct drm_framebuffer *fb); +int mdp4_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); +enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane); +struct drm_plane *mdp4_plane_init(struct drm_device *dev, +		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, 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); + +long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate); +struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev); + +#ifdef CONFIG_MSM_BUS_SCALING +static inline int match_dev_name(struct device *dev, void *data) +{ +	return !strcmp(dev_name(dev), data); +} +/* bus scaling data is associated with extra pointless platform devices, + * "dtv", etc.. this is a bit of a hack, but we need a way for encoders + * to find their pdata to make the bus-scaling stuff work. + */ +static inline void *mdp4_find_pdata(const char *devname) +{ +	struct device *dev; +	dev = bus_find_device(&platform_bus_type, NULL, +			(void *)devname, match_dev_name); +	return dev ? dev->platform_data : NULL; +} +#endif + +#endif /* __MDP4_KMS_H__ */ diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c new file mode 100644 index 00000000000..66f33dba1eb --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -0,0 +1,255 @@ +/* + * 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 "mdp4_kms.h" + + +struct mdp4_plane { +	struct drm_plane base; +	const char *name; + +	enum mdp4_pipe pipe; + +	uint32_t nformats; +	uint32_t formats[32]; + +	bool enabled; +}; +#define to_mdp4_plane(x) container_of(x, struct mdp4_plane, base) + +static struct mdp4_kms *get_kms(struct drm_plane *plane) +{ +	struct msm_drm_private *priv = plane->dev->dev_private; +	return to_mdp4_kms(to_mdp_kms(priv->kms)); +} + +static int mdp4_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 mdp4_plane *mdp4_plane = to_mdp4_plane(plane); + +	mdp4_plane->enabled = true; + +	if (plane->fb) +		drm_framebuffer_unreference(plane->fb); + +	drm_framebuffer_reference(fb); + +	return mdp4_plane_mode_set(plane, crtc, fb, +			crtc_x, crtc_y, crtc_w, crtc_h, +			src_x, src_y, src_w, src_h); +} + +static int mdp4_plane_disable(struct drm_plane *plane) +{ +	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); +	DBG("%s: disable", mdp4_plane->name); +	if (plane->crtc) +		mdp4_crtc_detach(plane->crtc, plane); +	return 0; +} + +static void mdp4_plane_destroy(struct drm_plane *plane) +{ +	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); + +	mdp4_plane_disable(plane); +	drm_plane_cleanup(plane); + +	kfree(mdp4_plane); +} + +/* helper to install properties which are common to planes and crtcs */ +void mdp4_plane_install_properties(struct drm_plane *plane, +		struct drm_mode_object *obj) +{ +	// XXX +} + +int mdp4_plane_set_property(struct drm_plane *plane, +		struct drm_property *property, uint64_t val) +{ +	// XXX +	return -EINVAL; +} + +static const struct drm_plane_funcs mdp4_plane_funcs = { +		.update_plane = mdp4_plane_update, +		.disable_plane = mdp4_plane_disable, +		.destroy = mdp4_plane_destroy, +		.set_property = mdp4_plane_set_property, +}; + +void mdp4_plane_set_scanout(struct drm_plane *plane, +		struct drm_framebuffer *fb) +{ +	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); +	struct mdp4_kms *mdp4_kms = get_kms(plane); +	enum mdp4_pipe pipe = mdp4_plane->pipe; +	uint32_t iova; + +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_A(pipe), +			MDP4_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) | +			MDP4_PIPE_SRC_STRIDE_A_P1(fb->pitches[1])); + +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_B(pipe), +			MDP4_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) | +			MDP4_PIPE_SRC_STRIDE_B_P3(fb->pitches[3])); + +	msm_gem_get_iova(msm_framebuffer_bo(fb, 0), mdp4_kms->id, &iova); +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP0_BASE(pipe), iova); + +	plane->fb = fb; +} + +#define MDP4_VG_PHASE_STEP_DEFAULT	0x20000000 + +int mdp4_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 mdp4_plane *mdp4_plane = to_mdp4_plane(plane); +	struct mdp4_kms *mdp4_kms = get_kms(plane); +	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; + +	/* 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", 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 */ +	} + +	if (src_h != crtc_h) { +		op_mode |= MDP4_PIPE_OP_MODE_SCALEY_EN; +		/* TODO calc phasey_step */ +	} + +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_SIZE(pipe), +			MDP4_PIPE_SRC_SIZE_WIDTH(src_w) | +			MDP4_PIPE_SRC_SIZE_HEIGHT(src_h)); + +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_XY(pipe), +			MDP4_PIPE_SRC_XY_X(src_x) | +			MDP4_PIPE_SRC_XY_Y(src_y)); + +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_DST_SIZE(pipe), +			MDP4_PIPE_DST_SIZE_WIDTH(crtc_w) | +			MDP4_PIPE_DST_SIZE_HEIGHT(crtc_h)); + +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_DST_XY(pipe), +			MDP4_PIPE_DST_XY_X(crtc_x) | +			MDP4_PIPE_DST_XY_Y(crtc_y)); + +	mdp4_plane_set_scanout(plane, 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) | +			MDP4_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) | +			MDP4_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) | +			MDP4_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) | +			COND(format->alpha_enable, MDP4_PIPE_SRC_FORMAT_ALPHA_ENABLE) | +			MDP4_PIPE_SRC_FORMAT_CPP(format->cpp - 1) | +			MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) | +			COND(format->unpack_tight, MDP4_PIPE_SRC_FORMAT_UNPACK_TIGHT)); + +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_UNPACK(pipe), +			MDP4_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) | +			MDP4_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) | +			MDP4_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) | +			MDP4_PIPE_SRC_UNPACK_ELEM3(format->unpack[3])); + +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(pipe), op_mode); +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEX_STEP(pipe), phasex_step); +	mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEY_STEP(pipe), phasey_step); + +	/* TODO detach from old crtc (if we had more than one) */ +	mdp4_crtc_attach(crtc, plane); + +	return 0; +} + +static const char *pipe_names[] = { +		"VG1", "VG2", +		"RGB1", "RGB2", "RGB3", +		"VG3", "VG4", +}; + +enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane) +{ +	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); +	return mdp4_plane->pipe; +} + +/* initialize plane */ +struct drm_plane *mdp4_plane_init(struct drm_device *dev, +		enum mdp4_pipe pipe_id, bool private_plane) +{ +	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) { +		ret = -ENOMEM; +		goto fail; +	} + +	plane = &mdp4_plane->base; + +	mdp4_plane->pipe = pipe_id; +	mdp4_plane->name = pipe_names[pipe_id]; + +	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); + +	return plane; + +fail: +	if (plane) +		mdp4_plane_destroy(plane); + +	return ERR_PTR(ret); +} 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/mdp/mdp_format.c b/drivers/gpu/drm/msm/mdp/mdp_format.c new file mode 100644 index 00000000000..e0a6ffbe6ab --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp_format.c @@ -0,0 +1,71 @@ +/* + * 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" + +#define FMT(name, a, r, g, b, e0, e1, e2, e3, alpha, tight, c, cnt) { \ +		.base = { .pixel_format = DRM_FORMAT_ ## name }, \ +		.bpc_a = BPC ## a ## A,                          \ +		.bpc_r = BPC ## r,                               \ +		.bpc_g = BPC ## g,                               \ +		.bpc_b = BPC ## b,                               \ +		.unpack = { e0, e1, e2, e3 },                    \ +		.alpha_enable = alpha,                           \ +		.unpack_tight = tight,                           \ +		.cpp = c,                                        \ +		.unpack_count = cnt,                             \ +	} + +#define BPC0A 0 + +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), +	FMT(RGB888,   0, 8, 8, 8,  1, 0, 2, 0,  false,  true,  3,  3), +	FMT(BGR888,   0, 8, 8, 8,  2, 0, 1, 0,  false,  true,  3,  3), +	FMT(RGB565,   0, 5, 6, 5,  1, 0, 2, 0,  false,  true,  2,  3), +	FMT(BGR565,   0, 5, 6, 5,  2, 0, 1, 0,  false,  true,  2,  3), +}; + +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 mdp_format *f = &formats[i]; +		if (f->base.pixel_format == format) +			return &f->base; +	} +	return NULL; +} 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__ */  | 
