diff options
Diffstat (limited to 'drivers/gpu/drm/msm')
63 files changed, 4462 insertions, 1337 deletions
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 08ba8d0d93f5..8e6c7c638e24 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -9,6 +9,7 @@ config DRM_MSM select DRM_PANEL select SHMEM select TMPFS + select QCOM_SCM default y help DRM/KMS driver for MSM/snapdragon. @@ -53,3 +54,17 @@ config DRM_MSM_DSI_PLL help Choose this option to enable DSI PLL driver which provides DSI source clocks under common clock framework. + +config DRM_MSM_DSI_28NM_PHY + bool "Enable DSI 28nm PHY driver in MSM DRM" + depends on DRM_MSM_DSI + default y + help + Choose this option if the 28nm DSI PHY is used on the platform. + +config DRM_MSM_DSI_20NM_PHY + bool "Enable DSI 20nm PHY driver in MSM DRM" + depends on DRM_MSM_DSI + default y + help + Choose this option if the 20nm DSI PHY is used on the platform. diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 16a81b94d6f0..0a543eb5e5d7 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -1,5 +1,5 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/msm -ccflags-$(CONFIG_DRM_MSM_DSI_PLL) += -Idrivers/gpu/drm/msm/dsi +ccflags-$(CONFIG_DRM_MSM_DSI) += -Idrivers/gpu/drm/msm/dsi msm-y := \ adreno/adreno_device.o \ @@ -10,6 +10,7 @@ msm-y := \ hdmi/hdmi_audio.o \ hdmi/hdmi_bridge.o \ hdmi/hdmi_connector.o \ + hdmi/hdmi_hdcp.o \ hdmi/hdmi_i2c.o \ hdmi/hdmi_phy_8960.o \ hdmi/hdmi_phy_8x60.o \ @@ -53,12 +54,18 @@ msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \ + dsi/dsi_cfg.o \ dsi/dsi_host.o \ dsi/dsi_manager.o \ - dsi/dsi_phy.o \ + dsi/phy/dsi_phy.o \ mdp/mdp5/mdp5_cmd_encoder.o -msm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \ - dsi/pll/dsi_pll_28nm.o +msm-$(CONFIG_DRM_MSM_DSI_28NM_PHY) += dsi/phy/dsi_phy_28nm.o +msm-$(CONFIG_DRM_MSM_DSI_20NM_PHY) += dsi/phy/dsi_phy_20nm.o + +ifeq ($(CONFIG_DRM_MSM_DSI_PLL),y) +msm-y += dsi/pll/dsi_pll.o +msm-$(CONFIG_DRM_MSM_DSI_28NM_PHY) += dsi/pll/dsi_pll_28nm.o +endif obj-$(CONFIG_DRM_MSM) += msm.o diff --git a/drivers/gpu/drm/msm/adreno/a2xx.xml.h b/drivers/gpu/drm/msm/adreno/a2xx.xml.h index 23176e402796..0261f0d31612 100644 --- a/drivers/gpu/drm/msm/adreno/a2xx.xml.h +++ b/drivers/gpu/drm/msm/adreno/a2xx.xml.h @@ -8,15 +8,15 @@ http://github.com/freedreno/envytools/ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2013-11-30 14:47:15) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2014-06-02 15:21:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10551 bytes, from 2014-11-13 22:44:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14895 bytes, from 2015-04-19 15:23:28) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 66709 bytes, from 2015-04-12 18:16:35) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 60633 bytes, from 2015-05-20 14:48:19) - -Copyright (C) 2013-2014 by the following authors: +- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10551 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14968 bytes, from 2015-05-20 20:12:27) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 67120 bytes, from 2015-08-14 23:22:03) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 63785 bytes, from 2015-08-14 18:27:06) + +Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) Permission is hereby granted, free of charge, to any person obtaining diff --git a/drivers/gpu/drm/msm/adreno/a3xx.xml.h b/drivers/gpu/drm/msm/adreno/a3xx.xml.h index 1c599e5cf318..48d133711487 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx.xml.h +++ b/drivers/gpu/drm/msm/adreno/a3xx.xml.h @@ -8,13 +8,13 @@ http://github.com/freedreno/envytools/ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2013-11-30 14:47:15) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2014-06-02 15:21:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10551 bytes, from 2014-11-13 22:44:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14895 bytes, from 2015-04-19 15:23:28) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 66709 bytes, from 2015-04-12 18:16:35) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 60633 bytes, from 2015-05-20 14:48:19) +- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10551 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14968 bytes, from 2015-05-20 20:12:27) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 67120 bytes, from 2015-08-14 23:22:03) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 63785 bytes, from 2015-08-14 18:27:06) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) @@ -326,6 +326,13 @@ enum a3xx_tex_type { A3XX_TEX_3D = 3, }; +enum a3xx_tex_msaa { + A3XX_TPL1_MSAA1X = 0, + A3XX_TPL1_MSAA2X = 1, + A3XX_TPL1_MSAA4X = 2, + A3XX_TPL1_MSAA8X = 3, +}; + #define A3XX_INT0_RBBM_GPU_IDLE 0x00000001 #define A3XX_INT0_RBBM_AHB_ERROR 0x00000002 #define A3XX_INT0_RBBM_REG_TIMEOUT 0x00000004 @@ -2652,6 +2659,7 @@ static inline uint32_t A3XX_VGT_DRAW_INITIATOR_NUM_INSTANCES(uint32_t val) #define REG_A3XX_VGT_IMMED_DATA 0x000021fd #define REG_A3XX_TEX_SAMP_0 0x00000000 +#define A3XX_TEX_SAMP_0_CLAMPENABLE 0x00000001 #define A3XX_TEX_SAMP_0_MIPFILTER_LINEAR 0x00000002 #define A3XX_TEX_SAMP_0_XY_MAG__MASK 0x0000000c #define A3XX_TEX_SAMP_0_XY_MAG__SHIFT 2 @@ -2695,6 +2703,7 @@ static inline uint32_t A3XX_TEX_SAMP_0_COMPARE_FUNC(enum adreno_compare_func val { return ((val) << A3XX_TEX_SAMP_0_COMPARE_FUNC__SHIFT) & A3XX_TEX_SAMP_0_COMPARE_FUNC__MASK; } +#define A3XX_TEX_SAMP_0_CUBEMAPSEAMLESSFILTOFF 0x01000000 #define A3XX_TEX_SAMP_0_UNNORM_COORDS 0x80000000 #define REG_A3XX_TEX_SAMP_1 0x00000001 @@ -2750,6 +2759,12 @@ static inline uint32_t A3XX_TEX_CONST_0_MIPLVLS(uint32_t val) { return ((val) << A3XX_TEX_CONST_0_MIPLVLS__SHIFT) & A3XX_TEX_CONST_0_MIPLVLS__MASK; } +#define A3XX_TEX_CONST_0_MSAATEX__MASK 0x00300000 +#define A3XX_TEX_CONST_0_MSAATEX__SHIFT 20 +static inline uint32_t A3XX_TEX_CONST_0_MSAATEX(enum a3xx_tex_msaa val) +{ + return ((val) << A3XX_TEX_CONST_0_MSAATEX__SHIFT) & A3XX_TEX_CONST_0_MSAATEX__MASK; +} #define A3XX_TEX_CONST_0_FMT__MASK 0x1fc00000 #define A3XX_TEX_CONST_0_FMT__SHIFT 22 static inline uint32_t A3XX_TEX_CONST_0_FMT(enum a3xx_tex_fmt val) @@ -2785,7 +2800,7 @@ static inline uint32_t A3XX_TEX_CONST_1_FETCHSIZE(enum a3xx_tex_fetchsize val) } #define REG_A3XX_TEX_CONST_2 0x00000002 -#define A3XX_TEX_CONST_2_INDX__MASK 0x000000ff +#define A3XX_TEX_CONST_2_INDX__MASK 0x000001ff #define A3XX_TEX_CONST_2_INDX__SHIFT 0 static inline uint32_t A3XX_TEX_CONST_2_INDX(uint32_t val) { @@ -2805,7 +2820,7 @@ static inline uint32_t A3XX_TEX_CONST_2_SWAP(enum a3xx_color_swap val) } #define REG_A3XX_TEX_CONST_3 0x00000003 -#define A3XX_TEX_CONST_3_LAYERSZ1__MASK 0x00007fff +#define A3XX_TEX_CONST_3_LAYERSZ1__MASK 0x0001ffff #define A3XX_TEX_CONST_3_LAYERSZ1__SHIFT 0 static inline uint32_t A3XX_TEX_CONST_3_LAYERSZ1(uint32_t val) { diff --git a/drivers/gpu/drm/msm/adreno/a4xx.xml.h b/drivers/gpu/drm/msm/adreno/a4xx.xml.h index 3f06ecf62583..ac55066db3b0 100644 --- a/drivers/gpu/drm/msm/adreno/a4xx.xml.h +++ b/drivers/gpu/drm/msm/adreno/a4xx.xml.h @@ -8,13 +8,13 @@ http://github.com/freedreno/envytools/ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2013-11-30 14:47:15) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2014-06-02 15:21:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10551 bytes, from 2014-11-13 22:44:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14895 bytes, from 2015-04-19 15:23:28) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 66709 bytes, from 2015-04-12 18:16:35) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 60633 bytes, from 2015-05-20 14:48:19) +- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10551 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14968 bytes, from 2015-05-20 20:12:27) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 67120 bytes, from 2015-08-14 23:22:03) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 63785 bytes, from 2015-08-14 18:27:06) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) @@ -227,6 +227,7 @@ enum a4xx_depth_format { DEPTH4_NONE = 0, DEPTH4_16 = 1, DEPTH4_24_8 = 2, + DEPTH4_32 = 3, }; enum a4xx_tess_spacing { @@ -429,7 +430,7 @@ static inline uint32_t A4XX_RB_MRT_BUF_INFO_COLOR_SWAP(enum a3xx_color_swap val) return ((val) << A4XX_RB_MRT_BUF_INFO_COLOR_SWAP__SHIFT) & A4XX_RB_MRT_BUF_INFO_COLOR_SWAP__MASK; } #define A4XX_RB_MRT_BUF_INFO_COLOR_SRGB 0x00002000 -#define A4XX_RB_MRT_BUF_INFO_COLOR_BUF_PITCH__MASK 0x007fc000 +#define A4XX_RB_MRT_BUF_INFO_COLOR_BUF_PITCH__MASK 0xffffc000 #define A4XX_RB_MRT_BUF_INFO_COLOR_BUF_PITCH__SHIFT 14 static inline uint32_t A4XX_RB_MRT_BUF_INFO_COLOR_BUF_PITCH(uint32_t val) { @@ -439,7 +440,7 @@ static inline uint32_t A4XX_RB_MRT_BUF_INFO_COLOR_BUF_PITCH(uint32_t val) static inline uint32_t REG_A4XX_RB_MRT_BASE(uint32_t i0) { return 0x000020a6 + 0x5*i0; } static inline uint32_t REG_A4XX_RB_MRT_CONTROL3(uint32_t i0) { return 0x000020a7 + 0x5*i0; } -#define A4XX_RB_MRT_CONTROL3_STRIDE__MASK 0x0001fff8 +#define A4XX_RB_MRT_CONTROL3_STRIDE__MASK 0x03fffff8 #define A4XX_RB_MRT_CONTROL3_STRIDE__SHIFT 3 static inline uint32_t A4XX_RB_MRT_CONTROL3_STRIDE(uint32_t val) { @@ -570,6 +571,15 @@ static inline uint32_t A4XX_RB_FS_OUTPUT_SAMPLE_MASK(uint32_t val) return ((val) << A4XX_RB_FS_OUTPUT_SAMPLE_MASK__SHIFT) & A4XX_RB_FS_OUTPUT_SAMPLE_MASK__MASK; } +#define REG_A4XX_RB_SAMPLE_COUNT_CONTROL 0x000020fa +#define A4XX_RB_SAMPLE_COUNT_CONTROL_COPY 0x00000002 +#define A4XX_RB_SAMPLE_COUNT_CONTROL_ADDR__MASK 0xfffffffc +#define A4XX_RB_SAMPLE_COUNT_CONTROL_ADDR__SHIFT 2 +static inline uint32_t A4XX_RB_SAMPLE_COUNT_CONTROL_ADDR(uint32_t val) +{ + return ((val >> 2) << A4XX_RB_SAMPLE_COUNT_CONTROL_ADDR__SHIFT) & A4XX_RB_SAMPLE_COUNT_CONTROL_ADDR__MASK; +} + #define REG_A4XX_RB_RENDER_COMPONENTS 0x000020fb #define A4XX_RB_RENDER_COMPONENTS_RT0__MASK 0x0000000f #define A4XX_RB_RENDER_COMPONENTS_RT0__SHIFT 0 @@ -811,6 +821,23 @@ static inline uint32_t A4XX_RB_STENCIL_CONTROL_ZFAIL_BF(enum adreno_stencil_op v #define REG_A4XX_RB_STENCIL_CONTROL2 0x00002107 #define A4XX_RB_STENCIL_CONTROL2_STENCIL_BUFFER 0x00000001 +#define REG_A4XX_RB_STENCIL_INFO 0x00002108 +#define A4XX_RB_STENCIL_INFO_SEPARATE_STENCIL 0x00000001 +#define A4XX_RB_STENCIL_INFO_STENCIL_BASE__MASK 0xfffff000 +#define A4XX_RB_STENCIL_INFO_STENCIL_BASE__SHIFT 12 +static inline uint32_t A4XX_RB_STENCIL_INFO_STENCIL_BASE(uint32_t val) +{ + return ((val >> 12) << A4XX_RB_STENCIL_INFO_STENCIL_BASE__SHIFT) & A4XX_RB_STENCIL_INFO_STENCIL_BASE__MASK; +} + +#define REG_A4XX_RB_STENCIL_PITCH 0x00002109 +#define A4XX_RB_STENCIL_PITCH__MASK 0xffffffff +#define A4XX_RB_STENCIL_PITCH__SHIFT 0 +static inline uint32_t A4XX_RB_STENCIL_PITCH(uint32_t val) +{ + return ((val >> 5) << A4XX_RB_STENCIL_PITCH__SHIFT) & A4XX_RB_STENCIL_PITCH__MASK; +} + #define REG_A4XX_RB_STENCILREFMASK 0x0000210b #define A4XX_RB_STENCILREFMASK_STENCILREF__MASK 0x000000ff #define A4XX_RB_STENCILREFMASK_STENCILREF__SHIFT 0 @@ -1433,6 +1460,7 @@ static inline uint32_t A4XX_SP_FS_MRT_REG_MRTFORMAT(enum a4xx_color_fmt val) { return ((val) << A4XX_SP_FS_MRT_REG_MRTFORMAT__SHIFT) & A4XX_SP_FS_MRT_REG_MRTFORMAT__MASK; } +#define A4XX_SP_FS_MRT_REG_COLOR_SRGB 0x00040000 #define REG_A4XX_SP_CS_CTRL_REG0 0x00002300 @@ -1470,6 +1498,76 @@ static inline uint32_t A4XX_SP_HS_OBJ_OFFSET_REG_SHADEROBJOFFSET(uint32_t val) #define REG_A4XX_SP_HS_LENGTH_REG 0x00002312 +#define REG_A4XX_SP_DS_PARAM_REG 0x0000231a +#define A4XX_SP_DS_PARAM_REG_POSREGID__MASK 0x000000ff +#define A4XX_SP_DS_PARAM_REG_POSREGID__SHIFT 0 +static inline uint32_t A4XX_SP_DS_PARAM_REG_POSREGID(uint32_t val) +{ + return ((val) << A4XX_SP_DS_PARAM_REG_POSREGID__SHIFT) & A4XX_SP_DS_PARAM_REG_POSREGID__MASK; +} +#define A4XX_SP_DS_PARAM_REG_TOTALGSOUTVAR__MASK 0xfff00000 +#define A4XX_SP_DS_PARAM_REG_TOTALGSOUTVAR__SHIFT 20 +static inline uint32_t A4XX_SP_DS_PARAM_REG_TOTALGSOUTVAR(uint32_t val) +{ + return ((val) << A4XX_SP_DS_PARAM_REG_TOTALGSOUTVAR__SHIFT) & A4XX_SP_DS_PARAM_REG_TOTALGSOUTVAR__MASK; +} + +static inline uint32_t REG_A4XX_SP_DS_OUT(uint32_t i0) { return 0x0000231b + 0x1*i0; } + +static inline uint32_t REG_A4XX_SP_DS_OUT_REG(uint32_t i0) { return 0x0000231b + 0x1*i0; } +#define A4XX_SP_DS_OUT_REG_A_REGID__MASK 0x000001ff +#define A4XX_SP_DS_OUT_REG_A_REGID__SHIFT 0 +static inline uint32_t A4XX_SP_DS_OUT_REG_A_REGID(uint32_t val) +{ + return ((val) << A4XX_SP_DS_OUT_REG_A_REGID__SHIFT) & A4XX_SP_DS_OUT_REG_A_REGID__MASK; +} +#define A4XX_SP_DS_OUT_REG_A_COMPMASK__MASK 0x00001e00 +#define A4XX_SP_DS_OUT_REG_A_COMPMASK__SHIFT 9 +static inline uint32_t A4XX_SP_DS_OUT_REG_A_COMPMASK(uint32_t val) +{ + return ((val) << A4XX_SP_DS_OUT_REG_A_COMPMASK__SHIFT) & A4XX_SP_DS_OUT_REG_A_COMPMASK__MASK; +} +#define A4XX_SP_DS_OUT_REG_B_REGID__MASK 0x01ff0000 +#define A4XX_SP_DS_OUT_REG_B_REGID__SHIFT 16 +static inline uint32_t A4XX_SP_DS_OUT_REG_B_REGID(uint32_t val) +{ + return ((val) << A4XX_SP_DS_OUT_REG_B_REGID__SHIFT) & A4XX_SP_DS_OUT_REG_B_REGID__MASK; +} +#define A4XX_SP_DS_OUT_REG_B_COMPMASK__MASK 0x1e000000 +#define A4XX_SP_DS_OUT_REG_B_COMPMASK__SHIFT 25 +static inline uint32_t A4XX_SP_DS_OUT_REG_B_COMPMASK(uint32_t val) +{ + return ((val) << A4XX_SP_DS_OUT_REG_B_COMPMASK__SHIFT) & A4XX_SP_DS_OUT_REG_B_COMPMASK__MASK; +} + +static inline uint32_t REG_A4XX_SP_DS_VPC_DST(uint32_t i0) { return 0x0000232c + 0x1*i0; } + +static inline uint32_t REG_A4XX_SP_DS_VPC_DST_REG(uint32_t i0) { return 0x0000232c + 0x1*i0; } +#define A4XX_SP_DS_VPC_DST_REG_OUTLOC0__MASK 0x000000ff +#define A4XX_SP_DS_VPC_DST_REG_OUTLOC0__SHIFT 0 +static inline uint32_t A4XX_SP_DS_VPC_DST_REG_OUTLOC0(uint32_t val) +{ + return ((val) << A4XX_SP_DS_VPC_DST_REG_OUTLOC0__SHIFT) & A4XX_SP_DS_VPC_DST_REG_OUTLOC0__MASK; +} +#define A4XX_SP_DS_VPC_DST_REG_OUTLOC1__MASK 0x0000ff00 +#define A4XX_SP_DS_VPC_DST_REG_OUTLOC1__SHIFT 8 +static inline uint32_t A4XX_SP_DS_VPC_DST_REG_OUTLOC1(uint32_t val) +{ + return ((val) << A4XX_SP_DS_VPC_DST_REG_OUTLOC1__SHIFT) & A4XX_SP_DS_VPC_DST_REG_OUTLOC1__MASK; +} +#define A4XX_SP_DS_VPC_DST_REG_OUTLOC2__MASK 0x00ff0000 +#define A4XX_SP_DS_VPC_DST_REG_OUTLOC2__SHIFT 16 +static inline uint32_t A4XX_SP_DS_VPC_DST_REG_OUTLOC2(uint32_t val) +{ + return ((val) << A4XX_SP_DS_VPC_DST_REG_OUTLOC2__SHIFT) & A4XX_SP_DS_VPC_DST_REG_OUTLOC2__MASK; +} +#define A4XX_SP_DS_VPC_DST_REG_OUTLOC3__MASK 0xff000000 +#define A4XX_SP_DS_VPC_DST_REG_OUTLOC3__SHIFT 24 +static inline uint32_t A4XX_SP_DS_VPC_DST_REG_OUTLOC3(uint32_t val) +{ + return ((val) << A4XX_SP_DS_VPC_DST_REG_OUTLOC3__SHIFT) & A4XX_SP_DS_VPC_DST_REG_OUTLOC3__MASK; +} + #define REG_A4XX_SP_DS_OBJ_OFFSET_REG 0x00002334 #define A4XX_SP_DS_OBJ_OFFSET_REG_CONSTOBJECTOFFSET__MASK 0x01ff0000 #define A4XX_SP_DS_OBJ_OFFSET_REG_CONSTOBJECTOFFSET__SHIFT 16 @@ -1492,6 +1590,82 @@ static inline uint32_t A4XX_SP_DS_OBJ_OFFSET_REG_SHADEROBJOFFSET(uint32_t val) #define REG_A4XX_SP_DS_LENGTH_REG 0x00002339 +#define REG_A4XX_SP_GS_PARAM_REG 0x00002341 +#define A4XX_SP_GS_PARAM_REG_POSREGID__MASK 0x000000ff +#define A4XX_SP_GS_PARAM_REG_POSREGID__SHIFT 0 +static inline uint32_t A4XX_SP_GS_PARAM_REG_POSREGID(uint32_t val) +{ + return ((val) << A4XX_SP_GS_PARAM_REG_POSREGID__SHIFT) & A4XX_SP_GS_PARAM_REG_POSREGID__MASK; +} +#define A4XX_SP_GS_PARAM_REG_PRIMREGID__MASK 0x0000ff00 +#define A4XX_SP_GS_PARAM_REG_PRIMREGID__SHIFT 8 +static inline uint32_t A4XX_SP_GS_PARAM_REG_PRIMREGID(uint32_t val) +{ + return ((val) << A4XX_SP_GS_PARAM_REG_PRIMREGID__SHIFT) & A4XX_SP_GS_PARAM_REG_PRIMREGID__MASK; +} +#define A4XX_SP_GS_PARAM_REG_TOTALGSOUTVAR__MASK 0xfff00000 +#define A4XX_SP_GS_PARAM_REG_TOTALGSOUTVAR__SHIFT 20 +static inline uint32_t A4XX_SP_GS_PARAM_REG_TOTALGSOUTVAR(uint32_t val) +{ + return ((val) << A4XX_SP_GS_PARAM_REG_TOTALGSOUTVAR__SHIFT) & A4XX_SP_GS_PARAM_REG_TOTALGSOUTVAR__MASK; +} + +static inline uint32_t REG_A4XX_SP_GS_OUT(uint32_t i0) { return 0x00002342 + 0x1*i0; } + +static inline uint32_t REG_A4XX_SP_GS_OUT_REG(uint32_t i0) { return 0x00002342 + 0x1*i0; } +#define A4XX_SP_GS_OUT_REG_A_REGID__MASK 0x000001ff +#define A4XX_SP_GS_OUT_REG_A_REGID__SHIFT 0 +static inline uint32_t A4XX_SP_GS_OUT_REG_A_REGID(uint32_t val) +{ + return ((val) << A4XX_SP_GS_OUT_REG_A_REGID__SHIFT) & A4XX_SP_GS_OUT_REG_A_REGID__MASK; +} +#define A4XX_SP_GS_OUT_REG_A_COMPMASK__MASK 0x00001e00 +#define A4XX_SP_GS_OUT_REG_A_COMPMASK__SHIFT 9 +static inline uint32_t A4XX_SP_GS_OUT_REG_A_COMPMASK(uint32_t val) +{ + return ((val) << A4XX_SP_GS_OUT_REG_A_COMPMASK__SHIFT) & A4XX_SP_GS_OUT_REG_A_COMPMASK__MASK; +} +#define A4XX_SP_GS_OUT_REG_B_REGID__MASK 0x01ff0000 +#define A4XX_SP_GS_OUT_REG_B_REGID__SHIFT 16 +static inline uint32_t A4XX_SP_GS_OUT_REG_B_REGID(uint32_t val) +{ + return ((val) << A4XX_SP_GS_OUT_REG_B_REGID__SHIFT) & A4XX_SP_GS_OUT_REG_B_REGID__MASK; +} +#define A4XX_SP_GS_OUT_REG_B_COMPMASK__MASK 0x1e000000 +#define A4XX_SP_GS_OUT_REG_B_COMPMASK__SHIFT 25 +static inline uint32_t A4XX_SP_GS_OUT_REG_B_COMPMASK(uint32_t val) +{ + return ((val) << A4XX_SP_GS_OUT_REG_B_COMPMASK__SHIFT) & A4XX_SP_GS_OUT_REG_B_COMPMASK__MASK; +} + +static inline uint32_t REG_A4XX_SP_GS_VPC_DST(uint32_t i0) { return 0x00002353 + 0x1*i0; } + +static inline uint32_t REG_A4XX_SP_GS_VPC_DST_REG(uint32_t i0) { return 0x00002353 + 0x1*i0; } +#define A4XX_SP_GS_VPC_DST_REG_OUTLOC0__MASK 0x000000ff +#define A4XX_SP_GS_VPC_DST_REG_OUTLOC0__SHIFT 0 +static inline uint32_t A4XX_SP_GS_VPC_DST_REG_OUTLOC0(uint32_t val) +{ + return ((val) << A4XX_SP_GS_VPC_DST_REG_OUTLOC0__SHIFT) & A4XX_SP_GS_VPC_DST_REG_OUTLOC0__MASK; +} +#define A4XX_SP_GS_VPC_DST_REG_OUTLOC1__MASK 0x0000ff00 +#define A4XX_SP_GS_VPC_DST_REG_OUTLOC1__SHIFT 8 +static inline uint32_t A4XX_SP_GS_VPC_DST_REG_OUTLOC1(uint32_t val) +{ + return ((val) << A4XX_SP_GS_VPC_DST_REG_OUTLOC1__SHIFT) & A4XX_SP_GS_VPC_DST_REG_OUTLOC1__MASK; +} +#define A4XX_SP_GS_VPC_DST_REG_OUTLOC2__MASK 0x00ff0000 +#define A4XX_SP_GS_VPC_DST_REG_OUTLOC2__SHIFT 16 +static inline uint32_t A4XX_SP_GS_VPC_DST_REG_OUTLOC2(uint32_t val) +{ + return ((val) << A4XX_SP_GS_VPC_DST_REG_OUTLOC2__SHIFT) & A4XX_SP_GS_VPC_DST_REG_OUTLOC2__MASK; +} +#define A4XX_SP_GS_VPC_DST_REG_OUTLOC3__MASK 0xff000000 +#define A4XX_SP_GS_VPC_DST_REG_OUTLOC3__SHIFT 24 +static inline uint32_t A4XX_SP_GS_VPC_DST_REG_OUTLOC3(uint32_t val) +{ + return ((val) << A4XX_SP_GS_VPC_DST_REG_OUTLOC3__SHIFT) & A4XX_SP_GS_VPC_DST_REG_OUTLOC3__MASK; +} + #define REG_A4XX_SP_GS_OBJ_OFFSET_REG 0x0000235b #define A4XX_SP_GS_OBJ_OFFSET_REG_CONSTOBJECTOFFSET__MASK 0x01ff0000 #define A4XX_SP_GS_OBJ_OFFSET_REG_CONSTOBJECTOFFSET__SHIFT 16 @@ -1693,6 +1867,18 @@ static inline uint32_t A4XX_VFD_CONTROL_3_REGID_VTXCNT(uint32_t val) { return ((val) << A4XX_VFD_CONTROL_3_REGID_VTXCNT__SHIFT) & A4XX_VFD_CONTROL_3_REGID_VTXCNT__MASK; } +#define A4XX_VFD_CONTROL_3_REGID_TESSX__MASK 0x00ff0000 +#define A4XX_VFD_CONTROL_3_REGID_TESSX__SHIFT 16 +static inline uint32_t A4XX_VFD_CONTROL_3_REGID_TESSX(uint32_t val) +{ + return ((val) << A4XX_VFD_CONTROL_3_REGID_TESSX__SHIFT) & A4XX_VFD_CONTROL_3_REGID_TESSX__MASK; +} +#define A4XX_VFD_CONTROL_3_REGID_TESSY__MASK 0xff000000 +#define A4XX_VFD_CONTROL_3_REGID_TESSY__SHIFT 24 +static inline uint32_t A4XX_VFD_CONTROL_3_REGID_TESSY(uint32_t val) +{ + return ((val) << A4XX_VFD_CONTROL_3_REGID_TESSY__SHIFT) & A4XX_VFD_CONTROL_3_REGID_TESSY__MASK; +} #define REG_A4XX_VFD_CONTROL_4 0x00002204 @@ -2489,6 +2675,8 @@ static inline uint32_t A4XX_UNKNOWN_20F7(float val) #define REG_A4XX_UNKNOWN_22D7 0x000022d7 +#define REG_A4XX_UNKNOWN_2352 0x00002352 + #define REG_A4XX_TEX_SAMP_0 0x00000000 #define A4XX_TEX_SAMP_0_MIPFILTER_LINEAR_NEAR 0x00000001 #define A4XX_TEX_SAMP_0_XY_MAG__MASK 0x00000006 diff --git a/drivers/gpu/drm/msm/adreno/adreno_common.xml.h b/drivers/gpu/drm/msm/adreno/adreno_common.xml.h index 9562a1fa552b..399a9e528139 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_common.xml.h +++ b/drivers/gpu/drm/msm/adreno/adreno_common.xml.h @@ -8,15 +8,15 @@ http://github.com/freedreno/envytools/ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2013-11-30 14:47:15) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2014-06-02 15:21:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10551 bytes, from 2014-11-13 22:44:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14895 bytes, from 2015-04-19 15:23:28) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 66709 bytes, from 2015-04-12 18:16:35) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 60633 bytes, from 2015-05-20 14:48:19) - -Copyright (C) 2013-2014 by the following authors: +- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10551 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14968 bytes, from 2015-05-20 20:12:27) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 67120 bytes, from 2015-08-14 23:22:03) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 63785 bytes, from 2015-08-14 18:27:06) + +Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) Permission is hereby granted, free of charge, to any person obtaining diff --git a/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h b/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h index bd5b23bf9041..41904fed1350 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h +++ b/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h @@ -8,13 +8,13 @@ http://github.com/freedreno/envytools/ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2013-11-30 14:47:15) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2014-06-02 15:21:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10551 bytes, from 2014-11-13 22:44:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14895 bytes, from 2015-04-19 15:23:28) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 66709 bytes, from 2015-04-12 18:16:35) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 60633 bytes, from 2015-05-20 14:48:19) +- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10551 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14968 bytes, from 2015-05-20 20:12:27) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 67120 bytes, from 2015-08-14 23:22:03) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 63785 bytes, from 2015-08-14 18:27:06) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) @@ -67,7 +67,7 @@ enum vgt_event_type { enum pc_di_primtype { DI_PT_NONE = 0, - DI_PT_POINTLIST_A2XX = 1, + DI_PT_POINTLIST_PSIZE = 1, DI_PT_LINELIST = 2, DI_PT_LINESTRIP = 3, DI_PT_TRILIST = 4, @@ -75,7 +75,7 @@ enum pc_di_primtype { DI_PT_TRISTRIP = 6, DI_PT_LINELOOP = 7, DI_PT_RECTLIST = 8, - DI_PT_POINTLIST_A3XX = 9, + DI_PT_POINTLIST = 9, DI_PT_LINE_ADJ = 10, DI_PT_LINESTRIP_ADJ = 11, DI_PT_TRI_ADJ = 12, diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c index 1f2561e2ff71..6edcd6f57e70 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.c +++ b/drivers/gpu/drm/msm/dsi/dsi.c @@ -15,10 +15,10 @@ struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi) { - if (!msm_dsi || !msm_dsi->panel) + if (!msm_dsi || !msm_dsi_device_connected(msm_dsi)) return NULL; - return (msm_dsi->panel_flags & MIPI_DSI_MODE_VIDEO) ? + return (msm_dsi->device_flags & MIPI_DSI_MODE_VIDEO) ? msm_dsi->encoders[MSM_DSI_VIDEO_ENCODER_ID] : msm_dsi->encoders[MSM_DSI_CMD_ENCODER_ID]; } @@ -74,19 +74,15 @@ static void dsi_destroy(struct msm_dsi *msm_dsi) static struct msm_dsi *dsi_init(struct platform_device *pdev) { - struct msm_dsi *msm_dsi = NULL; + struct msm_dsi *msm_dsi; int ret; - if (!pdev) { - ret = -ENXIO; - goto fail; - } + if (!pdev) + return ERR_PTR(-ENXIO); msm_dsi = devm_kzalloc(&pdev->dev, sizeof(*msm_dsi), GFP_KERNEL); - if (!msm_dsi) { - ret = -ENOMEM; - goto fail; - } + if (!msm_dsi) + return ERR_PTR(-ENOMEM); DBG("dsi probed=%p", msm_dsi); msm_dsi->pdev = pdev; @@ -95,24 +91,22 @@ static struct msm_dsi *dsi_init(struct platform_device *pdev) /* Init dsi host */ ret = msm_dsi_host_init(msm_dsi); if (ret) - goto fail; + goto destroy_dsi; /* GET dsi PHY */ ret = dsi_get_phy(msm_dsi); if (ret) - goto fail; + goto destroy_dsi; /* Register to dsi manager */ ret = msm_dsi_manager_register(msm_dsi); if (ret) - goto fail; + goto destroy_dsi; return msm_dsi; -fail: - if (msm_dsi) - dsi_destroy(msm_dsi); - +destroy_dsi: + dsi_destroy(msm_dsi); return ERR_PTR(ret); } @@ -196,6 +190,7 @@ int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, struct drm_encoder *encoders[MSM_DSI_ENCODER_NUM]) { struct msm_drm_private *priv = dev->dev_private; + struct drm_bridge *ext_bridge; int ret, i; if (WARN_ON(!encoders[MSM_DSI_VIDEO_ENCODER_ID] || @@ -223,10 +218,25 @@ int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, msm_dsi->encoders[i] = encoders[i]; } - msm_dsi->connector = msm_dsi_manager_connector_init(msm_dsi->id); + /* + * check if the dsi encoder output is connected to a panel or an + * external bridge. We create a connector only if we're connected to a + * drm_panel device. When we're connected to an external bridge, we + * assume that the drm_bridge driver will create the connector itself. + */ + ext_bridge = msm_dsi_host_get_bridge(msm_dsi->host); + + if (ext_bridge) + msm_dsi->connector = + msm_dsi_manager_ext_bridge_init(msm_dsi->id); + else + msm_dsi->connector = + msm_dsi_manager_connector_init(msm_dsi->id); + if (IS_ERR(msm_dsi->connector)) { ret = PTR_ERR(msm_dsi->connector); - dev_err(dev->dev, "failed to create dsi connector: %d\n", ret); + dev_err(dev->dev, + "failed to create dsi connector: %d\n", ret); msm_dsi->connector = NULL; goto fail; } @@ -242,10 +252,12 @@ fail: msm_dsi_manager_bridge_destroy(msm_dsi->bridge); msm_dsi->bridge = NULL; } - if (msm_dsi->connector) { + + /* don't destroy connector if we didn't make it */ + if (msm_dsi->connector && !msm_dsi->external_bridge) msm_dsi->connector->funcs->destroy(msm_dsi->connector); - msm_dsi->connector = NULL; - } + + msm_dsi->connector = NULL; } return ret; diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h index 92d697de4858..5f5a3732cdf6 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.h +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -27,21 +27,10 @@ #define DSI_1 1 #define DSI_MAX 2 -#define DSI_CLOCK_MASTER DSI_0 -#define DSI_CLOCK_SLAVE DSI_1 - -#define DSI_LEFT DSI_0 -#define DSI_RIGHT DSI_1 - -/* According to the current drm framework sequence, take the encoder of - * DSI_1 as master encoder - */ -#define DSI_ENCODER_MASTER DSI_1 -#define DSI_ENCODER_SLAVE DSI_0 - enum msm_dsi_phy_type { MSM_DSI_PHY_28NM_HPM, MSM_DSI_PHY_28NM_LP, + MSM_DSI_PHY_20NM, MSM_DSI_PHY_MAX }; @@ -65,13 +54,21 @@ struct msm_dsi { struct drm_device *dev; struct platform_device *pdev; + /* connector managed by us when we're connected to a drm_panel */ struct drm_connector *connector; + /* internal dsi bridge attached to MDP interface */ struct drm_bridge *bridge; struct mipi_dsi_host *host; struct msm_dsi_phy *phy; + + /* + * panel/external_bridge connected to dsi bridge output, only one of the + * two can be valid at a time + */ struct drm_panel *panel; - unsigned long panel_flags; + struct drm_bridge *external_bridge; + unsigned long device_flags; struct device *phy_dev; bool phy_enabled; @@ -86,6 +83,7 @@ struct msm_dsi { struct drm_bridge *msm_dsi_manager_bridge_init(u8 id); void msm_dsi_manager_bridge_destroy(struct drm_bridge *bridge); struct drm_connector *msm_dsi_manager_connector_init(u8 id); +struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id); int msm_dsi_manager_phy_enable(int id, const unsigned long bit_rate, const unsigned long esc_rate, u32 *clk_pre, u32 *clk_post); @@ -96,6 +94,11 @@ int msm_dsi_manager_register(struct msm_dsi *msm_dsi); void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi); /* msm dsi */ +static inline bool msm_dsi_device_connected(struct msm_dsi *msm_dsi) +{ + return msm_dsi->panel || msm_dsi->external_bridge; +} + struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi); /* dsi pll */ @@ -106,6 +109,8 @@ struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, void msm_dsi_pll_destroy(struct msm_dsi_pll *pll); int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll, struct clk **byte_clk_provider, struct clk **pixel_clk_provider); +void msm_dsi_pll_save_state(struct msm_dsi_pll *pll); +int msm_dsi_pll_restore_state(struct msm_dsi_pll *pll); #else static inline struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, enum msm_dsi_phy_type type, int id) { @@ -119,6 +124,13 @@ static inline int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll, { return -ENODEV; } +static inline void msm_dsi_pll_save_state(struct msm_dsi_pll *pll) +{ +} +static inline int msm_dsi_pll_restore_state(struct msm_dsi_pll *pll) +{ + return 0; +} #endif /* dsi host */ @@ -140,6 +152,7 @@ int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, struct drm_display_mode *mode); struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host, unsigned long *panel_flags); +struct drm_bridge *msm_dsi_host_get_bridge(struct mipi_dsi_host *host); int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer); void msm_dsi_host_unregister(struct mipi_dsi_host *host); int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host, @@ -153,9 +166,9 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi); struct msm_dsi_phy; void msm_dsi_phy_driver_register(void); void msm_dsi_phy_driver_unregister(void); -int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel, +int msm_dsi_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, const unsigned long bit_rate, const unsigned long esc_rate); -int msm_dsi_phy_disable(struct msm_dsi_phy *phy); +void msm_dsi_phy_disable(struct msm_dsi_phy *phy); void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy, u32 *clk_pre, u32 *clk_post); struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy); diff --git a/drivers/gpu/drm/msm/dsi/dsi.xml.h b/drivers/gpu/drm/msm/dsi/dsi.xml.h index 9791ea04bcbc..1d2e32f0817b 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.xml.h +++ b/drivers/gpu/drm/msm/dsi/dsi.xml.h @@ -8,17 +8,17 @@ 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 ( 676 bytes, from 2014-12-05 15:34:49) -- /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 ( 20915 bytes, from 2015-03-24 22:05:22) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2352 bytes, from 2015-04-12 15:02:42) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 35083 bytes, from 2015-04-12 15:04:03) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 22094 bytes, from 2015-05-12 12:45:23) -- /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 ( 1686 bytes, from 2014-10-31 16:48:57) -- /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 ( 29012 bytes, from 2015-05-12 12:45:23) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-12 12:45:23) +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2576 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 36021 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 26057 bytes, from 2015-08-14 21:47:57) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) @@ -382,6 +382,11 @@ static inline uint32_t DSI_TRIG_CTRL_STREAM(uint32_t val) #define REG_DSI_TRIG_DMA 0x0000008c #define REG_DSI_DLN0_PHY_ERR 0x000000b0 +#define DSI_DLN0_PHY_ERR_DLN0_ERR_ESC 0x00000001 +#define DSI_DLN0_PHY_ERR_DLN0_ERR_SYNC_ESC 0x00000010 +#define DSI_DLN0_PHY_ERR_DLN0_ERR_CONTROL 0x00000100 +#define DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP0 0x00001000 +#define DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP1 0x00010000 #define REG_DSI_TIMEOUT_STATUS 0x000000bc @@ -435,6 +440,9 @@ static inline uint32_t DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(enum dsi_lane_swap val) #define REG_DSI_PHY_RESET 0x00000128 #define DSI_PHY_RESET_RESET 0x00000001 +#define REG_DSI_T_CLK_PRE_EXTEND 0x0000017c +#define DSI_T_CLK_PRE_EXTEND_INC_BY_2_BYTECLK 0x00000001 + #define REG_DSI_RDBK_DATA_CTRL 0x000001d0 #define DSI_RDBK_DATA_CTRL_COUNT__MASK 0x00ff0000 #define DSI_RDBK_DATA_CTRL_COUNT__SHIFT 16 @@ -830,6 +838,7 @@ static inline uint32_t DSI_28nm_PHY_TIMING_CTRL_11_TRIG3_CMD(uint32_t val) #define REG_DSI_28nm_PHY_BIST_CTRL_5 0x000001c8 #define REG_DSI_28nm_PHY_GLBL_TEST_CTRL 0x000001d4 +#define DSI_28nm_PHY_GLBL_TEST_CTRL_BITCLK_HS_SEL 0x00000001 #define REG_DSI_28nm_PHY_LDO_CNTRL 0x000001dc @@ -994,5 +1003,185 @@ static inline uint32_t DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8(uint32_t val) #define REG_DSI_28nm_PHY_PLL_CTRL_54 0x000000d4 +static inline uint32_t REG_DSI_20nm_PHY_LN(uint32_t i0) { return 0x00000000 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_CFG_0(uint32_t i0) { return 0x00000000 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_CFG_1(uint32_t i0) { return 0x00000004 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_CFG_2(uint32_t i0) { return 0x00000008 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_CFG_3(uint32_t i0) { return 0x0000000c + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_CFG_4(uint32_t i0) { return 0x00000010 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_TEST_DATAPATH(uint32_t i0) { return 0x00000014 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_DEBUG_SEL(uint32_t i0) { return 0x00000018 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_TEST_STR_0(uint32_t i0) { return 0x0000001c + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_TEST_STR_1(uint32_t i0) { return 0x00000020 + 0x40*i0; } + +#define REG_DSI_20nm_PHY_LNCK_CFG_0 0x00000100 + +#define REG_DSI_20nm_PHY_LNCK_CFG_1 0x00000104 + +#define REG_DSI_20nm_PHY_LNCK_CFG_2 0x00000108 + +#define REG_DSI_20nm_PHY_LNCK_CFG_3 0x0000010c + +#define REG_DSI_20nm_PHY_LNCK_CFG_4 0x00000110 + +#define REG_DSI_20nm_PHY_LNCK_TEST_DATAPATH 0x00000114 + +#define REG_DSI_20nm_PHY_LNCK_DEBUG_SEL 0x00000118 + +#define REG_DSI_20nm_PHY_LNCK_TEST_STR0 0x0000011c + +#define REG_DSI_20nm_PHY_LNCK_TEST_STR1 0x00000120 + +#define REG_DSI_20nm_PHY_TIMING_CTRL_0 0x00000140 +#define DSI_20nm_PHY_TIMING_CTRL_0_CLK_ZERO__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_0_CLK_ZERO__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_0_CLK_ZERO(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_0_CLK_ZERO__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_0_CLK_ZERO__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_1 0x00000144 +#define DSI_20nm_PHY_TIMING_CTRL_1_CLK_TRAIL__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_1_CLK_TRAIL__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_1_CLK_TRAIL(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_1_CLK_TRAIL__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_1_CLK_TRAIL__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_2 0x00000148 +#define DSI_20nm_PHY_TIMING_CTRL_2_CLK_PREPARE__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_2_CLK_PREPARE__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_2_CLK_PREPARE(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_2_CLK_PREPARE__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_2_CLK_PREPARE__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_3 0x0000014c +#define DSI_20nm_PHY_TIMING_CTRL_3_CLK_ZERO_8 0x00000001 + +#define REG_DSI_20nm_PHY_TIMING_CTRL_4 0x00000150 +#define DSI_20nm_PHY_TIMING_CTRL_4_HS_EXIT__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_4_HS_EXIT__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_4_HS_EXIT(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_4_HS_EXIT__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_4_HS_EXIT__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_5 0x00000154 +#define DSI_20nm_PHY_TIMING_CTRL_5_HS_ZERO__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_5_HS_ZERO__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_5_HS_ZERO(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_5_HS_ZERO__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_5_HS_ZERO__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_6 0x00000158 +#define DSI_20nm_PHY_TIMING_CTRL_6_HS_PREPARE__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_6_HS_PREPARE__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_6_HS_PREPARE(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_6_HS_PREPARE__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_6_HS_PREPARE__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_7 0x0000015c +#define DSI_20nm_PHY_TIMING_CTRL_7_HS_TRAIL__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_7_HS_TRAIL__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_7_HS_TRAIL(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_7_HS_TRAIL__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_7_HS_TRAIL__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_8 0x00000160 +#define DSI_20nm_PHY_TIMING_CTRL_8_HS_RQST__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_8_HS_RQST__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_8_HS_RQST(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_8_HS_RQST__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_8_HS_RQST__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_9 0x00000164 +#define DSI_20nm_PHY_TIMING_CTRL_9_TA_GO__MASK 0x00000007 +#define DSI_20nm_PHY_TIMING_CTRL_9_TA_GO__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_9_TA_GO(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_9_TA_GO__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_9_TA_GO__MASK; +} +#define DSI_20nm_PHY_TIMING_CTRL_9_TA_SURE__MASK 0x00000070 +#define DSI_20nm_PHY_TIMING_CTRL_9_TA_SURE__SHIFT 4 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_9_TA_SURE(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_9_TA_SURE__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_9_TA_SURE__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_10 0x00000168 +#define DSI_20nm_PHY_TIMING_CTRL_10_TA_GET__MASK 0x00000007 +#define DSI_20nm_PHY_TIMING_CTRL_10_TA_GET__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_10_TA_GET(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_10_TA_GET__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_10_TA_GET__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_11 0x0000016c +#define DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD__MASK; +} + +#define REG_DSI_20nm_PHY_CTRL_0 0x00000170 + +#define REG_DSI_20nm_PHY_CTRL_1 0x00000174 + +#define REG_DSI_20nm_PHY_CTRL_2 0x00000178 + +#define REG_DSI_20nm_PHY_CTRL_3 0x0000017c + +#define REG_DSI_20nm_PHY_CTRL_4 0x00000180 + +#define REG_DSI_20nm_PHY_STRENGTH_0 0x00000184 + +#define REG_DSI_20nm_PHY_STRENGTH_1 0x00000188 + +#define REG_DSI_20nm_PHY_BIST_CTRL_0 0x000001b4 + +#define REG_DSI_20nm_PHY_BIST_CTRL_1 0x000001b8 + +#define REG_DSI_20nm_PHY_BIST_CTRL_2 0x000001bc + +#define REG_DSI_20nm_PHY_BIST_CTRL_3 0x000001c0 + +#define REG_DSI_20nm_PHY_BIST_CTRL_4 0x000001c4 + +#define REG_DSI_20nm_PHY_BIST_CTRL_5 0x000001c8 + +#define REG_DSI_20nm_PHY_GLBL_TEST_CTRL 0x000001d4 +#define DSI_20nm_PHY_GLBL_TEST_CTRL_BITCLK_HS_SEL 0x00000001 + +#define REG_DSI_20nm_PHY_LDO_CNTRL 0x000001dc + +#define REG_DSI_20nm_PHY_REGULATOR_CTRL_0 0x00000000 + +#define REG_DSI_20nm_PHY_REGULATOR_CTRL_1 0x00000004 + +#define REG_DSI_20nm_PHY_REGULATOR_CTRL_2 0x00000008 + +#define REG_DSI_20nm_PHY_REGULATOR_CTRL_3 0x0000000c + +#define REG_DSI_20nm_PHY_REGULATOR_CTRL_4 0x00000010 + +#define REG_DSI_20nm_PHY_REGULATOR_CTRL_5 0x00000014 + +#define REG_DSI_20nm_PHY_REGULATOR_CAL_PWR_CFG 0x00000018 + #endif /* DSI_XML */ diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.c b/drivers/gpu/drm/msm/dsi/dsi_cfg.c new file mode 100644 index 000000000000..5872d5e5934f --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "dsi_cfg.h" + +/* DSI v2 has not been supported by now */ +static const struct msm_dsi_config dsi_v2_cfg = { + .io_offset = 0, +}; + +static const struct msm_dsi_config msm8974_apq8084_dsi_cfg = { + .io_offset = DSI_6G_REG_SHIFT, + .reg_cfg = { + .num = 4, + .regs = { + {"gdsc", -1, -1, -1, -1}, + {"vdd", 3000000, 3000000, 150000, 100}, + {"vdda", 1200000, 1200000, 100000, 100}, + {"vddio", 1800000, 1800000, 100000, 100}, + }, + }, +}; + +static const struct msm_dsi_config msm8916_dsi_cfg = { + .io_offset = DSI_6G_REG_SHIFT, + .reg_cfg = { + .num = 4, + .regs = { + {"gdsc", -1, -1, -1, -1}, + {"vdd", 2850000, 2850000, 100000, 100}, + {"vdda", 1200000, 1200000, 100000, 100}, + {"vddio", 1800000, 1800000, 100000, 100}, + }, + }, +}; + +static const struct msm_dsi_config msm8994_dsi_cfg = { + .io_offset = DSI_6G_REG_SHIFT, + .reg_cfg = { + .num = 7, + .regs = { + {"gdsc", -1, -1, -1, -1}, + {"vdda", 1250000, 1250000, 100000, 100}, + {"vddio", 1800000, 1800000, 100000, 100}, + {"vcca", 1000000, 1000000, 10000, 100}, + {"vdd", 1800000, 1800000, 100000, 100}, + {"lab_reg", -1, -1, -1, -1}, + {"ibb_reg", -1, -1, -1, -1}, + }, + } +}; + +static const struct msm_dsi_cfg_handler dsi_cfg_handlers[] = { + {MSM_DSI_VER_MAJOR_V2, U32_MAX, &dsi_v2_cfg}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_0, + &msm8974_apq8084_dsi_cfg}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_1, + &msm8974_apq8084_dsi_cfg}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_1_1, + &msm8974_apq8084_dsi_cfg}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_2, + &msm8974_apq8084_dsi_cfg}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_3, &msm8994_dsi_cfg}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_3_1, &msm8916_dsi_cfg}, +}; + +const struct msm_dsi_cfg_handler *msm_dsi_cfg_get(u32 major, u32 minor) +{ + const struct msm_dsi_cfg_handler *cfg_hnd = NULL; + int i; + + for (i = ARRAY_SIZE(dsi_cfg_handlers) - 1; i >= 0; i--) { + if ((dsi_cfg_handlers[i].major == major) && + (dsi_cfg_handlers[i].minor == minor)) { + cfg_hnd = &dsi_cfg_handlers[i]; + break; + } + } + + return cfg_hnd; +} + diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.h b/drivers/gpu/drm/msm/dsi/dsi_cfg.h new file mode 100644 index 000000000000..4cf887240177 --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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. + */ + +#ifndef __MSM_DSI_CFG_H__ +#define __MSM_DSI_CFG_H__ + +#include "dsi.h" + +#define MSM_DSI_VER_MAJOR_V2 0x02 +#define MSM_DSI_VER_MAJOR_6G 0x03 +#define MSM_DSI_6G_VER_MINOR_V1_0 0x10000000 +#define MSM_DSI_6G_VER_MINOR_V1_1 0x10010000 +#define MSM_DSI_6G_VER_MINOR_V1_1_1 0x10010001 +#define MSM_DSI_6G_VER_MINOR_V1_2 0x10020000 +#define MSM_DSI_6G_VER_MINOR_V1_3 0x10030000 +#define MSM_DSI_6G_VER_MINOR_V1_3_1 0x10030001 + +#define DSI_6G_REG_SHIFT 4 + +struct msm_dsi_config { + u32 io_offset; + struct dsi_reg_config reg_cfg; +}; + +struct msm_dsi_cfg_handler { + u32 major; + u32 minor; + const struct msm_dsi_config *cfg; +}; + +const struct msm_dsi_cfg_handler *msm_dsi_cfg_get(u32 major, u32 minor); + +#endif /* __MSM_DSI_CFG_H__ */ + diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index de0400923303..8d82973fe9db 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -20,103 +20,15 @@ #include <linux/of_device.h> #include <linux/of_gpio.h> #include <linux/of_irq.h> +#include <linux/pinctrl/consumer.h> +#include <linux/of_graph.h> #include <linux/regulator/consumer.h> #include <linux/spinlock.h> #include <video/mipi_display.h> #include "dsi.h" #include "dsi.xml.h" - -#define MSM_DSI_VER_MAJOR_V2 0x02 -#define MSM_DSI_VER_MAJOR_6G 0x03 -#define MSM_DSI_6G_VER_MINOR_V1_0 0x10000000 -#define MSM_DSI_6G_VER_MINOR_V1_1 0x10010000 -#define MSM_DSI_6G_VER_MINOR_V1_1_1 0x10010001 -#define MSM_DSI_6G_VER_MINOR_V1_2 0x10020000 -#define MSM_DSI_6G_VER_MINOR_V1_3_1 0x10030001 - -#define DSI_6G_REG_SHIFT 4 - -struct dsi_config { - u32 major; - u32 minor; - u32 io_offset; - struct dsi_reg_config reg_cfg; -}; - -static const struct dsi_config dsi_cfgs[] = { - {MSM_DSI_VER_MAJOR_V2, 0, 0, {0,} }, - { /* 8974 v1 */ - .major = MSM_DSI_VER_MAJOR_6G, - .minor = MSM_DSI_6G_VER_MINOR_V1_0, - .io_offset = DSI_6G_REG_SHIFT, - .reg_cfg = { - .num = 4, - .regs = { - {"gdsc", -1, -1, -1, -1}, - {"vdd", 3000000, 3000000, 150000, 100}, - {"vdda", 1200000, 1200000, 100000, 100}, - {"vddio", 1800000, 1800000, 100000, 100}, - }, - }, - }, - { /* 8974 v2 */ - .major = MSM_DSI_VER_MAJOR_6G, - .minor = MSM_DSI_6G_VER_MINOR_V1_1, - .io_offset = DSI_6G_REG_SHIFT, - .reg_cfg = { - .num = 4, - .regs = { - {"gdsc", -1, -1, -1, -1}, - {"vdd", 3000000, 3000000, 150000, 100}, - {"vdda", 1200000, 1200000, 100000, 100}, - {"vddio", 1800000, 1800000, 100000, 100}, - }, - }, - }, - { /* 8974 v3 */ - .major = MSM_DSI_VER_MAJOR_6G, - .minor = MSM_DSI_6G_VER_MINOR_V1_1_1, - .io_offset = DSI_6G_REG_SHIFT, - .reg_cfg = { - .num = 4, - .regs = { - {"gdsc", -1, -1, -1, -1}, - {"vdd", 3000000, 3000000, 150000, 100}, - {"vdda", 1200000, 1200000, 100000, 100}, - {"vddio", 1800000, 1800000, 100000, 100}, - }, - }, - }, - { /* 8084 */ - .major = MSM_DSI_VER_MAJOR_6G, - .minor = MSM_DSI_6G_VER_MINOR_V1_2, - .io_offset = DSI_6G_REG_SHIFT, - .reg_cfg = { - .num = 4, - .regs = { - {"gdsc", -1, -1, -1, -1}, - {"vdd", 3000000, 3000000, 150000, 100}, - {"vdda", 1200000, 1200000, 100000, 100}, - {"vddio", 1800000, 1800000, 100000, 100}, - }, - }, - }, - { /* 8916 */ - .major = MSM_DSI_VER_MAJOR_6G, - .minor = MSM_DSI_6G_VER_MINOR_V1_3_1, - .io_offset = DSI_6G_REG_SHIFT, - .reg_cfg = { - .num = 4, - .regs = { - {"gdsc", -1, -1, -1, -1}, - {"vdd", 2850000, 2850000, 100000, 100}, - {"vdda", 1200000, 1200000, 100000, 100}, - {"vddio", 1800000, 1800000, 100000, 100}, - }, - }, - }, -}; +#include "dsi_cfg.h" static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor) { @@ -194,7 +106,7 @@ struct msm_dsi_host { struct gpio_desc *disp_en_gpio; struct gpio_desc *te_gpio; - const struct dsi_config *cfg; + const struct msm_dsi_cfg_handler *cfg_hnd; struct completion dma_comp; struct completion video_comp; @@ -212,8 +124,8 @@ struct msm_dsi_host { struct drm_display_mode *mode; - /* Panel info */ - struct device_node *panel_node; + /* connected device info */ + struct device_node *device_node; unsigned int channel; unsigned int lanes; enum mipi_dsi_pixel_format format; @@ -239,61 +151,58 @@ static u32 dsi_get_bpp(const enum mipi_dsi_pixel_format fmt) static inline u32 dsi_read(struct msm_dsi_host *msm_host, u32 reg) { - return msm_readl(msm_host->ctrl_base + msm_host->cfg->io_offset + reg); + return msm_readl(msm_host->ctrl_base + reg); } static inline void dsi_write(struct msm_dsi_host *msm_host, u32 reg, u32 data) { - msm_writel(data, msm_host->ctrl_base + msm_host->cfg->io_offset + reg); + msm_writel(data, msm_host->ctrl_base + reg); } static int dsi_host_regulator_enable(struct msm_dsi_host *msm_host); static void dsi_host_regulator_disable(struct msm_dsi_host *msm_host); -static const struct dsi_config *dsi_get_config(struct msm_dsi_host *msm_host) +static const struct msm_dsi_cfg_handler *dsi_get_config( + struct msm_dsi_host *msm_host) { - const struct dsi_config *cfg; + const struct msm_dsi_cfg_handler *cfg_hnd = NULL; struct regulator *gdsc_reg; - int i, ret; + int ret; u32 major = 0, minor = 0; gdsc_reg = regulator_get(&msm_host->pdev->dev, "gdsc"); if (IS_ERR(gdsc_reg)) { pr_err("%s: cannot get gdsc\n", __func__); - goto fail; + goto exit; } ret = regulator_enable(gdsc_reg); if (ret) { pr_err("%s: unable to enable gdsc\n", __func__); - regulator_put(gdsc_reg); - goto fail; + goto put_gdsc; } ret = clk_prepare_enable(msm_host->ahb_clk); if (ret) { pr_err("%s: unable to enable ahb_clk\n", __func__); - regulator_disable(gdsc_reg); - regulator_put(gdsc_reg); - goto fail; + goto disable_gdsc; } ret = dsi_get_version(msm_host->ctrl_base, &major, &minor); - - clk_disable_unprepare(msm_host->ahb_clk); - regulator_disable(gdsc_reg); - regulator_put(gdsc_reg); if (ret) { pr_err("%s: Invalid version\n", __func__); - goto fail; + goto disable_clks; } - for (i = 0; i < ARRAY_SIZE(dsi_cfgs); i++) { - cfg = dsi_cfgs + i; - if ((cfg->major == major) && (cfg->minor == minor)) - return cfg; - } - pr_err("%s: Version %x:%x not support\n", __func__, major, minor); + cfg_hnd = msm_dsi_cfg_get(major, minor); -fail: - return NULL; + DBG("%s: Version %x:%x\n", __func__, major, minor); + +disable_clks: + clk_disable_unprepare(msm_host->ahb_clk); +disable_gdsc: + regulator_disable(gdsc_reg); +put_gdsc: + regulator_put(gdsc_reg); +exit: + return cfg_hnd; } static inline struct msm_dsi_host *to_msm_dsi_host(struct mipi_dsi_host *host) @@ -304,8 +213,8 @@ static inline struct msm_dsi_host *to_msm_dsi_host(struct mipi_dsi_host *host) static void dsi_host_regulator_disable(struct msm_dsi_host *msm_host) { struct regulator_bulk_data *s = msm_host->supplies; - const struct dsi_reg_entry *regs = msm_host->cfg->reg_cfg.regs; - int num = msm_host->cfg->reg_cfg.num; + const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; + int num = msm_host->cfg_hnd->cfg->reg_cfg.num; int i; DBG(""); @@ -320,8 +229,8 @@ static void dsi_host_regulator_disable(struct msm_dsi_host *msm_host) static int dsi_host_regulator_enable(struct msm_dsi_host *msm_host) { struct regulator_bulk_data *s = msm_host->supplies; - const struct dsi_reg_entry *regs = msm_host->cfg->reg_cfg.regs; - int num = msm_host->cfg->reg_cfg.num; + const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; + int num = msm_host->cfg_hnd->cfg->reg_cfg.num; int ret, i; DBG(""); @@ -354,8 +263,8 @@ fail: static int dsi_regulator_init(struct msm_dsi_host *msm_host) { struct regulator_bulk_data *s = msm_host->supplies; - const struct dsi_reg_entry *regs = msm_host->cfg->reg_cfg.regs; - int num = msm_host->cfg->reg_cfg.num; + const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; + int num = msm_host->cfg_hnd->cfg->reg_cfg.num; int i, ret; for (i = 0; i < num; i++) @@ -697,6 +606,7 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, { u32 flags = msm_host->mode_flags; enum mipi_dsi_pixel_format mipi_fmt = msm_host->format; + const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; u32 data = 0; if (!enable) { @@ -750,8 +660,8 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, data |= DSI_TRIG_CTRL_MDP_TRIGGER(TRIGGER_NONE); data |= DSI_TRIG_CTRL_DMA_TRIGGER(TRIGGER_SW); data |= DSI_TRIG_CTRL_STREAM(msm_host->channel); - if ((msm_host->cfg->major == MSM_DSI_VER_MAJOR_6G) && - (msm_host->cfg->minor >= MSM_DSI_6G_VER_MINOR_V1_2)) + if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && + (cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_2)) data |= DSI_TRIG_CTRL_BLOCK_DMA_WITHIN_FRAME; dsi_write(msm_host, REG_DSI_TRIG_CTRL, data); @@ -1257,7 +1167,11 @@ static void dsi_dln0_phy_err(struct msm_dsi_host *msm_host) status = dsi_read(msm_host, REG_DSI_DLN0_PHY_ERR); - if (status) { + if (status & (DSI_DLN0_PHY_ERR_DLN0_ERR_ESC | + DSI_DLN0_PHY_ERR_DLN0_ERR_SYNC_ESC | + DSI_DLN0_PHY_ERR_DLN0_ERR_CONTROL | + DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP0 | + DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP1)) { dsi_write(msm_host, REG_DSI_DLN0_PHY_ERR, status); msm_host->err_work_state |= DSI_ERR_STATE_DLN0_PHY; } @@ -1359,7 +1273,8 @@ static int dsi_host_init_panel_gpios(struct msm_dsi_host *msm_host, return PTR_ERR(msm_host->disp_en_gpio); } - msm_host->te_gpio = devm_gpiod_get(panel_device, "disp-te", GPIOD_IN); + msm_host->te_gpio = devm_gpiod_get_optional(panel_device, "disp-te", + GPIOD_IN); if (IS_ERR(msm_host->te_gpio)) { DBG("cannot get disp-te-gpios %ld", PTR_ERR(msm_host->te_gpio)); return PTR_ERR(msm_host->te_gpio); @@ -1379,7 +1294,7 @@ static int dsi_host_attach(struct mipi_dsi_host *host, msm_host->format = dsi->format; msm_host->mode_flags = dsi->mode_flags; - msm_host->panel_node = dsi->dev.of_node; + WARN_ON(dsi->dev.of_node != msm_host->device_node); /* Some gpios defined in panel DT need to be controlled by host */ ret = dsi_host_init_panel_gpios(msm_host, &dsi->dev); @@ -1398,7 +1313,7 @@ static int dsi_host_detach(struct mipi_dsi_host *host, { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); - msm_host->panel_node = NULL; + msm_host->device_node = NULL; DBG("id=%d", msm_host->id); if (msm_host->dev) @@ -1429,6 +1344,48 @@ static struct mipi_dsi_host_ops dsi_host_ops = { .transfer = dsi_host_transfer, }; +static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) +{ + struct device *dev = &msm_host->pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *endpoint, *device_node; + int ret; + + ret = of_property_read_u32(np, "qcom,dsi-host-index", &msm_host->id); + if (ret) { + dev_err(dev, "%s: host index not specified, ret=%d\n", + __func__, ret); + return ret; + } + + /* + * Get the first endpoint node. In our case, dsi has one output port + * to which the panel is connected. Don't return an error if a port + * isn't defined. It's possible that there is nothing connected to + * the dsi output. + */ + endpoint = of_graph_get_next_endpoint(np, NULL); + if (!endpoint) { + dev_dbg(dev, "%s: no endpoint\n", __func__); + return 0; + } + + /* Get panel node from the output port's endpoint data */ + device_node = of_graph_get_remote_port_parent(endpoint); + if (!device_node) { + dev_err(dev, "%s: no valid device\n", __func__); + of_node_put(endpoint); + return -ENODEV; + } + + of_node_put(endpoint); + of_node_put(device_node); + + msm_host->device_node = device_node; + + return 0; +} + int msm_dsi_host_init(struct msm_dsi *msm_dsi) { struct msm_dsi_host *msm_host = NULL; @@ -1443,15 +1400,13 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) goto fail; } - ret = of_property_read_u32(pdev->dev.of_node, - "qcom,dsi-host-index", &msm_host->id); + msm_host->pdev = pdev; + + ret = dsi_host_parse_dt(msm_host); if (ret) { - dev_err(&pdev->dev, - "%s: host index not specified, ret=%d\n", - __func__, ret); + pr_err("%s: failed to parse dt\n", __func__); goto fail; } - msm_host->pdev = pdev; ret = dsi_clk_init(msm_host); if (ret) { @@ -1466,13 +1421,16 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) goto fail; } - msm_host->cfg = dsi_get_config(msm_host); - if (!msm_host->cfg) { + msm_host->cfg_hnd = dsi_get_config(msm_host); + if (!msm_host->cfg_hnd) { ret = -EINVAL; pr_err("%s: get config failed\n", __func__); goto fail; } + /* fixup base address by io offset */ + msm_host->ctrl_base += msm_host->cfg_hnd->cfg->io_offset; + ret = dsi_regulator_init(msm_host); if (ret) { pr_err("%s: regulator init failed\n", __func__); @@ -1559,7 +1517,6 @@ int msm_dsi_host_modeset_init(struct mipi_dsi_host *host, int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); - struct device_node *node; int ret; /* Register mipi dsi host */ @@ -1577,14 +1534,13 @@ int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer) * It makes sure panel is connected when fbcon detects * connector status and gets the proper display mode to * create framebuffer. + * Don't try to defer if there is nothing connected to the dsi + * output */ - if (check_defer) { - node = of_get_child_by_name(msm_host->pdev->dev.of_node, - "panel"); - if (node) { - if (!of_drm_find_panel(node)) + if (check_defer && msm_host->device_node) { + if (!of_drm_find_panel(msm_host->device_node)) + if (!of_drm_find_bridge(msm_host->device_node)) return -EPROBE_DEFER; - } } } @@ -1663,6 +1619,7 @@ int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host, const struct mipi_dsi_msg *msg) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; int data_byte, rx_byte, dlen, end; int short_response, diff, pkt_size, ret = 0; char cmd; @@ -1704,8 +1661,8 @@ int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host, return -EINVAL; } - if ((msm_host->cfg->major == MSM_DSI_VER_MAJOR_6G) && - (msm_host->cfg->minor >= MSM_DSI_6G_VER_MINOR_V1_1)) { + if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && + (cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_1)) { /* Clear the RDBK_DATA registers */ dsi_write(msm_host, REG_DSI_RDBK_DATA_CTRL, DSI_RDBK_DATA_CTRL_CLR); @@ -1919,6 +1876,13 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host) goto fail_disable_reg; } + ret = pinctrl_pm_select_default_state(&msm_host->pdev->dev); + if (ret) { + pr_err("%s: failed to set pinctrl default state, %d\n", + __func__, ret); + goto fail_disable_clk; + } + dsi_timing_setup(msm_host); dsi_sw_reset(msm_host); dsi_ctrl_config(msm_host, true, clk_pre, clk_post); @@ -1931,6 +1895,8 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host) return 0; +fail_disable_clk: + dsi_clk_ctrl(msm_host, 0); fail_disable_reg: dsi_host_regulator_disable(msm_host); unlock_ret: @@ -1953,6 +1919,8 @@ int msm_dsi_host_power_off(struct mipi_dsi_host *host) if (msm_host->disp_en_gpio) gpiod_set_value(msm_host->disp_en_gpio, 0); + pinctrl_pm_select_sleep_state(&msm_host->pdev->dev); + msm_dsi_manager_phy_disable(msm_host->id); dsi_clk_ctrl(msm_host, 0); @@ -1993,10 +1961,16 @@ struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host, struct msm_dsi_host *msm_host = to_msm_dsi_host(host); struct drm_panel *panel; - panel = of_drm_find_panel(msm_host->panel_node); + panel = of_drm_find_panel(msm_host->device_node); if (panel_flags) *panel_flags = msm_host->mode_flags; return panel; } +struct drm_bridge *msm_dsi_host_get_bridge(struct mipi_dsi_host *host) +{ + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + + return of_drm_find_bridge(msm_host->device_node); +} diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index 87ac6612b6f8..0455ff75074a 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -14,19 +14,31 @@ #include "msm_kms.h" #include "dsi.h" +#define DSI_CLOCK_MASTER DSI_0 +#define DSI_CLOCK_SLAVE DSI_1 + +#define DSI_LEFT DSI_0 +#define DSI_RIGHT DSI_1 + +/* According to the current drm framework sequence, take the encoder of + * DSI_1 as master encoder + */ +#define DSI_ENCODER_MASTER DSI_1 +#define DSI_ENCODER_SLAVE DSI_0 + struct msm_dsi_manager { struct msm_dsi *dsi[DSI_MAX]; - bool is_dual_panel; + bool is_dual_dsi; bool is_sync_needed; - int master_panel_id; + int master_dsi_link_id; }; static struct msm_dsi_manager msm_dsim_glb; -#define IS_DUAL_PANEL() (msm_dsim_glb.is_dual_panel) +#define IS_DUAL_DSI() (msm_dsim_glb.is_dual_dsi) #define IS_SYNC_NEEDED() (msm_dsim_glb.is_sync_needed) -#define IS_MASTER_PANEL(id) (msm_dsim_glb.master_panel_id == id) +#define IS_MASTER_DSI_LINK(id) (msm_dsim_glb.master_dsi_link_id == id) static inline struct msm_dsi *dsi_mgr_get_dsi(int id) { @@ -38,23 +50,23 @@ static inline struct msm_dsi *dsi_mgr_get_other_dsi(int id) return msm_dsim_glb.dsi[(id + 1) % DSI_MAX]; } -static int dsi_mgr_parse_dual_panel(struct device_node *np, int id) +static int dsi_mgr_parse_dual_dsi(struct device_node *np, int id) { struct msm_dsi_manager *msm_dsim = &msm_dsim_glb; - /* We assume 2 dsi nodes have the same information of dual-panel and + /* We assume 2 dsi nodes have the same information of dual-dsi and * sync-mode, and only one node specifies master in case of dual mode. */ - if (!msm_dsim->is_dual_panel) - msm_dsim->is_dual_panel = of_property_read_bool( - np, "qcom,dual-panel-mode"); + if (!msm_dsim->is_dual_dsi) + msm_dsim->is_dual_dsi = of_property_read_bool( + np, "qcom,dual-dsi-mode"); - if (msm_dsim->is_dual_panel) { - if (of_property_read_bool(np, "qcom,master-panel")) - msm_dsim->master_panel_id = id; + if (msm_dsim->is_dual_dsi) { + if (of_property_read_bool(np, "qcom,master-dsi")) + msm_dsim->master_dsi_link_id = id; if (!msm_dsim->is_sync_needed) msm_dsim->is_sync_needed = of_property_read_bool( - np, "qcom,sync-dual-panel"); + np, "qcom,sync-dual-dsi"); } return 0; @@ -68,7 +80,7 @@ static int dsi_mgr_host_register(int id) struct msm_dsi_pll *src_pll; int ret; - if (!IS_DUAL_PANEL()) { + if (!IS_DUAL_DSI()) { ret = msm_dsi_host_register(msm_dsi->host, true); if (ret) return ret; @@ -78,9 +90,9 @@ static int dsi_mgr_host_register(int id) } else if (!other_dsi) { ret = 0; } else { - struct msm_dsi *mdsi = IS_MASTER_PANEL(id) ? + struct msm_dsi *mdsi = IS_MASTER_DSI_LINK(id) ? msm_dsi : other_dsi; - struct msm_dsi *sdsi = IS_MASTER_PANEL(id) ? + struct msm_dsi *sdsi = IS_MASTER_DSI_LINK(id) ? other_dsi : msm_dsi; /* Register slave host first, so that slave DSI device * has a chance to probe, and do not block the master @@ -144,28 +156,28 @@ static enum drm_connector_status dsi_mgr_connector_detect( DBG("id=%d", id); if (!msm_dsi->panel) { msm_dsi->panel = msm_dsi_host_get_panel(msm_dsi->host, - &msm_dsi->panel_flags); + &msm_dsi->device_flags); /* There is only 1 panel in the global panel list - * for dual panel mode. Therefore slave dsi should get + * for dual DSI mode. Therefore slave dsi should get * the drm_panel instance from master dsi, and * keep using the panel flags got from the current DSI link. */ - if (!msm_dsi->panel && IS_DUAL_PANEL() && - !IS_MASTER_PANEL(id) && other_dsi) + if (!msm_dsi->panel && IS_DUAL_DSI() && + !IS_MASTER_DSI_LINK(id) && other_dsi) msm_dsi->panel = msm_dsi_host_get_panel( other_dsi->host, NULL); - if (msm_dsi->panel && IS_DUAL_PANEL()) + if (msm_dsi->panel && IS_DUAL_DSI()) drm_object_attach_property(&connector->base, connector->dev->mode_config.tile_property, 0); - /* Set split display info to kms once dual panel is connected - * to both hosts + /* Set split display info to kms once dual DSI panel is + * connected to both hosts. */ - if (msm_dsi->panel && IS_DUAL_PANEL() && + if (msm_dsi->panel && IS_DUAL_DSI() && other_dsi && other_dsi->panel) { - bool cmd_mode = !(msm_dsi->panel_flags & + bool cmd_mode = !(msm_dsi->device_flags & MIPI_DSI_MODE_VIDEO); struct drm_encoder *encoder = msm_dsi_get_encoder( dsi_mgr_get_dsi(DSI_ENCODER_MASTER)); @@ -176,7 +188,7 @@ static enum drm_connector_status dsi_mgr_connector_detect( kms->funcs->set_split_display(kms, encoder, slave_enc, cmd_mode); else - pr_err("mdp does not support dual panel\n"); + pr_err("mdp does not support dual DSI\n"); } } @@ -273,7 +285,7 @@ static int dsi_mgr_connector_get_modes(struct drm_connector *connector) if (!num) return 0; - if (IS_DUAL_PANEL()) { + if (IS_DUAL_DSI()) { /* report half resolution to user */ dsi_dual_connector_fix_modes(connector); ret = dsi_dual_connector_tile_init(connector, id); @@ -328,11 +340,12 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); struct mipi_dsi_host *host = msm_dsi->host; struct drm_panel *panel = msm_dsi->panel; - bool is_dual_panel = IS_DUAL_PANEL(); + bool is_dual_dsi = IS_DUAL_DSI(); int ret; DBG("id=%d", id); - if (!panel || (is_dual_panel && (DSI_1 == id))) + if (!msm_dsi_device_connected(msm_dsi) || + (is_dual_dsi && (DSI_1 == id))) return; ret = msm_dsi_host_power_on(host); @@ -341,7 +354,7 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) goto host_on_fail; } - if (is_dual_panel && msm_dsi1) { + if (is_dual_dsi && msm_dsi1) { ret = msm_dsi_host_power_on(msm_dsi1->host); if (ret) { pr_err("%s: power on host1 failed, %d\n", @@ -353,10 +366,13 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) /* Always call panel functions once, because even for dual panels, * there is only one drm_panel instance. */ - ret = drm_panel_prepare(panel); - if (ret) { - pr_err("%s: prepare panel %d failed, %d\n", __func__, id, ret); - goto panel_prep_fail; + if (panel) { + ret = drm_panel_prepare(panel); + if (ret) { + pr_err("%s: prepare panel %d failed, %d\n", __func__, + id, ret); + goto panel_prep_fail; + } } ret = msm_dsi_host_enable(host); @@ -365,7 +381,7 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) goto host_en_fail; } - if (is_dual_panel && msm_dsi1) { + if (is_dual_dsi && msm_dsi1) { ret = msm_dsi_host_enable(msm_dsi1->host); if (ret) { pr_err("%s: enable host1 failed, %d\n", __func__, ret); @@ -373,23 +389,27 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) } } - ret = drm_panel_enable(panel); - if (ret) { - pr_err("%s: enable panel %d failed, %d\n", __func__, id, ret); - goto panel_en_fail; + if (panel) { + ret = drm_panel_enable(panel); + if (ret) { + pr_err("%s: enable panel %d failed, %d\n", __func__, id, + ret); + goto panel_en_fail; + } } return; panel_en_fail: - if (is_dual_panel && msm_dsi1) + if (is_dual_dsi && msm_dsi1) msm_dsi_host_disable(msm_dsi1->host); host1_en_fail: msm_dsi_host_disable(host); host_en_fail: - drm_panel_unprepare(panel); + if (panel) + drm_panel_unprepare(panel); panel_prep_fail: - if (is_dual_panel && msm_dsi1) + if (is_dual_dsi && msm_dsi1) msm_dsi_host_power_off(msm_dsi1->host); host1_on_fail: msm_dsi_host_power_off(host); @@ -414,37 +434,44 @@ static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge) struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); struct mipi_dsi_host *host = msm_dsi->host; struct drm_panel *panel = msm_dsi->panel; - bool is_dual_panel = IS_DUAL_PANEL(); + bool is_dual_dsi = IS_DUAL_DSI(); int ret; DBG("id=%d", id); - if (!panel || (is_dual_panel && (DSI_1 == id))) + if (!msm_dsi_device_connected(msm_dsi) || + (is_dual_dsi && (DSI_1 == id))) return; - ret = drm_panel_disable(panel); - if (ret) - pr_err("%s: Panel %d OFF failed, %d\n", __func__, id, ret); + if (panel) { + ret = drm_panel_disable(panel); + if (ret) + pr_err("%s: Panel %d OFF failed, %d\n", __func__, id, + ret); + } ret = msm_dsi_host_disable(host); if (ret) pr_err("%s: host %d disable failed, %d\n", __func__, id, ret); - if (is_dual_panel && msm_dsi1) { + if (is_dual_dsi && msm_dsi1) { ret = msm_dsi_host_disable(msm_dsi1->host); if (ret) pr_err("%s: host1 disable failed, %d\n", __func__, ret); } - ret = drm_panel_unprepare(panel); - if (ret) - pr_err("%s: Panel %d unprepare failed,%d\n", __func__, id, ret); + if (panel) { + ret = drm_panel_unprepare(panel); + if (ret) + pr_err("%s: Panel %d unprepare failed,%d\n", __func__, + id, ret); + } ret = msm_dsi_host_power_off(host); if (ret) pr_err("%s: host %d power off failed,%d\n", __func__, id, ret); - if (is_dual_panel && msm_dsi1) { + if (is_dual_dsi && msm_dsi1) { ret = msm_dsi_host_power_off(msm_dsi1->host); if (ret) pr_err("%s: host1 power off failed, %d\n", @@ -460,7 +487,7 @@ static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge, struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); struct mipi_dsi_host *host = msm_dsi->host; - bool is_dual_panel = IS_DUAL_PANEL(); + bool is_dual_dsi = IS_DUAL_DSI(); DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", mode->base.id, mode->name, @@ -471,11 +498,11 @@ static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge, mode->vsync_end, mode->vtotal, mode->type, mode->flags); - if (is_dual_panel && (DSI_1 == id)) + if (is_dual_dsi && (DSI_1 == id)) return; msm_dsi_host_set_display_mode(host, adjusted_mode); - if (is_dual_panel && other_dsi) + if (is_dual_dsi && other_dsi) msm_dsi_host_set_display_mode(other_dsi->host, adjusted_mode); } @@ -503,7 +530,7 @@ static const struct drm_bridge_funcs dsi_mgr_bridge_funcs = { .mode_set = dsi_mgr_bridge_mode_set, }; -/* initialize connector */ +/* initialize connector when we're connected to a drm_panel */ struct drm_connector *msm_dsi_manager_connector_init(u8 id) { struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); @@ -588,6 +615,53 @@ fail: return ERR_PTR(ret); } +struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id) +{ + struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); + struct drm_device *dev = msm_dsi->dev; + struct drm_encoder *encoder; + struct drm_bridge *int_bridge, *ext_bridge; + struct drm_connector *connector; + struct list_head *connector_list; + + int_bridge = msm_dsi->bridge; + ext_bridge = msm_dsi->external_bridge = + msm_dsi_host_get_bridge(msm_dsi->host); + + /* + * HACK: we may not know the external DSI bridge device's mode + * flags here. We'll get to know them only when the device + * attaches to the dsi host. For now, assume the bridge supports + * DSI video mode + */ + encoder = msm_dsi->encoders[MSM_DSI_VIDEO_ENCODER_ID]; + + /* link the internal dsi bridge to the external bridge */ + int_bridge->next = ext_bridge; + /* set the external bridge's encoder as dsi's encoder */ + ext_bridge->encoder = encoder; + + drm_bridge_attach(dev, ext_bridge); + + /* + * we need the drm_connector created by the external bridge + * driver (or someone else) to feed it to our driver's + * priv->connector[] list, mainly for msm_fbdev_init() + */ + connector_list = &dev->mode_config.connector_list; + + list_for_each_entry(connector, connector_list, head) { + int i; + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == encoder->base.id) + return connector; + } + } + + return ERR_PTR(-ENODEV); +} + void msm_dsi_manager_bridge_destroy(struct drm_bridge *bridge) { } @@ -598,12 +672,29 @@ int msm_dsi_manager_phy_enable(int id, { struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); struct msm_dsi_phy *phy = msm_dsi->phy; + int src_pll_id = IS_DUAL_DSI() ? DSI_CLOCK_MASTER : id; + struct msm_dsi_pll *pll = msm_dsi_phy_get_pll(msm_dsi->phy); int ret; - ret = msm_dsi_phy_enable(phy, IS_DUAL_PANEL(), bit_rate, esc_rate); + ret = msm_dsi_phy_enable(phy, src_pll_id, bit_rate, esc_rate); if (ret) return ret; + /* + * Reset DSI PHY silently changes its PLL registers to reset status, + * which will confuse clock driver and result in wrong output rate of + * link clocks. Restore PLL status if its PLL is being used as clock + * source. + */ + if (!IS_DUAL_DSI() || (id == DSI_CLOCK_MASTER)) { + ret = msm_dsi_pll_restore_state(pll); + if (ret) { + pr_err("%s: failed to restore pll state\n", __func__); + msm_dsi_phy_disable(phy); + return ret; + } + } + msm_dsi->phy_enabled = true; msm_dsi_phy_get_clk_pre_post(phy, clk_pre, clk_post); @@ -616,13 +707,18 @@ void msm_dsi_manager_phy_disable(int id) struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER); struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE); struct msm_dsi_phy *phy = msm_dsi->phy; + struct msm_dsi_pll *pll = msm_dsi_phy_get_pll(msm_dsi->phy); + + /* Save PLL status if it is a clock source */ + if (!IS_DUAL_DSI() || (id == DSI_CLOCK_MASTER)) + msm_dsi_pll_save_state(pll); /* disable DSI phy * In dual-dsi configuration, the phy should be disabled for the * first controller only when the second controller is disabled. */ msm_dsi->phy_enabled = false; - if (IS_DUAL_PANEL() && mdsi && sdsi) { + if (IS_DUAL_DSI() && mdsi && sdsi) { if (!mdsi->phy_enabled && !sdsi->phy_enabled) { msm_dsi_phy_disable(sdsi->phy); msm_dsi_phy_disable(mdsi->phy); @@ -713,9 +809,9 @@ int msm_dsi_manager_register(struct msm_dsi *msm_dsi) msm_dsim->dsi[id] = msm_dsi; - ret = dsi_mgr_parse_dual_panel(msm_dsi->pdev->dev.of_node, id); + ret = dsi_mgr_parse_dual_dsi(msm_dsi->pdev->dev.of_node, id); if (ret) { - pr_err("%s: failed to parse dual panel info\n", __func__); + pr_err("%s: failed to parse dual DSI info\n", __func__); goto fail; } diff --git a/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h b/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h index 728152f3ef48..5de505e627be 100644 --- a/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h +++ b/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h @@ -8,19 +8,19 @@ 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 ( 676 bytes, from 2014-12-05 15:34:49) -- /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 ( 20915 bytes, from 2015-03-24 22:05:22) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2352 bytes, from 2015-04-12 15:02:42) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 35083 bytes, from 2015-04-12 15:04:03) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 22094 bytes, from 2015-05-12 12:45:23) -- /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 ( 1686 bytes, from 2014-10-31 16:48:57) -- /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 ( 29012 bytes, from 2015-05-12 12:45:23) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-12 12:45:23) - -Copyright (C) 2013-2014 by the following authors: +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2576 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 36021 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 26057 bytes, from 2015-08-14 21:47:57) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) + +Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) Permission is hereby granted, free of charge, to any person obtaining diff --git a/drivers/gpu/drm/msm/dsi/dsi_phy.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c index 2d3b33ce1cc5..401ff58d6893 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c @@ -12,142 +12,8 @@ */ #include <linux/platform_device.h> -#include <linux/regulator/consumer.h> -#include "dsi.h" -#include "dsi.xml.h" - -#define dsi_phy_read(offset) msm_readl((offset)) -#define dsi_phy_write(offset, data) msm_writel((data), (offset)) - -struct dsi_phy_ops { - int (*enable)(struct msm_dsi_phy *phy, bool is_dual_panel, - const unsigned long bit_rate, const unsigned long esc_rate); - int (*disable)(struct msm_dsi_phy *phy); -}; - -struct dsi_phy_cfg { - enum msm_dsi_phy_type type; - struct dsi_reg_config reg_cfg; - struct dsi_phy_ops ops; -}; - -struct dsi_dphy_timing { - u32 clk_pre; - u32 clk_post; - u32 clk_zero; - u32 clk_trail; - u32 clk_prepare; - u32 hs_exit; - u32 hs_zero; - u32 hs_prepare; - u32 hs_trail; - u32 hs_rqst; - u32 ta_go; - u32 ta_sure; - u32 ta_get; -}; - -struct msm_dsi_phy { - struct platform_device *pdev; - void __iomem *base; - void __iomem *reg_base; - int id; - - struct clk *ahb_clk; - struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX]; - - struct dsi_dphy_timing timing; - const struct dsi_phy_cfg *cfg; - - struct msm_dsi_pll *pll; -}; - -static int dsi_phy_regulator_init(struct msm_dsi_phy *phy) -{ - struct regulator_bulk_data *s = phy->supplies; - const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs; - struct device *dev = &phy->pdev->dev; - int num = phy->cfg->reg_cfg.num; - int i, ret; - - for (i = 0; i < num; i++) - s[i].supply = regs[i].name; - - ret = devm_regulator_bulk_get(&phy->pdev->dev, num, s); - if (ret < 0) { - dev_err(dev, "%s: failed to init regulator, ret=%d\n", - __func__, ret); - return ret; - } - - for (i = 0; i < num; i++) { - if ((regs[i].min_voltage >= 0) && (regs[i].max_voltage >= 0)) { - ret = regulator_set_voltage(s[i].consumer, - regs[i].min_voltage, regs[i].max_voltage); - if (ret < 0) { - dev_err(dev, - "regulator %d set voltage failed, %d\n", - i, ret); - return ret; - } - } - } - - return 0; -} - -static void dsi_phy_regulator_disable(struct msm_dsi_phy *phy) -{ - struct regulator_bulk_data *s = phy->supplies; - const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs; - int num = phy->cfg->reg_cfg.num; - int i; - - DBG(""); - for (i = num - 1; i >= 0; i--) - if (regs[i].disable_load >= 0) - regulator_set_load(s[i].consumer, - regs[i].disable_load); - - regulator_bulk_disable(num, s); -} - -static int dsi_phy_regulator_enable(struct msm_dsi_phy *phy) -{ - struct regulator_bulk_data *s = phy->supplies; - const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs; - struct device *dev = &phy->pdev->dev; - int num = phy->cfg->reg_cfg.num; - int ret, i; - - DBG(""); - for (i = 0; i < num; i++) { - if (regs[i].enable_load >= 0) { - ret = regulator_set_load(s[i].consumer, - regs[i].enable_load); - if (ret < 0) { - dev_err(dev, - "regulator %d set op mode failed, %d\n", - i, ret); - goto fail; - } - } - } - - ret = regulator_bulk_enable(num, s); - if (ret < 0) { - dev_err(dev, "regulator enable failed, %d\n", ret); - goto fail; - } - - return 0; - -fail: - for (i--; i >= 0; i--) - regulator_set_load(s[i].consumer, regs[i].disable_load); - return ret; -} +#include "dsi_phy.h" #define S_DIV_ROUND_UP(n, d) \ (((n) >= 0) ? (((n) + (d) - 1) / (d)) : (((n) - (d) + 1) / (d))) @@ -156,6 +22,7 @@ static inline s32 linear_inter(s32 tmax, s32 tmin, s32 percent, s32 min_result, bool even) { s32 v; + v = (tmax - tmin) * percent; v = S_DIV_ROUND_UP(v, 100) + tmin; if (even && (v & 0x1)) @@ -164,7 +31,7 @@ static inline s32 linear_inter(s32 tmax, s32 tmin, s32 percent, return max_t(s32, min_result, v); } -static void dsi_dphy_timing_calc_clk_zero(struct dsi_dphy_timing *timing, +static void dsi_dphy_timing_calc_clk_zero(struct msm_dsi_dphy_timing *timing, s32 ui, s32 coeff, s32 pcnt) { s32 tmax, tmin, clk_z; @@ -186,7 +53,7 @@ static void dsi_dphy_timing_calc_clk_zero(struct dsi_dphy_timing *timing, timing->clk_zero = clk_z + 8 - temp; } -static int dsi_dphy_timing_calc(struct dsi_dphy_timing *timing, +int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, const unsigned long bit_rate, const unsigned long esc_rate) { s32 ui, lpx; @@ -256,9 +123,8 @@ static int dsi_dphy_timing_calc(struct dsi_dphy_timing *timing, temp += 8 * ui + lpx; tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1; if (tmin > tmax) { - temp = linear_inter(2 * tmax, tmin, pcnt2, 0, false) >> 1; + temp = linear_inter(2 * tmax, tmin, pcnt2, 0, false); timing->clk_pre = temp >> 1; - temp = (2 * tmax - tmin) * pcnt2; } else { timing->clk_pre = linear_inter(tmax, tmin, pcnt2, 0, false); } @@ -276,130 +142,119 @@ static int dsi_dphy_timing_calc(struct dsi_dphy_timing *timing, return 0; } -static void dsi_28nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable) +void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg, + u32 bit_mask) { - void __iomem *base = phy->reg_base; + int phy_id = phy->id; + u32 val; - if (!enable) { - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 0); + if ((phy_id >= DSI_MAX) || (pll_id >= DSI_MAX)) return; - } - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0, 0x0); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 1); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_5, 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_3, 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_2, 0x3); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_1, 0x9); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0, 0x7); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_4, 0x20); + val = dsi_phy_read(phy->base + reg); + + if (phy->cfg->src_pll_truthtable[phy_id][pll_id]) + dsi_phy_write(phy->base + reg, val | bit_mask); + else + dsi_phy_write(phy->base + reg, val & (~bit_mask)); } -static int dsi_28nm_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel, - const unsigned long bit_rate, const unsigned long esc_rate) +static int dsi_phy_regulator_init(struct msm_dsi_phy *phy) { - struct dsi_dphy_timing *timing = &phy->timing; - int i; - void __iomem *base = phy->base; + struct regulator_bulk_data *s = phy->supplies; + const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs; + struct device *dev = &phy->pdev->dev; + int num = phy->cfg->reg_cfg.num; + int i, ret; - DBG(""); + for (i = 0; i < num; i++) + s[i].supply = regs[i].name; - if (dsi_dphy_timing_calc(timing, bit_rate, esc_rate)) { - pr_err("%s: D-PHY timing calculation failed\n", __func__); - return -EINVAL; + ret = devm_regulator_bulk_get(dev, num, s); + if (ret < 0) { + dev_err(dev, "%s: failed to init regulator, ret=%d\n", + __func__, ret); + return ret; } - dsi_phy_write(base + REG_DSI_28nm_PHY_STRENGTH_0, 0xff); - - dsi_28nm_phy_regulator_ctrl(phy, true); - - dsi_phy_write(base + REG_DSI_28nm_PHY_LDO_CNTRL, 0x00); - - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_0, - DSI_28nm_PHY_TIMING_CTRL_0_CLK_ZERO(timing->clk_zero)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_1, - DSI_28nm_PHY_TIMING_CTRL_1_CLK_TRAIL(timing->clk_trail)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_2, - DSI_28nm_PHY_TIMING_CTRL_2_CLK_PREPARE(timing->clk_prepare)); - if (timing->clk_zero & BIT(8)) - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_3, - DSI_28nm_PHY_TIMING_CTRL_3_CLK_ZERO_8); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_4, - DSI_28nm_PHY_TIMING_CTRL_4_HS_EXIT(timing->hs_exit)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_5, - DSI_28nm_PHY_TIMING_CTRL_5_HS_ZERO(timing->hs_zero)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_6, - DSI_28nm_PHY_TIMING_CTRL_6_HS_PREPARE(timing->hs_prepare)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_7, - DSI_28nm_PHY_TIMING_CTRL_7_HS_TRAIL(timing->hs_trail)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_8, - DSI_28nm_PHY_TIMING_CTRL_8_HS_RQST(timing->hs_rqst)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_9, - DSI_28nm_PHY_TIMING_CTRL_9_TA_GO(timing->ta_go) | - DSI_28nm_PHY_TIMING_CTRL_9_TA_SURE(timing->ta_sure)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_10, - DSI_28nm_PHY_TIMING_CTRL_10_TA_GET(timing->ta_get)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_11, - DSI_28nm_PHY_TIMING_CTRL_11_TRIG3_CMD(0)); - - dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_1, 0x00); - dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_0, 0x5f); - - dsi_phy_write(base + REG_DSI_28nm_PHY_STRENGTH_1, 0x6); - - for (i = 0; i < 4; i++) { - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_0(i), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_1(i), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_2(i), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_3(i), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_DATAPATH(i), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_DEBUG_SEL(i), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_STR_0(i), 0x1); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_STR_1(i), 0x97); + for (i = 0; i < num; i++) { + if ((regs[i].min_voltage >= 0) && (regs[i].max_voltage >= 0)) { + ret = regulator_set_voltage(s[i].consumer, + regs[i].min_voltage, regs[i].max_voltage); + if (ret < 0) { + dev_err(dev, + "regulator %d set voltage failed, %d\n", + i, ret); + return ret; + } + } } - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(0), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(1), 0x5); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(2), 0xa); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(3), 0xf); - dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_CFG_1, 0xc0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_TEST_STR0, 0x1); - dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_TEST_STR1, 0xbb); + return 0; +} - dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_0, 0x5f); +static void dsi_phy_regulator_disable(struct msm_dsi_phy *phy) +{ + struct regulator_bulk_data *s = phy->supplies; + const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs; + int num = phy->cfg->reg_cfg.num; + int i; - if (is_dual_panel && (phy->id != DSI_CLOCK_MASTER)) - dsi_phy_write(base + REG_DSI_28nm_PHY_GLBL_TEST_CTRL, 0x00); - else - dsi_phy_write(base + REG_DSI_28nm_PHY_GLBL_TEST_CTRL, 0x01); + DBG(""); + for (i = num - 1; i >= 0; i--) + if (regs[i].disable_load >= 0) + regulator_set_load(s[i].consumer, regs[i].disable_load); - return 0; + regulator_bulk_disable(num, s); } -static int dsi_28nm_phy_disable(struct msm_dsi_phy *phy) +static int dsi_phy_regulator_enable(struct msm_dsi_phy *phy) { - dsi_phy_write(phy->base + REG_DSI_28nm_PHY_CTRL_0, 0); - dsi_28nm_phy_regulator_ctrl(phy, false); + struct regulator_bulk_data *s = phy->supplies; + const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs; + struct device *dev = &phy->pdev->dev; + int num = phy->cfg->reg_cfg.num; + int ret, i; - /* - * Wait for the registers writes to complete in order to - * ensure that the phy is completely disabled - */ - wmb(); + DBG(""); + for (i = 0; i < num; i++) { + if (regs[i].enable_load >= 0) { + ret = regulator_set_load(s[i].consumer, + regs[i].enable_load); + if (ret < 0) { + dev_err(dev, + "regulator %d set op mode failed, %d\n", + i, ret); + goto fail; + } + } + } + + ret = regulator_bulk_enable(num, s); + if (ret < 0) { + dev_err(dev, "regulator enable failed, %d\n", ret); + goto fail; + } return 0; + +fail: + for (i--; i >= 0; i--) + regulator_set_load(s[i].consumer, regs[i].disable_load); + return ret; } static int dsi_phy_enable_resource(struct msm_dsi_phy *phy) { + struct device *dev = &phy->pdev->dev; int ret; - pm_runtime_get_sync(&phy->pdev->dev); + pm_runtime_get_sync(dev); ret = clk_prepare_enable(phy->ahb_clk); if (ret) { - pr_err("%s: can't enable ahb clk, %d\n", __func__, ret); - pm_runtime_put_sync(&phy->pdev->dev); + dev_err(dev, "%s: can't enable ahb clk, %d\n", __func__, ret); + pm_runtime_put_sync(dev); } return ret; @@ -411,92 +266,74 @@ static void dsi_phy_disable_resource(struct msm_dsi_phy *phy) pm_runtime_put_sync(&phy->pdev->dev); } -static const struct dsi_phy_cfg dsi_phy_cfgs[MSM_DSI_PHY_MAX] = { - [MSM_DSI_PHY_28NM_HPM] = { - .type = MSM_DSI_PHY_28NM_HPM, - .reg_cfg = { - .num = 1, - .regs = { - {"vddio", 1800000, 1800000, 100000, 100}, - }, - }, - .ops = { - .enable = dsi_28nm_phy_enable, - .disable = dsi_28nm_phy_disable, - } - }, - [MSM_DSI_PHY_28NM_LP] = { - .type = MSM_DSI_PHY_28NM_LP, - .reg_cfg = { - .num = 1, - .regs = { - {"vddio", 1800000, 1800000, 100000, 100}, - }, - }, - .ops = { - .enable = dsi_28nm_phy_enable, - .disable = dsi_28nm_phy_disable, - } - }, -}; - static const struct of_device_id dsi_phy_dt_match[] = { +#ifdef CONFIG_DRM_MSM_DSI_28NM_PHY { .compatible = "qcom,dsi-phy-28nm-hpm", - .data = &dsi_phy_cfgs[MSM_DSI_PHY_28NM_HPM],}, + .data = &dsi_phy_28nm_hpm_cfgs }, { .compatible = "qcom,dsi-phy-28nm-lp", - .data = &dsi_phy_cfgs[MSM_DSI_PHY_28NM_LP],}, + .data = &dsi_phy_28nm_lp_cfgs }, +#endif +#ifdef CONFIG_DRM_MSM_DSI_20NM_PHY + { .compatible = "qcom,dsi-phy-20nm", + .data = &dsi_phy_20nm_cfgs }, +#endif {} }; static int dsi_phy_driver_probe(struct platform_device *pdev) { struct msm_dsi_phy *phy; + struct device *dev = &pdev->dev; const struct of_device_id *match; int ret; - phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); if (!phy) return -ENOMEM; - match = of_match_node(dsi_phy_dt_match, pdev->dev.of_node); + match = of_match_node(dsi_phy_dt_match, dev->of_node); if (!match) return -ENODEV; phy->cfg = match->data; phy->pdev = pdev; - ret = of_property_read_u32(pdev->dev.of_node, + ret = of_property_read_u32(dev->of_node, "qcom,dsi-phy-index", &phy->id); if (ret) { - dev_err(&pdev->dev, - "%s: PHY index not specified, ret=%d\n", + dev_err(dev, "%s: PHY index not specified, %d\n", __func__, ret); goto fail; } + phy->regulator_ldo_mode = of_property_read_bool(dev->of_node, + "qcom,dsi-phy-regulator-ldo-mode"); + phy->base = msm_ioremap(pdev, "dsi_phy", "DSI_PHY"); if (IS_ERR(phy->base)) { - dev_err(&pdev->dev, "%s: failed to map phy base\n", __func__); + dev_err(dev, "%s: failed to map phy base\n", __func__); ret = -ENOMEM; goto fail; } - phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", "DSI_PHY_REG"); + + phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", + "DSI_PHY_REG"); if (IS_ERR(phy->reg_base)) { - dev_err(&pdev->dev, - "%s: failed to map phy regulator base\n", __func__); + dev_err(dev, "%s: failed to map phy regulator base\n", + __func__); ret = -ENOMEM; goto fail; } ret = dsi_phy_regulator_init(phy); if (ret) { - dev_err(&pdev->dev, "%s: failed to init regulator\n", __func__); + dev_err(dev, "%s: failed to init regulator\n", __func__); goto fail; } - phy->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk"); + phy->ahb_clk = devm_clk_get(dev, "iface_clk"); if (IS_ERR(phy->ahb_clk)) { - pr_err("%s: Unable to get ahb clk\n", __func__); + dev_err(dev, "%s: Unable to get ahb clk\n", __func__); ret = PTR_ERR(phy->ahb_clk); goto fail; } @@ -510,7 +347,7 @@ static int dsi_phy_driver_probe(struct platform_device *pdev) phy->pll = msm_dsi_pll_init(pdev, phy->cfg->type, phy->id); if (!phy->pll) - dev_info(&pdev->dev, + dev_info(dev, "%s: pll init failed, need separate pll clk driver\n", __func__); @@ -557,9 +394,10 @@ void __exit msm_dsi_phy_driver_unregister(void) platform_driver_unregister(&dsi_phy_platform_driver); } -int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel, +int msm_dsi_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, const unsigned long bit_rate, const unsigned long esc_rate) { + struct device *dev = &phy->pdev->dev; int ret; if (!phy || !phy->cfg->ops.enable) @@ -567,30 +405,37 @@ int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel, ret = dsi_phy_regulator_enable(phy); if (ret) { - dev_err(&phy->pdev->dev, "%s: regulator enable failed, %d\n", + dev_err(dev, "%s: regulator enable failed, %d\n", __func__, ret); return ret; } - return phy->cfg->ops.enable(phy, is_dual_panel, bit_rate, esc_rate); + ret = phy->cfg->ops.enable(phy, src_pll_id, bit_rate, esc_rate); + if (ret) { + dev_err(dev, "%s: phy enable failed, %d\n", __func__, ret); + dsi_phy_regulator_disable(phy); + return ret; + } + + return 0; } -int msm_dsi_phy_disable(struct msm_dsi_phy *phy) +void msm_dsi_phy_disable(struct msm_dsi_phy *phy) { if (!phy || !phy->cfg->ops.disable) - return -EINVAL; + return; phy->cfg->ops.disable(phy); - dsi_phy_regulator_disable(phy); - return 0; + dsi_phy_regulator_disable(phy); } void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy, - u32 *clk_pre, u32 *clk_post) + u32 *clk_pre, u32 *clk_post) { if (!phy) return; + if (clk_pre) *clk_pre = phy->timing.clk_pre; if (clk_post) diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h new file mode 100644 index 000000000000..0456b253239f --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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. + */ + +#ifndef __DSI_PHY_H__ +#define __DSI_PHY_H__ + +#include <linux/regulator/consumer.h> + +#include "dsi.h" + +#define dsi_phy_read(offset) msm_readl((offset)) +#define dsi_phy_write(offset, data) msm_writel((data), (offset)) + +struct msm_dsi_phy_ops { + int (*enable)(struct msm_dsi_phy *phy, int src_pll_id, + const unsigned long bit_rate, const unsigned long esc_rate); + void (*disable)(struct msm_dsi_phy *phy); +}; + +struct msm_dsi_phy_cfg { + enum msm_dsi_phy_type type; + struct dsi_reg_config reg_cfg; + struct msm_dsi_phy_ops ops; + + /* + * Each cell {phy_id, pll_id} of the truth table indicates + * if the source PLL selection bit should be set for each PHY. + * Fill default H/W values in illegal cells, eg. cell {0, 1}. + */ + bool src_pll_truthtable[DSI_MAX][DSI_MAX]; +}; + +extern const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs; +extern const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs; +extern const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs; + +struct msm_dsi_dphy_timing { + u32 clk_pre; + u32 clk_post; + u32 clk_zero; + u32 clk_trail; + u32 clk_prepare; + u32 hs_exit; + u32 hs_zero; + u32 hs_prepare; + u32 hs_trail; + u32 hs_rqst; + u32 ta_go; + u32 ta_sure; + u32 ta_get; +}; + +struct msm_dsi_phy { + struct platform_device *pdev; + void __iomem *base; + void __iomem *reg_base; + int id; + + struct clk *ahb_clk; + struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX]; + + struct msm_dsi_dphy_timing timing; + const struct msm_dsi_phy_cfg *cfg; + + bool regulator_ldo_mode; + + struct msm_dsi_pll *pll; +}; + +/* + * PHY internal functions + */ +int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, + const unsigned long bit_rate, const unsigned long esc_rate); +void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg, + u32 bit_mask); + +#endif /* __DSI_PHY_H__ */ + diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c new file mode 100644 index 000000000000..2e9ba118d50a --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "dsi_phy.h" +#include "dsi.xml.h" + +static void dsi_20nm_dphy_set_timing(struct msm_dsi_phy *phy, + struct msm_dsi_dphy_timing *timing) +{ + void __iomem *base = phy->base; + + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_0, + DSI_20nm_PHY_TIMING_CTRL_0_CLK_ZERO(timing->clk_zero)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_1, + DSI_20nm_PHY_TIMING_CTRL_1_CLK_TRAIL(timing->clk_trail)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_2, + DSI_20nm_PHY_TIMING_CTRL_2_CLK_PREPARE(timing->clk_prepare)); + if (timing->clk_zero & BIT(8)) + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_3, + DSI_20nm_PHY_TIMING_CTRL_3_CLK_ZERO_8); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_4, + DSI_20nm_PHY_TIMING_CTRL_4_HS_EXIT(timing->hs_exit)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_5, + DSI_20nm_PHY_TIMING_CTRL_5_HS_ZERO(timing->hs_zero)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_6, + DSI_20nm_PHY_TIMING_CTRL_6_HS_PREPARE(timing->hs_prepare)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_7, + DSI_20nm_PHY_TIMING_CTRL_7_HS_TRAIL(timing->hs_trail)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_8, + DSI_20nm_PHY_TIMING_CTRL_8_HS_RQST(timing->hs_rqst)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_9, + DSI_20nm_PHY_TIMING_CTRL_9_TA_GO(timing->ta_go) | + DSI_20nm_PHY_TIMING_CTRL_9_TA_SURE(timing->ta_sure)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_10, + DSI_20nm_PHY_TIMING_CTRL_10_TA_GET(timing->ta_get)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_11, + DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD(0)); +} + +static void dsi_20nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable) +{ + void __iomem *base = phy->reg_base; + + if (!enable) { + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CAL_PWR_CFG, 0); + return; + } + + if (phy->regulator_ldo_mode) { + dsi_phy_write(phy->base + REG_DSI_20nm_PHY_LDO_CNTRL, 0x1d); + return; + } + + /* non LDO mode */ + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CTRL_1, 0x03); + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CTRL_2, 0x03); + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CTRL_3, 0x00); + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CTRL_4, 0x20); + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CAL_PWR_CFG, 0x01); + dsi_phy_write(phy->base + REG_DSI_20nm_PHY_LDO_CNTRL, 0x00); + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CTRL_0, 0x03); +} + +static int dsi_20nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, + const unsigned long bit_rate, const unsigned long esc_rate) +{ + struct msm_dsi_dphy_timing *timing = &phy->timing; + int i; + void __iomem *base = phy->base; + u32 cfg_4[4] = {0x20, 0x40, 0x20, 0x00}; + + DBG(""); + + if (msm_dsi_dphy_timing_calc(timing, bit_rate, esc_rate)) { + dev_err(&phy->pdev->dev, + "%s: D-PHY timing calculation failed\n", __func__); + return -EINVAL; + } + + dsi_20nm_phy_regulator_ctrl(phy, true); + + dsi_phy_write(base + REG_DSI_20nm_PHY_STRENGTH_0, 0xff); + + msm_dsi_phy_set_src_pll(phy, src_pll_id, + REG_DSI_20nm_PHY_GLBL_TEST_CTRL, + DSI_20nm_PHY_GLBL_TEST_CTRL_BITCLK_HS_SEL); + + for (i = 0; i < 4; i++) { + dsi_phy_write(base + REG_DSI_20nm_PHY_LN_CFG_3(i), + (i >> 1) * 0x40); + dsi_phy_write(base + REG_DSI_20nm_PHY_LN_TEST_STR_0(i), 0x01); + dsi_phy_write(base + REG_DSI_20nm_PHY_LN_TEST_STR_1(i), 0x46); + dsi_phy_write(base + REG_DSI_20nm_PHY_LN_CFG_0(i), 0x02); + dsi_phy_write(base + REG_DSI_20nm_PHY_LN_CFG_1(i), 0xa0); + dsi_phy_write(base + REG_DSI_20nm_PHY_LN_CFG_4(i), cfg_4[i]); + } + + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_CFG_3, 0x80); + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_TEST_STR0, 0x01); + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_TEST_STR1, 0x46); + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_CFG_0, 0x00); + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_CFG_1, 0xa0); + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_CFG_2, 0x00); + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_CFG_4, 0x00); + + dsi_20nm_dphy_set_timing(phy, timing); + + dsi_phy_write(base + REG_DSI_20nm_PHY_CTRL_1, 0x00); + + dsi_phy_write(base + REG_DSI_20nm_PHY_STRENGTH_1, 0x06); + + /* make sure everything is written before enable */ + wmb(); + dsi_phy_write(base + REG_DSI_20nm_PHY_CTRL_0, 0x7f); + + return 0; +} + +static void dsi_20nm_phy_disable(struct msm_dsi_phy *phy) +{ + dsi_phy_write(phy->base + REG_DSI_20nm_PHY_CTRL_0, 0); + dsi_20nm_phy_regulator_ctrl(phy, false); +} + +const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs = { + .type = MSM_DSI_PHY_20NM, + .src_pll_truthtable = { {false, true}, {false, true} }, + .reg_cfg = { + .num = 2, + .regs = { + {"vddio", 1800000, 1800000, 100000, 100}, + {"vcca", 1000000, 1000000, 10000, 100}, + }, + }, + .ops = { + .enable = dsi_20nm_phy_enable, + .disable = dsi_20nm_phy_disable, + } +}; + diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c new file mode 100644 index 000000000000..f1a7c7b46420 --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "dsi_phy.h" +#include "dsi.xml.h" + +static void dsi_28nm_dphy_set_timing(struct msm_dsi_phy *phy, + struct msm_dsi_dphy_timing *timing) +{ + void __iomem *base = phy->base; + + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_0, + DSI_28nm_PHY_TIMING_CTRL_0_CLK_ZERO(timing->clk_zero)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_1, + DSI_28nm_PHY_TIMING_CTRL_1_CLK_TRAIL(timing->clk_trail)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_2, + DSI_28nm_PHY_TIMING_CTRL_2_CLK_PREPARE(timing->clk_prepare)); + if (timing->clk_zero & BIT(8)) + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_3, + DSI_28nm_PHY_TIMING_CTRL_3_CLK_ZERO_8); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_4, + DSI_28nm_PHY_TIMING_CTRL_4_HS_EXIT(timing->hs_exit)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_5, + DSI_28nm_PHY_TIMING_CTRL_5_HS_ZERO(timing->hs_zero)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_6, + DSI_28nm_PHY_TIMING_CTRL_6_HS_PREPARE(timing->hs_prepare)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_7, + DSI_28nm_PHY_TIMING_CTRL_7_HS_TRAIL(timing->hs_trail)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_8, + DSI_28nm_PHY_TIMING_CTRL_8_HS_RQST(timing->hs_rqst)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_9, + DSI_28nm_PHY_TIMING_CTRL_9_TA_GO(timing->ta_go) | + DSI_28nm_PHY_TIMING_CTRL_9_TA_SURE(timing->ta_sure)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_10, + DSI_28nm_PHY_TIMING_CTRL_10_TA_GET(timing->ta_get)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_11, + DSI_28nm_PHY_TIMING_CTRL_11_TRIG3_CMD(0)); +} + +static void dsi_28nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable) +{ + void __iomem *base = phy->reg_base; + + if (!enable) { + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 0); + return; + } + + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0, 0x0); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 1); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_5, 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_3, 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_2, 0x3); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_1, 0x9); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0, 0x7); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_4, 0x20); +} + +static int dsi_28nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, + const unsigned long bit_rate, const unsigned long esc_rate) +{ + struct msm_dsi_dphy_timing *timing = &phy->timing; + int i; + void __iomem *base = phy->base; + + DBG(""); + + if (msm_dsi_dphy_timing_calc(timing, bit_rate, esc_rate)) { + dev_err(&phy->pdev->dev, + "%s: D-PHY timing calculation failed\n", __func__); + return -EINVAL; + } + + dsi_phy_write(base + REG_DSI_28nm_PHY_STRENGTH_0, 0xff); + + dsi_28nm_phy_regulator_ctrl(phy, true); + + dsi_phy_write(base + REG_DSI_28nm_PHY_LDO_CNTRL, 0x00); + + dsi_28nm_dphy_set_timing(phy, timing); + + dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_1, 0x00); + dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_0, 0x5f); + + dsi_phy_write(base + REG_DSI_28nm_PHY_STRENGTH_1, 0x6); + + for (i = 0; i < 4; i++) { + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_0(i), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_1(i), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_2(i), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_3(i), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_DATAPATH(i), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_DEBUG_SEL(i), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_STR_0(i), 0x1); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_STR_1(i), 0x97); + } + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(0), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(1), 0x5); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(2), 0xa); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(3), 0xf); + + dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_CFG_1, 0xc0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_TEST_STR0, 0x1); + dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_TEST_STR1, 0xbb); + + dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_0, 0x5f); + + msm_dsi_phy_set_src_pll(phy, src_pll_id, + REG_DSI_28nm_PHY_GLBL_TEST_CTRL, + DSI_28nm_PHY_GLBL_TEST_CTRL_BITCLK_HS_SEL); + + return 0; +} + +static void dsi_28nm_phy_disable(struct msm_dsi_phy *phy) +{ + dsi_phy_write(phy->base + REG_DSI_28nm_PHY_CTRL_0, 0); + dsi_28nm_phy_regulator_ctrl(phy, false); + + /* + * Wait for the registers writes to complete in order to + * ensure that the phy is completely disabled + */ + wmb(); +} + +const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs = { + .type = MSM_DSI_PHY_28NM_HPM, + .src_pll_truthtable = { {true, true}, {false, true} }, + .reg_cfg = { + .num = 1, + .regs = { + {"vddio", 1800000, 1800000, 100000, 100}, + }, + }, + .ops = { + .enable = dsi_28nm_phy_enable, + .disable = dsi_28nm_phy_disable, + }, +}; + +const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs = { + .type = MSM_DSI_PHY_28NM_LP, + .src_pll_truthtable = { {true, true}, {true, true} }, + .reg_cfg = { + .num = 1, + .regs = { + {"vddio", 1800000, 1800000, 100000, 100}, + }, + }, + .ops = { + .enable = dsi_28nm_phy_enable, + .disable = dsi_28nm_phy_disable, + }, +}; + diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c index 509376fdd112..5104fc9f9a53 100644 --- a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c @@ -72,31 +72,14 @@ long msm_dsi_pll_helper_clk_round_rate(struct clk_hw *hw, int msm_dsi_pll_helper_clk_prepare(struct clk_hw *hw) { struct msm_dsi_pll *pll = hw_clk_to_pll(hw); - int ret; - - /* - * Certain PLLs need to update the same VCO rate and registers - * after resume in suspend/resume scenario. - */ - if (pll->restore_state) { - ret = pll->restore_state(pll); - if (ret) - goto error; - } - ret = dsi_pll_enable(pll); - -error: - return ret; + return dsi_pll_enable(pll); } void msm_dsi_pll_helper_clk_unprepare(struct clk_hw *hw) { struct msm_dsi_pll *pll = hw_clk_to_pll(hw); - if (pll->save_state) - pll->save_state(pll); - dsi_pll_disable(pll); } @@ -134,6 +117,29 @@ void msm_dsi_pll_destroy(struct msm_dsi_pll *pll) pll->destroy(pll); } +void msm_dsi_pll_save_state(struct msm_dsi_pll *pll) +{ + if (pll->save_state) { + pll->save_state(pll); + pll->state_saved = true; + } +} + +int msm_dsi_pll_restore_state(struct msm_dsi_pll *pll) +{ + int ret; + + if (pll->restore_state && pll->state_saved) { + ret = pll->restore_state(pll); + if (ret) + return ret; + + pll->state_saved = false; + } + + return 0; +} + struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, enum msm_dsi_phy_type type, int id) { diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h index 5a3bb241c039..063caa2c5740 100644 --- a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h @@ -27,6 +27,7 @@ struct msm_dsi_pll { struct clk_hw clk_hw; bool pll_on; + bool state_saved; unsigned long min_rate; unsigned long max_rate; @@ -82,8 +83,16 @@ void msm_dsi_pll_helper_unregister_clks(struct platform_device *pdev, /* * Initialization for Each PLL Type */ +#ifdef CONFIG_DRM_MSM_DSI_28NM_PHY struct msm_dsi_pll *msm_dsi_pll_28nm_init(struct platform_device *pdev, enum msm_dsi_phy_type type, int id); +#else +static inline struct msm_dsi_pll *msm_dsi_pll_28nm_init( + struct platform_device *pdev, enum msm_dsi_phy_type type, int id) +{ + return ERR_PTR(-ENODEV); +} +#endif #endif /* __DSI_PLL_H__ */ diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c index 18b7727bdc57..598fdaff0a41 100644 --- a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c @@ -465,26 +465,21 @@ static int dsi_pll_28nm_restore_state(struct msm_dsi_pll *pll) void __iomem *base = pll_28nm->mmio; int ret; - if ((cached_state->vco_rate != 0) && - (cached_state->vco_rate == clk_hw_get_rate(&pll->clk_hw))) { - ret = dsi_pll_28nm_clk_set_rate(&pll->clk_hw, - cached_state->vco_rate, 0); - if (ret) { - dev_err(&pll_28nm->pdev->dev, - "restore vco rate failed. ret=%d\n", ret); - return ret; - } - - pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG, - cached_state->postdiv3); - pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG, - cached_state->postdiv1); - pll_write(base + REG_DSI_28nm_PHY_PLL_VREG_CFG, - cached_state->byte_mux); - - cached_state->vco_rate = 0; + ret = dsi_pll_28nm_clk_set_rate(&pll->clk_hw, + cached_state->vco_rate, 0); + if (ret) { + dev_err(&pll_28nm->pdev->dev, + "restore vco rate failed. ret=%d\n", ret); + return ret; } + pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG, + cached_state->postdiv3); + pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG, + cached_state->postdiv1); + pll_write(base + REG_DSI_28nm_PHY_PLL_VREG_CFG, + cached_state->byte_mux); + return 0; } diff --git a/drivers/gpu/drm/msm/dsi/sfpb.xml.h b/drivers/gpu/drm/msm/dsi/sfpb.xml.h index 26f268e2dd3d..06cbddfc914f 100644 --- a/drivers/gpu/drm/msm/dsi/sfpb.xml.h +++ b/drivers/gpu/drm/msm/dsi/sfpb.xml.h @@ -8,19 +8,19 @@ 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 ( 676 bytes, from 2014-12-05 15:34:49) -- /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 ( 20915 bytes, from 2015-03-24 22:05:22) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2352 bytes, from 2015-04-12 15:02:42) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 35083 bytes, from 2015-04-12 15:04:03) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 22094 bytes, from 2015-05-12 12:45:23) -- /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 ( 1686 bytes, from 2014-10-31 16:48:57) -- /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 ( 29012 bytes, from 2015-05-12 12:45:23) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-12 12:45:23) - -Copyright (C) 2013 by the following authors: +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2576 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 36021 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 26057 bytes, from 2015-08-14 21:47:57) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) + +Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) Permission is hereby granted, free of charge, to any person obtaining diff --git a/drivers/gpu/drm/msm/edp/edp.xml.h b/drivers/gpu/drm/msm/edp/edp.xml.h index f9c71dceb5e2..bef1d65fe28c 100644 --- a/drivers/gpu/drm/msm/edp/edp.xml.h +++ b/drivers/gpu/drm/msm/edp/edp.xml.h @@ -8,17 +8,17 @@ 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 ( 676 bytes, from 2014-12-05 15:34:49) -- /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 ( 20915 bytes, from 2015-03-24 22:05:22) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2352 bytes, from 2015-04-12 15:02:42) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 35083 bytes, from 2015-04-12 15:04:03) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 22094 bytes, from 2015-05-12 12:45:23) -- /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 ( 1686 bytes, from 2014-10-31 16:48:57) -- /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 ( 29012 bytes, from 2015-05-12 12:45:23) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-12 12:45:23) +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2576 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 36021 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 26057 bytes, from 2015-08-14 21:47:57) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 814536202efe..101b324cdeef 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -22,7 +22,9 @@ void hdmi_set_mode(struct hdmi *hdmi, bool power_on) { uint32_t ctrl = 0; + unsigned long flags; + spin_lock_irqsave(&hdmi->reg_lock, flags); if (power_on) { ctrl |= HDMI_CTRL_ENABLE; if (!hdmi->hdmi_mode) { @@ -37,6 +39,7 @@ void hdmi_set_mode(struct hdmi *hdmi, bool power_on) } hdmi_write(hdmi, REG_HDMI_CTRL, ctrl); + spin_unlock_irqrestore(&hdmi->reg_lock, flags); DBG("HDMI Core: %s, HDMI_CTRL=0x%08x", power_on ? "Enable" : "Disable", ctrl); } @@ -51,6 +54,10 @@ static irqreturn_t hdmi_irq(int irq, void *dev_id) /* Process DDC: */ hdmi_i2c_irq(hdmi->i2c); + /* Process HDCP: */ + if (hdmi->hdcp_ctrl) + hdmi_hdcp_irq(hdmi->hdcp_ctrl); + /* TODO audio.. */ return IRQ_HANDLED; @@ -60,6 +67,15 @@ static void hdmi_destroy(struct hdmi *hdmi) { struct hdmi_phy *phy = hdmi->phy; + /* + * at this point, hpd has been disabled, + * after flush workq, it's safe to deinit hdcp + */ + if (hdmi->workq) { + flush_workqueue(hdmi->workq); + destroy_workqueue(hdmi->workq); + } + hdmi_hdcp_destroy(hdmi); if (phy) phy->funcs->destroy(phy); @@ -77,6 +93,7 @@ static struct hdmi *hdmi_init(struct platform_device *pdev) { struct hdmi_platform_config *config = pdev->dev.platform_data; struct hdmi *hdmi = NULL; + struct resource *res; int i, ret; hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); @@ -87,18 +104,18 @@ static struct hdmi *hdmi_init(struct platform_device *pdev) hdmi->pdev = pdev; hdmi->config = config; + spin_lock_init(&hdmi->reg_lock); /* not sure about which phy maps to which msm.. probably I miss some */ - if (config->phy_init) + if (config->phy_init) { hdmi->phy = config->phy_init(hdmi); - else - hdmi->phy = ERR_PTR(-ENXIO); - if (IS_ERR(hdmi->phy)) { - ret = PTR_ERR(hdmi->phy); - dev_err(&pdev->dev, "failed to load phy: %d\n", ret); - hdmi->phy = NULL; - goto fail; + if (IS_ERR(hdmi->phy)) { + ret = PTR_ERR(hdmi->phy); + dev_err(&pdev->dev, "failed to load phy: %d\n", ret); + hdmi->phy = NULL; + goto fail; + } } hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI"); @@ -107,6 +124,18 @@ static struct hdmi *hdmi_init(struct platform_device *pdev) goto fail; } + /* HDCP needs physical address of hdmi register */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + config->mmio_name); + hdmi->mmio_phy_addr = res->start; + + hdmi->qfprom_mmio = msm_ioremap(pdev, + config->qfprom_mmio_name, "HDMI_QFPROM"); + if (IS_ERR(hdmi->qfprom_mmio)) { + dev_info(&pdev->dev, "can't find qfprom resource\n"); + hdmi->qfprom_mmio = NULL; + } + hdmi->hpd_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_regs[0]) * config->hpd_reg_cnt, GFP_KERNEL); if (!hdmi->hpd_regs) { @@ -189,6 +218,8 @@ static struct hdmi *hdmi_init(struct platform_device *pdev) hdmi->pwr_clks[i] = clk; } + hdmi->workq = alloc_ordered_workqueue("msm_hdmi", 0); + hdmi->i2c = hdmi_i2c_init(hdmi); if (IS_ERR(hdmi->i2c)) { ret = PTR_ERR(hdmi->i2c); @@ -197,6 +228,12 @@ static struct hdmi *hdmi_init(struct platform_device *pdev) goto fail; } + hdmi->hdcp_ctrl = hdmi_hdcp_init(hdmi); + if (IS_ERR(hdmi->hdcp_ctrl)) { + dev_warn(&pdev->dev, "failed to init hdcp: disabled\n"); + hdmi->hdcp_ctrl = NULL; + } + return hdmi; fail: @@ -310,7 +347,7 @@ static const char *pwr_clk_names_8x74[] = {"extp_clk", "alt_iface_clk"}; static const char *hpd_clk_names_8x74[] = {"iface_clk", "core_clk", "mdp_core_clk"}; static unsigned long hpd_clk_freq_8x74[] = {0, 19200000, 0}; -static struct hdmi_platform_config hdmi_tx_8074_config = { +static struct hdmi_platform_config hdmi_tx_8974_config = { .phy_init = hdmi_phy_8x74_init, HDMI_CFG(pwr_reg, 8x74), HDMI_CFG(hpd_reg, 8x74), @@ -330,9 +367,21 @@ static struct hdmi_platform_config hdmi_tx_8084_config = { .hpd_freq = hpd_clk_freq_8x74, }; +static const char *hpd_reg_names_8x94[] = {}; + +static struct hdmi_platform_config hdmi_tx_8994_config = { + .phy_init = NULL, /* nothing to do for this HDMI PHY 20nm */ + HDMI_CFG(pwr_reg, 8x74), + HDMI_CFG(hpd_reg, 8x94), + HDMI_CFG(pwr_clk, 8x74), + HDMI_CFG(hpd_clk, 8x74), + .hpd_freq = hpd_clk_freq_8x74, +}; + static const struct of_device_id dt_match[] = { + { .compatible = "qcom,hdmi-tx-8994", .data = &hdmi_tx_8994_config }, { .compatible = "qcom,hdmi-tx-8084", .data = &hdmi_tx_8084_config }, - { .compatible = "qcom,hdmi-tx-8074", .data = &hdmi_tx_8074_config }, + { .compatible = "qcom,hdmi-tx-8974", .data = &hdmi_tx_8974_config }, { .compatible = "qcom,hdmi-tx-8960", .data = &hdmi_tx_8960_config }, { .compatible = "qcom,hdmi-tx-8660", .data = &hdmi_tx_8660_config }, {} @@ -347,8 +396,7 @@ static int get_gpio(struct device *dev, struct device_node *of_node, const char snprintf(name2, sizeof(name2), "%s-gpio", name); gpio = of_get_named_gpio(of_node, name2, 0); if (gpio < 0) { - dev_err(dev, "failed to get gpio: %s (%d)\n", - name, gpio); + DBG("failed to get gpio: %s (%d)", name, gpio); gpio = -1; } } @@ -376,6 +424,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) } hdmi_cfg->mmio_name = "core_physical"; + hdmi_cfg->qfprom_mmio_name = "qfprom_physical"; hdmi_cfg->ddc_clk_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-clk"); hdmi_cfg->ddc_data_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-data"); hdmi_cfg->hpd_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-hpd"); @@ -391,7 +440,6 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) if (cpu_is_apq8064()) { static const char *hpd_reg_names[] = {"8921_hdmi_mvs"}; config.phy_init = hdmi_phy_8960_init; - config.mmio_name = "hdmi_msm_hdmi_addr"; config.hpd_reg_names = hpd_reg_names; config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names); config.hpd_clk_names = hpd_clk_names; @@ -404,7 +452,6 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) } else if (cpu_is_msm8960() || cpu_is_msm8960ab()) { static const char *hpd_reg_names[] = {"8921_hdmi_mvs"}; config.phy_init = hdmi_phy_8960_init; - config.mmio_name = "hdmi_msm_hdmi_addr"; config.hpd_reg_names = hpd_reg_names; config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names); config.hpd_clk_names = hpd_clk_names; @@ -419,7 +466,6 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) "8901_hdmi_mvs", "8901_mpp0" }; config.phy_init = hdmi_phy_8x60_init; - config.mmio_name = "hdmi_msm_hdmi_addr"; config.hpd_reg_names = hpd_reg_names; config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names); config.hpd_clk_names = hpd_clk_names; @@ -430,6 +476,9 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) config.mux_en_gpio = -1; config.mux_sel_gpio = -1; } + config.mmio_name = "hdmi_msm_hdmi_addr"; + config.qfprom_mmio_name = "hdmi_msm_qfprom_addr"; + hdmi_cfg = &config; #endif dev->platform_data = hdmi_cfg; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 68fdfb3622a5..d0e663192d01 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -37,6 +37,8 @@ struct hdmi_audio { int rate; }; +struct hdmi_hdcp_ctrl; + struct hdmi { struct drm_device *dev; struct platform_device *pdev; @@ -51,6 +53,8 @@ struct hdmi { unsigned long int pixclock; void __iomem *mmio; + void __iomem *qfprom_mmio; + phys_addr_t mmio_phy_addr; struct regulator **hpd_regs; struct regulator **pwr_regs; @@ -68,12 +72,25 @@ struct hdmi { bool hdmi_mode; /* are we in hdmi mode? */ int irq; + struct workqueue_struct *workq; + + struct hdmi_hdcp_ctrl *hdcp_ctrl; + + /* + * spinlock to protect registers shared by different execution + * REG_HDMI_CTRL + * REG_HDMI_DDC_ARBITRATION + * REG_HDMI_HDCP_INT_CTRL + * REG_HDMI_HPD_CTRL + */ + spinlock_t reg_lock; }; /* platform config data (ie. from DT, or pdata) */ struct hdmi_platform_config { struct hdmi_phy *(*phy_init)(struct hdmi *hdmi); const char *mmio_name; + const char *qfprom_mmio_name; /* regulators that need to be on for hpd: */ const char **hpd_reg_names; @@ -109,6 +126,11 @@ static inline u32 hdmi_read(struct hdmi *hdmi, u32 reg) return msm_readl(hdmi->mmio + reg); } +static inline u32 hdmi_qfprom_read(struct hdmi *hdmi, u32 reg) +{ + return msm_readl(hdmi->qfprom_mmio + reg); +} + /* * The phy appears to be different, for example between 8960 and 8x60, * so split the phy related functions out and load the correct one at @@ -117,7 +139,6 @@ static inline u32 hdmi_read(struct hdmi *hdmi, u32 reg) struct hdmi_phy_funcs { void (*destroy)(struct hdmi_phy *phy); - void (*reset)(struct hdmi_phy *phy); void (*powerup)(struct hdmi_phy *phy, unsigned long int pixclock); void (*powerdown)(struct hdmi_phy *phy); }; @@ -163,4 +184,13 @@ void hdmi_i2c_irq(struct i2c_adapter *i2c); void hdmi_i2c_destroy(struct i2c_adapter *i2c); struct i2c_adapter *hdmi_i2c_init(struct hdmi *hdmi); +/* + * hdcp + */ +struct hdmi_hdcp_ctrl *hdmi_hdcp_init(struct hdmi *hdmi); +void hdmi_hdcp_destroy(struct hdmi *hdmi); +void hdmi_hdcp_on(struct hdmi_hdcp_ctrl *hdcp_ctrl); +void hdmi_hdcp_off(struct hdmi_hdcp_ctrl *hdcp_ctrl); +void hdmi_hdcp_irq(struct hdmi_hdcp_ctrl *hdcp_ctrl); + #endif /* __HDMI_CONNECTOR_H__ */ diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h index e6f034808371..0b1b5586ff35 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h @@ -8,17 +8,17 @@ 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 ( 676 bytes, from 2014-12-05 15:34:49) -- /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 ( 20915 bytes, from 2015-03-24 22:05:22) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2352 bytes, from 2015-04-12 15:02:42) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 35083 bytes, from 2015-04-12 15:04:03) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 22094 bytes, from 2015-05-12 12:45:23) -- /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 ( 1686 bytes, from 2014-10-31 16:48:57) -- /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 ( 29012 bytes, from 2015-05-12 12:45:23) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-12 12:45:23) +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2576 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 36021 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 26057 bytes, from 2015-08-14 21:47:57) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) @@ -441,6 +441,12 @@ static inline uint32_t HDMI_DDC_REF_REFTIMER(uint32_t val) #define REG_HDMI_HDCP_SW_LOWER_AKSV 0x00000288 +#define REG_HDMI_CEC_CTRL 0x0000028c + +#define REG_HDMI_CEC_WR_DATA 0x00000290 + +#define REG_HDMI_CEC_CEC_RETRANSMIT 0x00000294 + #define REG_HDMI_CEC_STATUS 0x00000298 #define REG_HDMI_CEC_INT 0x0000029c diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c index 872485f60134..df232e20c13e 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c @@ -203,7 +203,6 @@ int hdmi_audio_update(struct hdmi *hdmi) audio_config |= HDMI_AUDIO_CFG_FIFO_WATERMARK(4); audio_config |= HDMI_AUDIO_CFG_ENGINE_ENABLE; } else { - hdmi_write(hdmi, REG_HDMI_GC, HDMI_GC_MUTE); acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_CONT; acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_SEND; vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_ENABLE; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index a7a1d8267cf0..92b69ae8caf9 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -100,8 +100,13 @@ static void hdmi_bridge_pre_enable(struct drm_bridge *bridge) hdmi_audio_update(hdmi); } - phy->funcs->powerup(phy, hdmi->pixclock); + if (phy) + phy->funcs->powerup(phy, hdmi->pixclock); + hdmi_set_mode(hdmi, true); + + if (hdmi->hdcp_ctrl) + hdmi_hdcp_on(hdmi->hdcp_ctrl); } static void hdmi_bridge_enable(struct drm_bridge *bridge) @@ -118,9 +123,14 @@ static void hdmi_bridge_post_disable(struct drm_bridge *bridge) struct hdmi *hdmi = hdmi_bridge->hdmi; struct hdmi_phy *phy = hdmi->phy; + if (hdmi->hdcp_ctrl) + hdmi_hdcp_off(hdmi->hdcp_ctrl); + DBG("power down"); hdmi_set_mode(hdmi, false); - phy->funcs->powerdown(phy); + + if (phy) + phy->funcs->powerdown(phy); if (hdmi->power_on) { power_off(bridge); @@ -142,8 +152,6 @@ static void hdmi_bridge_mode_set(struct drm_bridge *bridge, hdmi->pixclock = mode->clock * 1000; - hdmi->hdmi_mode = drm_match_cea_mode(mode) > 1; - hstart = mode->htotal - mode->hsync_start; hend = mode->htotal - mode->hsync_start + mode->hdisplay; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c index 54aa93ff5473..a3b05ae52dae 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c @@ -28,6 +28,55 @@ struct hdmi_connector { }; #define to_hdmi_connector(x) container_of(x, struct hdmi_connector, base) +static void hdmi_phy_reset(struct hdmi *hdmi) +{ + unsigned int val; + + val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL); + + if (val & HDMI_PHY_CTRL_SW_RESET_LOW) { + /* pull low */ + hdmi_write(hdmi, REG_HDMI_PHY_CTRL, + val & ~HDMI_PHY_CTRL_SW_RESET); + } else { + /* pull high */ + hdmi_write(hdmi, REG_HDMI_PHY_CTRL, + val | HDMI_PHY_CTRL_SW_RESET); + } + + if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) { + /* pull low */ + hdmi_write(hdmi, REG_HDMI_PHY_CTRL, + val & ~HDMI_PHY_CTRL_SW_RESET_PLL); + } else { + /* pull high */ + hdmi_write(hdmi, REG_HDMI_PHY_CTRL, + val | HDMI_PHY_CTRL_SW_RESET_PLL); + } + + msleep(100); + + if (val & HDMI_PHY_CTRL_SW_RESET_LOW) { + /* pull high */ + hdmi_write(hdmi, REG_HDMI_PHY_CTRL, + val | HDMI_PHY_CTRL_SW_RESET); + } else { + /* pull low */ + hdmi_write(hdmi, REG_HDMI_PHY_CTRL, + val & ~HDMI_PHY_CTRL_SW_RESET); + } + + if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) { + /* pull high */ + hdmi_write(hdmi, REG_HDMI_PHY_CTRL, + val | HDMI_PHY_CTRL_SW_RESET_PLL); + } else { + /* pull low */ + hdmi_write(hdmi, REG_HDMI_PHY_CTRL, + val & ~HDMI_PHY_CTRL_SW_RESET_PLL); + } +} + static int gpio_config(struct hdmi *hdmi, bool on) { struct device *dev = &hdmi->pdev->dev; @@ -35,21 +84,25 @@ static int gpio_config(struct hdmi *hdmi, bool on) int ret; if (on) { - ret = gpio_request(config->ddc_clk_gpio, "HDMI_DDC_CLK"); - if (ret) { - dev_err(dev, "'%s'(%d) gpio_request failed: %d\n", - "HDMI_DDC_CLK", config->ddc_clk_gpio, ret); - goto error1; + if (config->ddc_clk_gpio != -1) { + ret = gpio_request(config->ddc_clk_gpio, "HDMI_DDC_CLK"); + if (ret) { + dev_err(dev, "'%s'(%d) gpio_request failed: %d\n", + "HDMI_DDC_CLK", config->ddc_clk_gpio, ret); + goto error1; + } + gpio_set_value_cansleep(config->ddc_clk_gpio, 1); } - gpio_set_value_cansleep(config->ddc_clk_gpio, 1); - ret = gpio_request(config->ddc_data_gpio, "HDMI_DDC_DATA"); - if (ret) { - dev_err(dev, "'%s'(%d) gpio_request failed: %d\n", - "HDMI_DDC_DATA", config->ddc_data_gpio, ret); - goto error2; + if (config->ddc_data_gpio != -1) { + ret = gpio_request(config->ddc_data_gpio, "HDMI_DDC_DATA"); + if (ret) { + dev_err(dev, "'%s'(%d) gpio_request failed: %d\n", + "HDMI_DDC_DATA", config->ddc_data_gpio, ret); + goto error2; + } + gpio_set_value_cansleep(config->ddc_data_gpio, 1); } - gpio_set_value_cansleep(config->ddc_data_gpio, 1); ret = gpio_request(config->hpd_gpio, "HDMI_HPD"); if (ret) { @@ -94,8 +147,12 @@ static int gpio_config(struct hdmi *hdmi, bool on) } DBG("gpio on"); } else { - gpio_free(config->ddc_clk_gpio); - gpio_free(config->ddc_data_gpio); + if (config->ddc_clk_gpio != -1) + gpio_free(config->ddc_clk_gpio); + + if (config->ddc_data_gpio != -1) + gpio_free(config->ddc_data_gpio); + gpio_free(config->hpd_gpio); if (config->mux_en_gpio != -1) { @@ -126,9 +183,11 @@ error5: error4: gpio_free(config->hpd_gpio); error3: - gpio_free(config->ddc_data_gpio); + if (config->ddc_data_gpio != -1) + gpio_free(config->ddc_data_gpio); error2: - gpio_free(config->ddc_clk_gpio); + if (config->ddc_clk_gpio != -1) + gpio_free(config->ddc_clk_gpio); error1: return ret; } @@ -138,9 +197,9 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector) struct hdmi *hdmi = hdmi_connector->hdmi; const struct hdmi_platform_config *config = hdmi->config; struct device *dev = &hdmi->pdev->dev; - struct hdmi_phy *phy = hdmi->phy; uint32_t hpd_ctrl; int i, ret; + unsigned long flags; for (i = 0; i < config->hpd_reg_cnt; i++) { ret = regulator_enable(hdmi->hpd_regs[i]); @@ -181,7 +240,7 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector) } hdmi_set_mode(hdmi, false); - phy->funcs->reset(phy); + hdmi_phy_reset(hdmi); hdmi_set_mode(hdmi, true); hdmi_write(hdmi, REG_HDMI_USEC_REFTIMER, 0x0001001b); @@ -192,6 +251,7 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector) HDMI_HPD_INT_CTRL_INT_EN); /* set timeout to 4.1ms (max) for hardware debounce */ + spin_lock_irqsave(&hdmi->reg_lock, flags); hpd_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_CTRL); hpd_ctrl |= HDMI_HPD_CTRL_TIMEOUT(0x1fff); @@ -200,6 +260,7 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector) ~HDMI_HPD_CTRL_ENABLE & hpd_ctrl); hdmi_write(hdmi, REG_HDMI_HPD_CTRL, HDMI_HPD_CTRL_ENABLE | hpd_ctrl); + spin_unlock_irqrestore(&hdmi->reg_lock, flags); return 0; @@ -250,7 +311,6 @@ hotplug_work(struct work_struct *work) void hdmi_connector_irq(struct drm_connector *connector) { struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector); - struct msm_drm_private *priv = connector->dev->dev_private; struct hdmi *hdmi = hdmi_connector->hdmi; uint32_t hpd_int_status, hpd_int_ctrl; @@ -274,7 +334,7 @@ void hdmi_connector_irq(struct drm_connector *connector) hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT; hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl); - queue_work(priv->wq, &hdmi_connector->hpd_work); + queue_work(hdmi->workq, &hdmi_connector->hpd_work); } } @@ -350,6 +410,7 @@ static int hdmi_connector_get_modes(struct drm_connector *connector) hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl); + hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid); drm_mode_connector_update_edid_property(connector, edid); if (edid) { diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c new file mode 100644 index 000000000000..1dc9c34eb0df --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c @@ -0,0 +1,1437 @@ +/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "hdmi.h" +#include <linux/qcom_scm.h> + +#define HDCP_REG_ENABLE 0x01 +#define HDCP_REG_DISABLE 0x00 +#define HDCP_PORT_ADDR 0x74 + +#define HDCP_INT_STATUS_MASK ( \ + HDMI_HDCP_INT_CTRL_AUTH_SUCCESS_INT | \ + HDMI_HDCP_INT_CTRL_AUTH_FAIL_INT | \ + HDMI_HDCP_INT_CTRL_AUTH_XFER_REQ_INT | \ + HDMI_HDCP_INT_CTRL_AUTH_XFER_DONE_INT) + +#define AUTH_WORK_RETRIES_TIME 100 +#define AUTH_RETRIES_TIME 30 + +/* QFPROM Registers for HDMI/HDCP */ +#define QFPROM_RAW_FEAT_CONFIG_ROW0_LSB 0x000000F8 +#define QFPROM_RAW_FEAT_CONFIG_ROW0_MSB 0x000000FC +#define HDCP_KSV_LSB 0x000060D8 +#define HDCP_KSV_MSB 0x000060DC + +enum DS_TYPE { /* type of downstream device */ + DS_UNKNOWN, + DS_RECEIVER, + DS_REPEATER, +}; + +enum hdmi_hdcp_state { + HDCP_STATE_NO_AKSV, + HDCP_STATE_INACTIVE, + HDCP_STATE_AUTHENTICATING, + HDCP_STATE_AUTHENTICATED, + HDCP_STATE_AUTH_FAILED +}; + +struct hdmi_hdcp_reg_data { + u32 reg_id; + u32 off; + char *name; + u32 reg_val; +}; + +struct hdmi_hdcp_ctrl { + struct hdmi *hdmi; + u32 auth_retries; + bool tz_hdcp; + enum hdmi_hdcp_state hdcp_state; + struct work_struct hdcp_auth_work; + struct work_struct hdcp_reauth_work; + +#define AUTH_ABORT_EV 1 +#define AUTH_RESULT_RDY_EV 2 + unsigned long auth_event; + wait_queue_head_t auth_event_queue; + + u32 ksv_fifo_w_index; + /* + * store aksv from qfprom + */ + u32 aksv_lsb; + u32 aksv_msb; + bool aksv_valid; + u32 ds_type; + u32 bksv_lsb; + u32 bksv_msb; + u8 dev_count; + u8 depth; + u8 ksv_list[5 * 127]; + bool max_cascade_exceeded; + bool max_dev_exceeded; +}; + +static int hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, + u8 *data, u16 data_len) +{ + int rc; + int retry = 5; + struct i2c_msg msgs[] = { + { + .addr = addr >> 1, + .flags = 0, + .len = 1, + .buf = &offset, + }, { + .addr = addr >> 1, + .flags = I2C_M_RD, + .len = data_len, + .buf = data, + } + }; + + DBG("Start DDC read"); +retry: + rc = i2c_transfer(hdmi->i2c, msgs, 2); + + retry--; + if (rc == 2) + rc = 0; + else if (retry > 0) + goto retry; + else + rc = -EIO; + + DBG("End DDC read %d", rc); + + return rc; +} + +#define HDCP_DDC_WRITE_MAX_BYTE_NUM 32 + +static int hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, + u8 *data, u16 data_len) +{ + int rc; + int retry = 10; + u8 buf[HDCP_DDC_WRITE_MAX_BYTE_NUM]; + struct i2c_msg msgs[] = { + { + .addr = addr >> 1, + .flags = 0, + .len = 1, + } + }; + + DBG("Start DDC write"); + if (data_len > (HDCP_DDC_WRITE_MAX_BYTE_NUM - 1)) { + pr_err("%s: write size too big\n", __func__); + return -ERANGE; + } + + buf[0] = offset; + memcpy(&buf[1], data, data_len); + msgs[0].buf = buf; + msgs[0].len = data_len + 1; +retry: + rc = i2c_transfer(hdmi->i2c, msgs, 1); + + retry--; + if (rc == 1) + rc = 0; + else if (retry > 0) + goto retry; + else + rc = -EIO; + + DBG("End DDC write %d", rc); + + return rc; +} + +static int hdmi_hdcp_scm_wr(struct hdmi_hdcp_ctrl *hdcp_ctrl, u32 *preg, + u32 *pdata, u32 count) +{ + struct hdmi *hdmi = hdcp_ctrl->hdmi; + struct qcom_scm_hdcp_req scm_buf[QCOM_SCM_HDCP_MAX_REQ_CNT]; + u32 resp, phy_addr, idx = 0; + int i, ret = 0; + + WARN_ON(!pdata || !preg || (count == 0)); + + if (hdcp_ctrl->tz_hdcp) { + phy_addr = (u32)hdmi->mmio_phy_addr; + + while (count) { + memset(scm_buf, 0, sizeof(scm_buf)); + for (i = 0; i < count && i < QCOM_SCM_HDCP_MAX_REQ_CNT; + i++) { + scm_buf[i].addr = phy_addr + preg[idx]; + scm_buf[i].val = pdata[idx]; + idx++; + } + ret = qcom_scm_hdcp_req(scm_buf, i, &resp); + + if (ret || resp) { + pr_err("%s: error: scm_call ret=%d resp=%u\n", + __func__, ret, resp); + ret = -EINVAL; + break; + } + + count -= i; + } + } else { + for (i = 0; i < count; i++) + hdmi_write(hdmi, preg[i], pdata[i]); + } + + return ret; +} + +void hdmi_hdcp_irq(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 reg_val, hdcp_int_status; + unsigned long flags; + + spin_lock_irqsave(&hdmi->reg_lock, flags); + reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_INT_CTRL); + hdcp_int_status = reg_val & HDCP_INT_STATUS_MASK; + if (!hdcp_int_status) { + spin_unlock_irqrestore(&hdmi->reg_lock, flags); + return; + } + /* Clear Interrupts */ + reg_val |= hdcp_int_status << 1; + /* Clear AUTH_FAIL_INFO as well */ + if (hdcp_int_status & HDMI_HDCP_INT_CTRL_AUTH_FAIL_INT) + reg_val |= HDMI_HDCP_INT_CTRL_AUTH_FAIL_INFO_ACK; + hdmi_write(hdmi, REG_HDMI_HDCP_INT_CTRL, reg_val); + spin_unlock_irqrestore(&hdmi->reg_lock, flags); + + DBG("hdcp irq %x", hdcp_int_status); + + if (hdcp_int_status & HDMI_HDCP_INT_CTRL_AUTH_SUCCESS_INT) { + pr_info("%s:AUTH_SUCCESS_INT received\n", __func__); + if (HDCP_STATE_AUTHENTICATING == hdcp_ctrl->hdcp_state) { + set_bit(AUTH_RESULT_RDY_EV, &hdcp_ctrl->auth_event); + wake_up_all(&hdcp_ctrl->auth_event_queue); + } + } + + if (hdcp_int_status & HDMI_HDCP_INT_CTRL_AUTH_FAIL_INT) { + reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_LINK0_STATUS); + pr_info("%s: AUTH_FAIL_INT rcvd, LINK0_STATUS=0x%08x\n", + __func__, reg_val); + if (HDCP_STATE_AUTHENTICATED == hdcp_ctrl->hdcp_state) + queue_work(hdmi->workq, &hdcp_ctrl->hdcp_reauth_work); + else if (HDCP_STATE_AUTHENTICATING == + hdcp_ctrl->hdcp_state) { + set_bit(AUTH_RESULT_RDY_EV, &hdcp_ctrl->auth_event); + wake_up_all(&hdcp_ctrl->auth_event_queue); + } + } +} + +static int hdmi_hdcp_msleep(struct hdmi_hdcp_ctrl *hdcp_ctrl, u32 ms, u32 ev) +{ + int rc; + + rc = wait_event_timeout(hdcp_ctrl->auth_event_queue, + !!test_bit(ev, &hdcp_ctrl->auth_event), + msecs_to_jiffies(ms)); + if (rc) { + pr_info("%s: msleep is canceled by event %d\n", + __func__, ev); + clear_bit(ev, &hdcp_ctrl->auth_event); + return -ECANCELED; + } + + return 0; +} + +static int hdmi_hdcp_read_validate_aksv(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + struct hdmi *hdmi = hdcp_ctrl->hdmi; + + /* Fetch aksv from QFPROM, this info should be public. */ + hdcp_ctrl->aksv_lsb = hdmi_qfprom_read(hdmi, HDCP_KSV_LSB); + hdcp_ctrl->aksv_msb = hdmi_qfprom_read(hdmi, HDCP_KSV_MSB); + + /* check there are 20 ones in AKSV */ + if ((hweight32(hdcp_ctrl->aksv_lsb) + hweight32(hdcp_ctrl->aksv_msb)) + != 20) { + pr_err("%s: AKSV QFPROM doesn't have 20 1's, 20 0's\n", + __func__); + pr_err("%s: QFPROM AKSV chk failed (AKSV=%02x%08x)\n", + __func__, hdcp_ctrl->aksv_msb, + hdcp_ctrl->aksv_lsb); + return -EINVAL; + } + DBG("AKSV=%02x%08x", hdcp_ctrl->aksv_msb, hdcp_ctrl->aksv_lsb); + + return 0; +} + +static int reset_hdcp_ddc_failures(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 reg_val, failure, nack0; + int rc = 0; + + /* Check for any DDC transfer failures */ + reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_DDC_STATUS); + failure = reg_val & HDMI_HDCP_DDC_STATUS_FAILED; + nack0 = reg_val & HDMI_HDCP_DDC_STATUS_NACK0; + DBG("HDCP_DDC_STATUS=0x%x, FAIL=%d, NACK0=%d", + reg_val, failure, nack0); + + if (failure) { + /* + * Indicates that the last HDCP HW DDC transfer failed. + * This occurs when a transfer is attempted with HDCP DDC + * disabled (HDCP_DDC_DISABLE=1) or the number of retries + * matches HDCP_DDC_RETRY_CNT. + * Failure occurred, let's clear it. + */ + DBG("DDC failure detected"); + + /* First, Disable DDC */ + hdmi_write(hdmi, REG_HDMI_HDCP_DDC_CTRL_0, + HDMI_HDCP_DDC_CTRL_0_DISABLE); + + /* ACK the Failure to Clear it */ + reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_DDC_CTRL_1); + reg_val |= HDMI_HDCP_DDC_CTRL_1_FAILED_ACK; + hdmi_write(hdmi, REG_HDMI_HDCP_DDC_CTRL_1, reg_val); + + /* Check if the FAILURE got Cleared */ + reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_DDC_STATUS); + if (reg_val & HDMI_HDCP_DDC_STATUS_FAILED) + pr_info("%s: Unable to clear HDCP DDC Failure\n", + __func__); + + /* Re-Enable HDCP DDC */ + hdmi_write(hdmi, REG_HDMI_HDCP_DDC_CTRL_0, 0); + } + + if (nack0) { + DBG("Before: HDMI_DDC_SW_STATUS=0x%08x", + hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS)); + /* Reset HDMI DDC software status */ + reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); + reg_val |= HDMI_DDC_CTRL_SW_STATUS_RESET; + hdmi_write(hdmi, REG_HDMI_DDC_CTRL, reg_val); + + rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + + reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); + reg_val &= ~HDMI_DDC_CTRL_SW_STATUS_RESET; + hdmi_write(hdmi, REG_HDMI_DDC_CTRL, reg_val); + + /* Reset HDMI DDC Controller */ + reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); + reg_val |= HDMI_DDC_CTRL_SOFT_RESET; + hdmi_write(hdmi, REG_HDMI_DDC_CTRL, reg_val); + + /* If previous msleep is aborted, skip this msleep */ + if (!rc) + rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + + reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); + reg_val &= ~HDMI_DDC_CTRL_SOFT_RESET; + hdmi_write(hdmi, REG_HDMI_DDC_CTRL, reg_val); + DBG("After: HDMI_DDC_SW_STATUS=0x%08x", + hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS)); + } + + return rc; +} + +static int hdmi_hdcp_hw_ddc_clean(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + int rc; + u32 hdcp_ddc_status, ddc_hw_status; + u32 xfer_done, xfer_req, hw_done; + bool hw_not_ready; + u32 timeout_count; + struct hdmi *hdmi = hdcp_ctrl->hdmi; + + if (hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS) == 0) + return 0; + + /* Wait to be clean on DDC HW engine */ + timeout_count = 100; + do { + hdcp_ddc_status = hdmi_read(hdmi, REG_HDMI_HDCP_DDC_STATUS); + ddc_hw_status = hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS); + + xfer_done = hdcp_ddc_status & HDMI_HDCP_DDC_STATUS_XFER_DONE; + xfer_req = hdcp_ddc_status & HDMI_HDCP_DDC_STATUS_XFER_REQ; + hw_done = ddc_hw_status & HDMI_DDC_HW_STATUS_DONE; + hw_not_ready = !xfer_done || xfer_req || !hw_done; + + if (hw_not_ready) + break; + + timeout_count--; + if (!timeout_count) { + pr_warn("%s: hw_ddc_clean failed\n", __func__); + return -ETIMEDOUT; + } + + rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + if (rc) + return rc; + } while (1); + + return 0; +} + +static void hdmi_hdcp_reauth_work(struct work_struct *work) +{ + struct hdmi_hdcp_ctrl *hdcp_ctrl = container_of(work, + struct hdmi_hdcp_ctrl, hdcp_reauth_work); + struct hdmi *hdmi = hdcp_ctrl->hdmi; + unsigned long flags; + u32 reg_val; + + DBG("HDCP REAUTH WORK"); + /* + * Disable HPD circuitry. + * This is needed to reset the HDCP cipher engine so that when we + * attempt a re-authentication, HW would clear the AN0_READY and + * AN1_READY bits in HDMI_HDCP_LINK0_STATUS register + */ + spin_lock_irqsave(&hdmi->reg_lock, flags); + reg_val = hdmi_read(hdmi, REG_HDMI_HPD_CTRL); + reg_val &= ~HDMI_HPD_CTRL_ENABLE; + hdmi_write(hdmi, REG_HDMI_HPD_CTRL, reg_val); + + /* Disable HDCP interrupts */ + hdmi_write(hdmi, REG_HDMI_HDCP_INT_CTRL, 0); + spin_unlock_irqrestore(&hdmi->reg_lock, flags); + + hdmi_write(hdmi, REG_HDMI_HDCP_RESET, + HDMI_HDCP_RESET_LINK0_DEAUTHENTICATE); + + /* Wait to be clean on DDC HW engine */ + if (hdmi_hdcp_hw_ddc_clean(hdcp_ctrl)) { + pr_info("%s: reauth work aborted\n", __func__); + return; + } + + /* Disable encryption and disable the HDCP block */ + hdmi_write(hdmi, REG_HDMI_HDCP_CTRL, 0); + + /* Enable HPD circuitry */ + spin_lock_irqsave(&hdmi->reg_lock, flags); + reg_val = hdmi_read(hdmi, REG_HDMI_HPD_CTRL); + reg_val |= HDMI_HPD_CTRL_ENABLE; + hdmi_write(hdmi, REG_HDMI_HPD_CTRL, reg_val); + spin_unlock_irqrestore(&hdmi->reg_lock, flags); + + /* + * Only retry defined times then abort current authenticating process + */ + if (++hdcp_ctrl->auth_retries == AUTH_RETRIES_TIME) { + hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE; + hdcp_ctrl->auth_retries = 0; + pr_info("%s: abort reauthentication!\n", __func__); + + return; + } + + DBG("Queue AUTH WORK"); + hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATING; + queue_work(hdmi->workq, &hdcp_ctrl->hdcp_auth_work); +} + +static int hdmi_hdcp_auth_prepare(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 link0_status; + u32 reg_val; + unsigned long flags; + int rc; + + if (!hdcp_ctrl->aksv_valid) { + rc = hdmi_hdcp_read_validate_aksv(hdcp_ctrl); + if (rc) { + pr_err("%s: ASKV validation failed\n", __func__); + hdcp_ctrl->hdcp_state = HDCP_STATE_NO_AKSV; + return -ENOTSUPP; + } + hdcp_ctrl->aksv_valid = true; + } + + spin_lock_irqsave(&hdmi->reg_lock, flags); + /* disable HDMI Encrypt */ + reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); + reg_val &= ~HDMI_CTRL_ENCRYPTED; + hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + + /* Enabling Software DDC */ + reg_val = hdmi_read(hdmi, REG_HDMI_DDC_ARBITRATION); + reg_val &= ~HDMI_DDC_ARBITRATION_HW_ARBITRATION; + hdmi_write(hdmi, REG_HDMI_DDC_ARBITRATION, reg_val); + spin_unlock_irqrestore(&hdmi->reg_lock, flags); + + /* + * Write AKSV read from QFPROM to the HDCP registers. + * This step is needed for HDCP authentication and must be + * written before enabling HDCP. + */ + hdmi_write(hdmi, REG_HDMI_HDCP_SW_LOWER_AKSV, hdcp_ctrl->aksv_lsb); + hdmi_write(hdmi, REG_HDMI_HDCP_SW_UPPER_AKSV, hdcp_ctrl->aksv_msb); + + /* + * HDCP setup prior to enabling HDCP_CTRL. + * Setup seed values for random number An. + */ + hdmi_write(hdmi, REG_HDMI_HDCP_ENTROPY_CTRL0, 0xB1FFB0FF); + hdmi_write(hdmi, REG_HDMI_HDCP_ENTROPY_CTRL1, 0xF00DFACE); + + /* Disable the RngCipher state */ + reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_DEBUG_CTRL); + reg_val &= ~HDMI_HDCP_DEBUG_CTRL_RNG_CIPHER; + hdmi_write(hdmi, REG_HDMI_HDCP_DEBUG_CTRL, reg_val); + DBG("HDCP_DEBUG_CTRL=0x%08x", + hdmi_read(hdmi, REG_HDMI_HDCP_DEBUG_CTRL)); + + /* + * Ensure that all register writes are completed before + * enabling HDCP cipher + */ + wmb(); + + /* + * Enable HDCP + * This needs to be done as early as possible in order for the + * hardware to make An available to read + */ + hdmi_write(hdmi, REG_HDMI_HDCP_CTRL, HDMI_HDCP_CTRL_ENABLE); + + /* + * If we had stale values for the An ready bit, it should most + * likely be cleared now after enabling HDCP cipher + */ + link0_status = hdmi_read(hdmi, REG_HDMI_HDCP_LINK0_STATUS); + DBG("After enabling HDCP Link0_Status=0x%08x", link0_status); + if (!(link0_status & + (HDMI_HDCP_LINK0_STATUS_AN_0_READY | + HDMI_HDCP_LINK0_STATUS_AN_1_READY))) + DBG("An not ready after enabling HDCP"); + + /* Clear any DDC failures from previous tries before enable HDCP*/ + rc = reset_hdcp_ddc_failures(hdcp_ctrl); + + return rc; +} + +static void hdmi_hdcp_auth_fail(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 reg_val; + unsigned long flags; + + DBG("hdcp auth failed, queue reauth work"); + /* clear HDMI Encrypt */ + spin_lock_irqsave(&hdmi->reg_lock, flags); + reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); + reg_val &= ~HDMI_CTRL_ENCRYPTED; + hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + spin_unlock_irqrestore(&hdmi->reg_lock, flags); + + hdcp_ctrl->hdcp_state = HDCP_STATE_AUTH_FAILED; + queue_work(hdmi->workq, &hdcp_ctrl->hdcp_reauth_work); +} + +static void hdmi_hdcp_auth_done(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 reg_val; + unsigned long flags; + + /* + * Disable software DDC before going into part3 to make sure + * there is no Arbitration between software and hardware for DDC + */ + spin_lock_irqsave(&hdmi->reg_lock, flags); + reg_val = hdmi_read(hdmi, REG_HDMI_DDC_ARBITRATION); + reg_val |= HDMI_DDC_ARBITRATION_HW_ARBITRATION; + hdmi_write(hdmi, REG_HDMI_DDC_ARBITRATION, reg_val); + spin_unlock_irqrestore(&hdmi->reg_lock, flags); + + /* enable HDMI Encrypt */ + spin_lock_irqsave(&hdmi->reg_lock, flags); + reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); + reg_val |= HDMI_CTRL_ENCRYPTED; + hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + spin_unlock_irqrestore(&hdmi->reg_lock, flags); + + hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATED; + hdcp_ctrl->auth_retries = 0; +} + +/* + * hdcp authenticating part 1 + * Wait Key/An ready + * Read BCAPS from sink + * Write BCAPS and AKSV into HDCP engine + * Write An and AKSV to sink + * Read BKSV from sink and write into HDCP engine + */ +static int hdmi_hdcp_wait_key_an_ready(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + int rc; + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 link0_status, keys_state; + u32 timeout_count; + bool an_ready; + + /* Wait for HDCP keys to be checked and validated */ + timeout_count = 100; + do { + link0_status = hdmi_read(hdmi, REG_HDMI_HDCP_LINK0_STATUS); + keys_state = (link0_status >> 28) & 0x7; + if (keys_state == HDCP_KEYS_STATE_VALID) + break; + + DBG("Keys not ready(%d). s=%d, l0=%0x08x", + timeout_count, keys_state, link0_status); + + timeout_count--; + if (!timeout_count) { + pr_err("%s: Wait key state timedout", __func__); + return -ETIMEDOUT; + } + + rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + if (rc) + return rc; + } while (1); + + timeout_count = 100; + do { + link0_status = hdmi_read(hdmi, REG_HDMI_HDCP_LINK0_STATUS); + an_ready = (link0_status & HDMI_HDCP_LINK0_STATUS_AN_0_READY) + && (link0_status & HDMI_HDCP_LINK0_STATUS_AN_1_READY); + if (an_ready) + break; + + DBG("An not ready(%d). l0_status=0x%08x", + timeout_count, link0_status); + + timeout_count--; + if (!timeout_count) { + pr_err("%s: Wait An timedout", __func__); + return -ETIMEDOUT; + } + + rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + if (rc) + return rc; + } while (1); + + return 0; +} + +static int hdmi_hdcp_send_aksv_an(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + int rc = 0; + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 link0_aksv_0, link0_aksv_1; + u32 link0_an[2]; + u8 aksv[5]; + + /* Read An0 and An1 */ + link0_an[0] = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA5); + link0_an[1] = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA6); + + /* Read AKSV */ + link0_aksv_0 = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA3); + link0_aksv_1 = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA4); + + DBG("Link ASKV=%08x%08x", link0_aksv_0, link0_aksv_1); + /* Copy An and AKSV to byte arrays for transmission */ + aksv[0] = link0_aksv_0 & 0xFF; + aksv[1] = (link0_aksv_0 >> 8) & 0xFF; + aksv[2] = (link0_aksv_0 >> 16) & 0xFF; + aksv[3] = (link0_aksv_0 >> 24) & 0xFF; + aksv[4] = link0_aksv_1 & 0xFF; + + /* Write An to offset 0x18 */ + rc = hdmi_ddc_write(hdmi, HDCP_PORT_ADDR, 0x18, (u8 *)link0_an, + (u16)sizeof(link0_an)); + if (rc) { + pr_err("%s:An write failed\n", __func__); + return rc; + } + DBG("Link0-An=%08x%08x", link0_an[0], link0_an[1]); + + /* Write AKSV to offset 0x10 */ + rc = hdmi_ddc_write(hdmi, HDCP_PORT_ADDR, 0x10, aksv, 5); + if (rc) { + pr_err("%s:AKSV write failed\n", __func__); + return rc; + } + DBG("Link0-AKSV=%02x%08x", link0_aksv_1 & 0xFF, link0_aksv_0); + + return 0; +} + +static int hdmi_hdcp_recv_bksv(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + int rc = 0; + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u8 bksv[5]; + u32 reg[2], data[2]; + + /* Read BKSV at offset 0x00 */ + rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x00, bksv, 5); + if (rc) { + pr_err("%s:BKSV read failed\n", __func__); + return rc; + } + + hdcp_ctrl->bksv_lsb = bksv[0] | (bksv[1] << 8) | + (bksv[2] << 16) | (bksv[3] << 24); + hdcp_ctrl->bksv_msb = bksv[4]; + DBG(":BKSV=%02x%08x", hdcp_ctrl->bksv_msb, hdcp_ctrl->bksv_lsb); + + /* check there are 20 ones in BKSV */ + if ((hweight32(hdcp_ctrl->bksv_lsb) + hweight32(hdcp_ctrl->bksv_msb)) + != 20) { + pr_err(": BKSV doesn't have 20 1's and 20 0's\n"); + pr_err(": BKSV chk fail. BKSV=%02x%02x%02x%02x%02x\n", + bksv[4], bksv[3], bksv[2], bksv[1], bksv[0]); + return -EINVAL; + } + + /* Write BKSV read from sink to HDCP registers */ + reg[0] = REG_HDMI_HDCP_RCVPORT_DATA0; + data[0] = hdcp_ctrl->bksv_lsb; + reg[1] = REG_HDMI_HDCP_RCVPORT_DATA1; + data[1] = hdcp_ctrl->bksv_msb; + rc = hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, 2); + + return rc; +} + +static int hdmi_hdcp_recv_bcaps(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + int rc = 0; + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 reg, data; + u8 bcaps; + + rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x40, &bcaps, 1); + if (rc) { + pr_err("%s:BCAPS read failed\n", __func__); + return rc; + } + DBG("BCAPS=%02x", bcaps); + + /* receiver (0), repeater (1) */ + hdcp_ctrl->ds_type = (bcaps & BIT(6)) ? DS_REPEATER : DS_RECEIVER; + + /* Write BCAPS to the hardware */ + reg = REG_HDMI_HDCP_RCVPORT_DATA12; + data = (u32)bcaps; + rc = hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1); + + return rc; +} + +static int hdmi_hdcp_auth_part1_key_exchange(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + struct hdmi *hdmi = hdcp_ctrl->hdmi; + unsigned long flags; + int rc; + + /* Wait for AKSV key and An ready */ + rc = hdmi_hdcp_wait_key_an_ready(hdcp_ctrl); + if (rc) { + pr_err("%s: wait key and an ready failed\n", __func__); + return rc; + }; + + /* Read BCAPS and send to HDCP engine */ + rc = hdmi_hdcp_recv_bcaps(hdcp_ctrl); + if (rc) { + pr_err("%s: read bcaps error, abort\n", __func__); + return rc; + } + + /* + * 1.1_Features turned off by default. + * No need to write AInfo since 1.1_Features is disabled. + */ + hdmi_write(hdmi, REG_HDMI_HDCP_RCVPORT_DATA4, 0); + + /* Send AKSV and An to sink */ + rc = hdmi_hdcp_send_aksv_an(hdcp_ctrl); + if (rc) { + pr_err("%s:An/Aksv write failed\n", __func__); + return rc; + } + + /* Read BKSV and send to HDCP engine*/ + rc = hdmi_hdcp_recv_bksv(hdcp_ctrl); + if (rc) { + pr_err("%s:BKSV Process failed\n", __func__); + return rc; + } + + /* Enable HDCP interrupts and ack/clear any stale interrupts */ + spin_lock_irqsave(&hdmi->reg_lock, flags); + hdmi_write(hdmi, REG_HDMI_HDCP_INT_CTRL, + HDMI_HDCP_INT_CTRL_AUTH_SUCCESS_ACK | + HDMI_HDCP_INT_CTRL_AUTH_SUCCESS_MASK | + HDMI_HDCP_INT_CTRL_AUTH_FAIL_ACK | + HDMI_HDCP_INT_CTRL_AUTH_FAIL_MASK | + HDMI_HDCP_INT_CTRL_AUTH_FAIL_INFO_ACK); + spin_unlock_irqrestore(&hdmi->reg_lock, flags); + + return 0; +} + +/* read R0' from sink and pass it to HDCP engine */ +static int hdmi_hdcp_auth_part1_recv_r0(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + struct hdmi *hdmi = hdcp_ctrl->hdmi; + int rc = 0; + u8 buf[2]; + + /* + * HDCP Compliance Test case 1A-01: + * Wait here at least 100ms before reading R0' + */ + rc = hdmi_hdcp_msleep(hdcp_ctrl, 125, AUTH_ABORT_EV); + if (rc) + return rc; + + /* Read R0' at offset 0x08 */ + rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x08, buf, 2); + if (rc) { + pr_err("%s:R0' read failed\n", __func__); + return rc; + } + DBG("R0'=%02x%02x", buf[1], buf[0]); + + /* Write R0' to HDCP registers and check to see if it is a match */ + hdmi_write(hdmi, REG_HDMI_HDCP_RCVPORT_DATA2_0, + (((u32)buf[1]) << 8) | buf[0]); + + return 0; +} + +/* Wait for authenticating result: R0/R0' are matched or not */ +static int hdmi_hdcp_auth_part1_verify_r0(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 link0_status; + int rc; + + /* wait for hdcp irq, 10 sec should be long enough */ + rc = hdmi_hdcp_msleep(hdcp_ctrl, 10000, AUTH_RESULT_RDY_EV); + if (!rc) { + pr_err("%s: Wait Auth IRQ timeout\n", __func__); + return -ETIMEDOUT; + } + + link0_status = hdmi_read(hdmi, REG_HDMI_HDCP_LINK0_STATUS); + if (!(link0_status & HDMI_HDCP_LINK0_STATUS_RI_MATCHES)) { + pr_err("%s: Authentication Part I failed\n", __func__); + return -EINVAL; + } + + /* Enable HDCP Encryption */ + hdmi_write(hdmi, REG_HDMI_HDCP_CTRL, + HDMI_HDCP_CTRL_ENABLE | + HDMI_HDCP_CTRL_ENCRYPTION_ENABLE); + + return 0; +} + +static int hdmi_hdcp_recv_check_bstatus(struct hdmi_hdcp_ctrl *hdcp_ctrl, + u16 *pbstatus) +{ + int rc; + struct hdmi *hdmi = hdcp_ctrl->hdmi; + bool max_devs_exceeded = false, max_cascade_exceeded = false; + u32 repeater_cascade_depth = 0, down_stream_devices = 0; + u16 bstatus; + u8 buf[2]; + + /* Read BSTATUS at offset 0x41 */ + rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x41, buf, 2); + if (rc) { + pr_err("%s: BSTATUS read failed\n", __func__); + goto error; + } + *pbstatus = bstatus = (buf[1] << 8) | buf[0]; + + + down_stream_devices = bstatus & 0x7F; + repeater_cascade_depth = (bstatus >> 8) & 0x7; + max_devs_exceeded = (bstatus & BIT(7)) ? true : false; + max_cascade_exceeded = (bstatus & BIT(11)) ? true : false; + + if (down_stream_devices == 0) { + /* + * If no downstream devices are attached to the repeater + * then part II fails. + * todo: The other approach would be to continue PART II. + */ + pr_err("%s: No downstream devices\n", __func__); + rc = -EINVAL; + goto error; + } + + /* + * HDCP Compliance 1B-05: + * Check if no. of devices connected to repeater + * exceed max_devices_connected from bit 7 of Bstatus. + */ + if (max_devs_exceeded) { + pr_err("%s: no. of devs connected exceeds max allowed", + __func__); + rc = -EINVAL; + goto error; + } + + /* + * HDCP Compliance 1B-06: + * Check if no. of cascade connected to repeater + * exceed max_cascade_connected from bit 11 of Bstatus. + */ + if (max_cascade_exceeded) { + pr_err("%s: no. of cascade conn exceeds max allowed", + __func__); + rc = -EINVAL; + goto error; + } + +error: + hdcp_ctrl->dev_count = down_stream_devices; + hdcp_ctrl->max_cascade_exceeded = max_cascade_exceeded; + hdcp_ctrl->max_dev_exceeded = max_devs_exceeded; + hdcp_ctrl->depth = repeater_cascade_depth; + return rc; +} + +static int hdmi_hdcp_auth_part2_wait_ksv_fifo_ready( + struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + int rc; + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 reg, data; + u32 timeout_count; + u16 bstatus; + u8 bcaps; + + /* + * Wait until READY bit is set in BCAPS, as per HDCP specifications + * maximum permitted time to check for READY bit is five seconds. + */ + timeout_count = 100; + do { + /* Read BCAPS at offset 0x40 */ + rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x40, &bcaps, 1); + if (rc) { + pr_err("%s: BCAPS read failed\n", __func__); + return rc; + } + + if (bcaps & BIT(5)) + break; + + timeout_count--; + if (!timeout_count) { + pr_err("%s: Wait KSV fifo ready timedout", __func__); + return -ETIMEDOUT; + } + + rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + if (rc) + return rc; + } while (1); + + rc = hdmi_hdcp_recv_check_bstatus(hdcp_ctrl, &bstatus); + if (rc) { + pr_err("%s: bstatus error\n", __func__); + return rc; + } + + /* Write BSTATUS and BCAPS to HDCP registers */ + reg = REG_HDMI_HDCP_RCVPORT_DATA12; + data = bcaps | (bstatus << 8); + rc = hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1); + if (rc) { + pr_err("%s: BSTATUS write failed\n", __func__); + return rc; + } + + return 0; +} + +/* + * hdcp authenticating part 2: 2nd + * read ksv fifo from sink + * transfer V' from sink to HDCP engine + * reset SHA engine + */ +static int hdmi_hdcp_transfer_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + struct hdmi *hdmi = hdcp_ctrl->hdmi; + int rc = 0; + struct hdmi_hdcp_reg_data reg_data[] = { + {REG_HDMI_HDCP_RCVPORT_DATA7, 0x20, "V' H0"}, + {REG_HDMI_HDCP_RCVPORT_DATA8, 0x24, "V' H1"}, + {REG_HDMI_HDCP_RCVPORT_DATA9, 0x28, "V' H2"}, + {REG_HDMI_HDCP_RCVPORT_DATA10, 0x2C, "V' H3"}, + {REG_HDMI_HDCP_RCVPORT_DATA11, 0x30, "V' H4"}, + }; + struct hdmi_hdcp_reg_data *rd; + u32 size = ARRAY_SIZE(reg_data); + u32 reg[ARRAY_SIZE(reg_data)]; + u32 data[ARRAY_SIZE(reg_data)]; + int i; + + for (i = 0; i < size; i++) { + rd = ®_data[i]; + rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, + rd->off, (u8 *)&data[i], (u16)sizeof(data[i])); + if (rc) { + pr_err("%s: Read %s failed\n", __func__, rd->name); + goto error; + } + + DBG("%s =%x", rd->name, data[i]); + reg[i] = reg_data[i].reg_id; + } + + rc = hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, size); + +error: + return rc; +} + +static int hdmi_hdcp_recv_ksv_fifo(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + int rc; + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 ksv_bytes; + + ksv_bytes = 5 * hdcp_ctrl->dev_count; + + rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x43, + hdcp_ctrl->ksv_list, ksv_bytes); + if (rc) + pr_err("%s: KSV FIFO read failed\n", __func__); + + return rc; +} + +static int hdmi_hdcp_reset_sha_engine(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + u32 reg[2], data[2]; + u32 rc = 0; + + reg[0] = REG_HDMI_HDCP_SHA_CTRL; + data[0] = HDCP_REG_ENABLE; + reg[1] = REG_HDMI_HDCP_SHA_CTRL; + data[1] = HDCP_REG_DISABLE; + + rc = hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, 2); + + return rc; +} + +static int hdmi_hdcp_auth_part2_recv_ksv_fifo( + struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + int rc; + u32 timeout_count; + + /* + * Read KSV FIFO over DDC + * Key Selection vector FIFO Used to pull downstream KSVs + * from HDCP Repeaters. + * All bytes (DEVICE_COUNT * 5) must be read in a single, + * auto incrementing access. + * All bytes read as 0x00 for HDCP Receivers that are not + * HDCP Repeaters (REPEATER == 0). + */ + timeout_count = 100; + do { + rc = hdmi_hdcp_recv_ksv_fifo(hdcp_ctrl); + if (!rc) + break; + + timeout_count--; + if (!timeout_count) { + pr_err("%s: Recv ksv fifo timedout", __func__); + return -ETIMEDOUT; + } + + rc = hdmi_hdcp_msleep(hdcp_ctrl, 25, AUTH_ABORT_EV); + if (rc) + return rc; + } while (1); + + rc = hdmi_hdcp_transfer_v_h(hdcp_ctrl); + if (rc) { + pr_err("%s: transfer V failed\n", __func__); + return rc; + } + + /* reset SHA engine before write ksv fifo */ + rc = hdmi_hdcp_reset_sha_engine(hdcp_ctrl); + if (rc) { + pr_err("%s: fail to reset sha engine\n", __func__); + return rc; + } + + return 0; +} + +/* + * Write KSV FIFO to HDCP_SHA_DATA. + * This is done 1 byte at time starting with the LSB. + * Once 64 bytes have been written, we need to poll for + * HDCP_SHA_BLOCK_DONE before writing any further + * If the last byte is written, we need to poll for + * HDCP_SHA_COMP_DONE to wait until HW finish + */ +static int hdmi_hdcp_write_ksv_fifo(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + int i; + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 ksv_bytes, last_byte = 0; + u8 *ksv_fifo = NULL; + u32 reg_val, data, reg; + u32 rc = 0; + + ksv_bytes = 5 * hdcp_ctrl->dev_count; + + /* Check if need to wait for HW completion */ + if (hdcp_ctrl->ksv_fifo_w_index) { + reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_SHA_STATUS); + DBG("HDCP_SHA_STATUS=%08x", reg_val); + if (hdcp_ctrl->ksv_fifo_w_index == ksv_bytes) { + /* check COMP_DONE if last write */ + if (reg_val & HDMI_HDCP_SHA_STATUS_COMP_DONE) { + DBG("COMP_DONE"); + return 0; + } else { + return -EAGAIN; + } + } else { + /* check BLOCK_DONE if not last write */ + if (!(reg_val & HDMI_HDCP_SHA_STATUS_BLOCK_DONE)) + return -EAGAIN; + + DBG("BLOCK_DONE"); + } + } + + ksv_bytes -= hdcp_ctrl->ksv_fifo_w_index; + if (ksv_bytes <= 64) + last_byte = 1; + else + ksv_bytes = 64; + + ksv_fifo = hdcp_ctrl->ksv_list; + ksv_fifo += hdcp_ctrl->ksv_fifo_w_index; + + for (i = 0; i < ksv_bytes; i++) { + /* Write KSV byte and set DONE bit[0] for last byte*/ + reg_val = ksv_fifo[i] << 16; + if ((i == (ksv_bytes - 1)) && last_byte) + reg_val |= HDMI_HDCP_SHA_DATA_DONE; + + reg = REG_HDMI_HDCP_SHA_DATA; + data = reg_val; + rc = hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1); + + if (rc) + return rc; + } + + hdcp_ctrl->ksv_fifo_w_index += ksv_bytes; + + /* + *return -EAGAIN to notify caller to wait for COMP_DONE or BLOCK_DONE + */ + return -EAGAIN; +} + +/* write ksv fifo into HDCP engine */ +static int hdmi_hdcp_auth_part2_write_ksv_fifo( + struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + int rc; + u32 timeout_count; + + hdcp_ctrl->ksv_fifo_w_index = 0; + timeout_count = 100; + do { + rc = hdmi_hdcp_write_ksv_fifo(hdcp_ctrl); + if (!rc) + break; + + if (rc != -EAGAIN) + return rc; + + timeout_count--; + if (!timeout_count) { + pr_err("%s: Write KSV fifo timedout", __func__); + return -ETIMEDOUT; + } + + rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + if (rc) + return rc; + } while (1); + + return 0; +} + +static int hdmi_hdcp_auth_part2_check_v_match(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + int rc = 0; + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 link0_status; + u32 timeout_count = 100; + + do { + link0_status = hdmi_read(hdmi, REG_HDMI_HDCP_LINK0_STATUS); + if (link0_status & HDMI_HDCP_LINK0_STATUS_V_MATCHES) + break; + + timeout_count--; + if (!timeout_count) { + pr_err("%s: HDCP V Match timedout", __func__); + return -ETIMEDOUT; + } + + rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + if (rc) + return rc; + } while (1); + + return 0; +} + +static void hdmi_hdcp_auth_work(struct work_struct *work) +{ + struct hdmi_hdcp_ctrl *hdcp_ctrl = container_of(work, + struct hdmi_hdcp_ctrl, hdcp_auth_work); + int rc; + + rc = hdmi_hdcp_auth_prepare(hdcp_ctrl); + if (rc) { + pr_err("%s: auth prepare failed %d\n", __func__, rc); + goto end; + } + + /* HDCP PartI */ + rc = hdmi_hdcp_auth_part1_key_exchange(hdcp_ctrl); + if (rc) { + pr_err("%s: key exchange failed %d\n", __func__, rc); + goto end; + } + + rc = hdmi_hdcp_auth_part1_recv_r0(hdcp_ctrl); + if (rc) { + pr_err("%s: receive r0 failed %d\n", __func__, rc); + goto end; + } + + rc = hdmi_hdcp_auth_part1_verify_r0(hdcp_ctrl); + if (rc) { + pr_err("%s: verify r0 failed %d\n", __func__, rc); + goto end; + } + pr_info("%s: Authentication Part I successful\n", __func__); + if (hdcp_ctrl->ds_type == DS_RECEIVER) + goto end; + + /* HDCP PartII */ + rc = hdmi_hdcp_auth_part2_wait_ksv_fifo_ready(hdcp_ctrl); + if (rc) { + pr_err("%s: wait ksv fifo ready failed %d\n", __func__, rc); + goto end; + } + + rc = hdmi_hdcp_auth_part2_recv_ksv_fifo(hdcp_ctrl); + if (rc) { + pr_err("%s: recv ksv fifo failed %d\n", __func__, rc); + goto end; + } + + rc = hdmi_hdcp_auth_part2_write_ksv_fifo(hdcp_ctrl); + if (rc) { + pr_err("%s: write ksv fifo failed %d\n", __func__, rc); + goto end; + } + + rc = hdmi_hdcp_auth_part2_check_v_match(hdcp_ctrl); + if (rc) + pr_err("%s: check v match failed %d\n", __func__, rc); + +end: + if (rc == -ECANCELED) { + pr_info("%s: hdcp authentication canceled\n", __func__); + } else if (rc == -ENOTSUPP) { + pr_info("%s: hdcp is not supported\n", __func__); + } else if (rc) { + pr_err("%s: hdcp authentication failed\n", __func__); + hdmi_hdcp_auth_fail(hdcp_ctrl); + } else { + hdmi_hdcp_auth_done(hdcp_ctrl); + } +} + +void hdmi_hdcp_on(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + struct hdmi *hdmi = hdcp_ctrl->hdmi; + u32 reg_val; + unsigned long flags; + + if ((HDCP_STATE_INACTIVE != hdcp_ctrl->hdcp_state) || + (HDCP_STATE_NO_AKSV == hdcp_ctrl->hdcp_state)) { + DBG("still active or activating or no askv. returning"); + return; + } + + /* clear HDMI Encrypt */ + spin_lock_irqsave(&hdmi->reg_lock, flags); + reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); + reg_val &= ~HDMI_CTRL_ENCRYPTED; + hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + spin_unlock_irqrestore(&hdmi->reg_lock, flags); + + hdcp_ctrl->auth_event = 0; + hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATING; + hdcp_ctrl->auth_retries = 0; + queue_work(hdmi->workq, &hdcp_ctrl->hdcp_auth_work); +} + +void hdmi_hdcp_off(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + struct hdmi *hdmi = hdcp_ctrl->hdmi; + unsigned long flags; + u32 reg_val; + + if ((HDCP_STATE_INACTIVE == hdcp_ctrl->hdcp_state) || + (HDCP_STATE_NO_AKSV == hdcp_ctrl->hdcp_state)) { + DBG("hdcp inactive or no aksv. returning"); + return; + } + + /* + * Disable HPD circuitry. + * This is needed to reset the HDCP cipher engine so that when we + * attempt a re-authentication, HW would clear the AN0_READY and + * AN1_READY bits in HDMI_HDCP_LINK0_STATUS register + */ + spin_lock_irqsave(&hdmi->reg_lock, flags); + reg_val = hdmi_read(hdmi, REG_HDMI_HPD_CTRL); + reg_val &= ~HDMI_HPD_CTRL_ENABLE; + hdmi_write(hdmi, REG_HDMI_HPD_CTRL, reg_val); + + /* + * Disable HDCP interrupts. + * Also, need to set the state to inactive here so that any ongoing + * reauth works will know that the HDCP session has been turned off. + */ + hdmi_write(hdmi, REG_HDMI_HDCP_INT_CTRL, 0); + spin_unlock_irqrestore(&hdmi->reg_lock, flags); + + /* + * Cancel any pending auth/reauth attempts. + * If one is ongoing, this will wait for it to finish. + * No more reauthentication attempts will be scheduled since we + * set the current state to inactive. + */ + set_bit(AUTH_ABORT_EV, &hdcp_ctrl->auth_event); + wake_up_all(&hdcp_ctrl->auth_event_queue); + cancel_work_sync(&hdcp_ctrl->hdcp_auth_work); + cancel_work_sync(&hdcp_ctrl->hdcp_reauth_work); + + hdmi_write(hdmi, REG_HDMI_HDCP_RESET, + HDMI_HDCP_RESET_LINK0_DEAUTHENTICATE); + + /* Disable encryption and disable the HDCP block */ + hdmi_write(hdmi, REG_HDMI_HDCP_CTRL, 0); + + spin_lock_irqsave(&hdmi->reg_lock, flags); + reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); + reg_val &= ~HDMI_CTRL_ENCRYPTED; + hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + + /* Enable HPD circuitry */ + reg_val = hdmi_read(hdmi, REG_HDMI_HPD_CTRL); + reg_val |= HDMI_HPD_CTRL_ENABLE; + hdmi_write(hdmi, REG_HDMI_HPD_CTRL, reg_val); + spin_unlock_irqrestore(&hdmi->reg_lock, flags); + + hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE; + + DBG("HDCP: Off"); +} + +struct hdmi_hdcp_ctrl *hdmi_hdcp_init(struct hdmi *hdmi) +{ + struct hdmi_hdcp_ctrl *hdcp_ctrl = NULL; + + if (!hdmi->qfprom_mmio) { + pr_err("%s: HDCP is not supported without qfprom\n", + __func__); + return ERR_PTR(-EINVAL); + } + + hdcp_ctrl = kzalloc(sizeof(*hdcp_ctrl), GFP_KERNEL); + if (!hdcp_ctrl) + return ERR_PTR(-ENOMEM); + + INIT_WORK(&hdcp_ctrl->hdcp_auth_work, hdmi_hdcp_auth_work); + INIT_WORK(&hdcp_ctrl->hdcp_reauth_work, hdmi_hdcp_reauth_work); + init_waitqueue_head(&hdcp_ctrl->auth_event_queue); + hdcp_ctrl->hdmi = hdmi; + hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE; + hdcp_ctrl->aksv_valid = false; + + if (qcom_scm_hdcp_available()) + hdcp_ctrl->tz_hdcp = true; + else + hdcp_ctrl->tz_hdcp = false; + + return hdcp_ctrl; +} + +void hdmi_hdcp_destroy(struct hdmi *hdmi) +{ + if (hdmi && hdmi->hdcp_ctrl) { + kfree(hdmi->hdcp_ctrl); + hdmi->hdcp_ctrl = NULL; + } +} diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c index 6997ec636c6d..3a01cb5051e2 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c @@ -426,57 +426,6 @@ static void hdmi_phy_8960_destroy(struct hdmi_phy *phy) kfree(phy_8960); } -static void hdmi_phy_8960_reset(struct hdmi_phy *phy) -{ - struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy); - struct hdmi *hdmi = phy_8960->hdmi; - unsigned int val; - - val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL); - - if (val & HDMI_PHY_CTRL_SW_RESET_LOW) { - /* pull low */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val & ~HDMI_PHY_CTRL_SW_RESET); - } else { - /* pull high */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val | HDMI_PHY_CTRL_SW_RESET); - } - - if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) { - /* pull low */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val & ~HDMI_PHY_CTRL_SW_RESET_PLL); - } else { - /* pull high */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val | HDMI_PHY_CTRL_SW_RESET_PLL); - } - - msleep(100); - - if (val & HDMI_PHY_CTRL_SW_RESET_LOW) { - /* pull high */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val | HDMI_PHY_CTRL_SW_RESET); - } else { - /* pull low */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val & ~HDMI_PHY_CTRL_SW_RESET); - } - - if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) { - /* pull high */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val | HDMI_PHY_CTRL_SW_RESET_PLL); - } else { - /* pull low */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val & ~HDMI_PHY_CTRL_SW_RESET_PLL); - } -} - static void hdmi_phy_8960_powerup(struct hdmi_phy *phy, unsigned long int pixclock) { @@ -511,7 +460,6 @@ static void hdmi_phy_8960_powerdown(struct hdmi_phy *phy) static const struct hdmi_phy_funcs hdmi_phy_8960_funcs = { .destroy = hdmi_phy_8960_destroy, - .reset = hdmi_phy_8960_reset, .powerup = hdmi_phy_8960_powerup, .powerdown = hdmi_phy_8960_powerdown, }; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c index 391433c1af7c..cb01421ae1e4 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c @@ -29,37 +29,6 @@ static void hdmi_phy_8x60_destroy(struct hdmi_phy *phy) kfree(phy_8x60); } -static void hdmi_phy_8x60_reset(struct hdmi_phy *phy) -{ - struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy); - struct hdmi *hdmi = phy_8x60->hdmi; - unsigned int val; - - val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL); - - if (val & HDMI_PHY_CTRL_SW_RESET_LOW) { - /* pull low */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val & ~HDMI_PHY_CTRL_SW_RESET); - } else { - /* pull high */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val | HDMI_PHY_CTRL_SW_RESET); - } - - msleep(100); - - if (val & HDMI_PHY_CTRL_SW_RESET_LOW) { - /* pull high */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val | HDMI_PHY_CTRL_SW_RESET); - } else { - /* pull low */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val & ~HDMI_PHY_CTRL_SW_RESET); - } -} - static void hdmi_phy_8x60_powerup(struct hdmi_phy *phy, unsigned long int pixclock) { @@ -182,7 +151,6 @@ static void hdmi_phy_8x60_powerdown(struct hdmi_phy *phy) static const struct hdmi_phy_funcs hdmi_phy_8x60_funcs = { .destroy = hdmi_phy_8x60_destroy, - .reset = hdmi_phy_8x60_reset, .powerup = hdmi_phy_8x60_powerup, .powerdown = hdmi_phy_8x60_powerdown, }; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c index 59fa6cdacb2a..56ab8917ee9a 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c @@ -19,7 +19,6 @@ struct hdmi_phy_8x74 { struct hdmi_phy base; - struct hdmi *hdmi; void __iomem *mmio; }; #define to_hdmi_phy_8x74(x) container_of(x, struct hdmi_phy_8x74, base) @@ -41,59 +40,6 @@ static void hdmi_phy_8x74_destroy(struct hdmi_phy *phy) kfree(phy_8x74); } -static void hdmi_phy_8x74_reset(struct hdmi_phy *phy) -{ - struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy); - struct hdmi *hdmi = phy_8x74->hdmi; - unsigned int val; - - /* NOTE that HDMI_PHY_CTL is in core mmio, not phy mmio: */ - - val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL); - - if (val & HDMI_PHY_CTRL_SW_RESET_LOW) { - /* pull low */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val & ~HDMI_PHY_CTRL_SW_RESET); - } else { - /* pull high */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val | HDMI_PHY_CTRL_SW_RESET); - } - - if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) { - /* pull low */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val & ~HDMI_PHY_CTRL_SW_RESET_PLL); - } else { - /* pull high */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val | HDMI_PHY_CTRL_SW_RESET_PLL); - } - - msleep(100); - - if (val & HDMI_PHY_CTRL_SW_RESET_LOW) { - /* pull high */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val | HDMI_PHY_CTRL_SW_RESET); - } else { - /* pull low */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val & ~HDMI_PHY_CTRL_SW_RESET); - } - - if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) { - /* pull high */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val | HDMI_PHY_CTRL_SW_RESET_PLL); - } else { - /* pull low */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - val & ~HDMI_PHY_CTRL_SW_RESET_PLL); - } -} - static void hdmi_phy_8x74_powerup(struct hdmi_phy *phy, unsigned long int pixclock) { @@ -117,7 +63,6 @@ static void hdmi_phy_8x74_powerdown(struct hdmi_phy *phy) static const struct hdmi_phy_funcs hdmi_phy_8x74_funcs = { .destroy = hdmi_phy_8x74_destroy, - .reset = hdmi_phy_8x74_reset, .powerup = hdmi_phy_8x74_powerup, .powerdown = hdmi_phy_8x74_powerdown, }; @@ -138,8 +83,6 @@ struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi) phy->funcs = &hdmi_phy_8x74_funcs; - phy_8x74->hdmi = hdmi; - /* for 8x74, the phy mmio is mapped separately: */ phy_8x74->mmio = msm_ioremap(hdmi->pdev, "phy_physical", "HDMI_8x74"); diff --git a/drivers/gpu/drm/msm/hdmi/qfprom.xml.h b/drivers/gpu/drm/msm/hdmi/qfprom.xml.h index 978c3f70872a..2aa23b98f8aa 100644 --- a/drivers/gpu/drm/msm/hdmi/qfprom.xml.h +++ b/drivers/gpu/drm/msm/hdmi/qfprom.xml.h @@ -8,19 +8,19 @@ 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 ( 676 bytes, from 2014-12-05 15:34:49) -- /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 ( 20915 bytes, from 2015-03-24 22:05:22) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2352 bytes, from 2015-04-12 15:02:42) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 35083 bytes, from 2015-04-12 15:04:03) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 22094 bytes, from 2015-05-12 12:45:23) -- /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 ( 1686 bytes, from 2014-10-31 16:48:57) -- /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 ( 29012 bytes, from 2015-05-12 12:45:23) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-12 12:45:23) - -Copyright (C) 2013 by the following authors: +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2576 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 36021 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 26057 bytes, from 2015-08-14 21:47:57) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) + +Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) Permission is hereby granted, free of charge, to any person obtaining diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h index 153fc487d683..74b86734fef5 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h @@ -8,17 +8,17 @@ 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 ( 676 bytes, from 2014-12-05 15:34:49) -- /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 ( 20915 bytes, from 2015-03-24 22:05:22) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2352 bytes, from 2015-04-12 15:02:42) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 35083 bytes, from 2015-04-12 15:04:03) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 22094 bytes, from 2015-05-12 12:45:23) -- /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 ( 1686 bytes, from 2014-10-31 16:48:57) -- /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 ( 29012 bytes, from 2015-05-12 12:45:23) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-12 12:45:23) +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2576 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 36021 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 26057 bytes, from 2015-08-14 21:47:57) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index c4bb9d9c7667..6ac9aa165768 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -334,13 +334,15 @@ static int mdp4_crtc_atomic_check(struct drm_crtc *crtc, return 0; } -static void mdp4_crtc_atomic_begin(struct drm_crtc *crtc) +static void mdp4_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) { struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); DBG("%s: begin", mdp4_crtc->name); } -static void mdp4_crtc_atomic_flush(struct drm_crtc *crtc) +static void mdp4_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) { struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); struct drm_device *dev = crtc->dev; @@ -680,7 +682,5 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev, drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs); plane->crtc = crtc; - mdp4_plane_install_properties(plane, &crtc->base); - return crtc; } diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c index 7369ee7f0c55..5ed38cf548a1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c @@ -19,8 +19,11 @@ #include "msm_drv.h" #include "mdp4_kms.h" -void mdp4_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask) +void mdp4_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask, + uint32_t old_irqmask) { + mdp4_write(to_mdp4_kms(mdp_kms), REG_MDP4_INTR_CLEAR, + irqmask ^ (irqmask & old_irqmask)); mdp4_write(to_mdp4_kms(mdp_kms), REG_MDP4_INTR_ENABLE, irqmask); } @@ -68,9 +71,10 @@ irqreturn_t mdp4_irq(struct msm_kms *kms) struct drm_device *dev = mdp4_kms->dev; struct msm_drm_private *priv = dev->dev_private; unsigned int id; - uint32_t status; + uint32_t status, enable; - status = mdp4_read(mdp4_kms, REG_MDP4_INTR_STATUS); + enable = mdp4_read(mdp4_kms, REG_MDP4_INTR_ENABLE); + status = mdp4_read(mdp4_kms, REG_MDP4_INTR_STATUS) & enable; mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, status); VERB("status=%08x", status); @@ -86,13 +90,22 @@ irqreturn_t mdp4_irq(struct msm_kms *kms) int mdp4_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) { + struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); + + mdp4_enable(mdp4_kms); mdp_update_vblank_mask(to_mdp_kms(kms), mdp4_crtc_vblank(crtc), true); + mdp4_disable(mdp4_kms); + return 0; } void mdp4_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) { + struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); + + mdp4_enable(mdp4_kms); mdp_update_vblank_mask(to_mdp_kms(kms), mdp4_crtc_vblank(crtc), false); + mdp4_disable(mdp4_kms); } diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index 531e4acc2a87..077f7521a971 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -241,22 +241,37 @@ int mdp4_enable(struct mdp4_kms *mdp4_kms) } #ifdef CONFIG_OF -static struct drm_panel *detect_panel(struct drm_device *dev, const char *name) +static struct drm_panel *detect_panel(struct drm_device *dev) { - struct device_node *n; + struct device_node *endpoint, *panel_node; + struct device_node *np = dev->dev->of_node; struct drm_panel *panel = NULL; - n = of_parse_phandle(dev->dev->of_node, name, 0); - if (n) { - panel = of_drm_find_panel(n); - if (!panel) - panel = ERR_PTR(-EPROBE_DEFER); + endpoint = of_graph_get_next_endpoint(np, NULL); + if (!endpoint) { + dev_err(dev->dev, "no valid endpoint\n"); + return ERR_PTR(-ENODEV); + } + + panel_node = of_graph_get_remote_port_parent(endpoint); + if (!panel_node) { + dev_err(dev->dev, "no valid panel node\n"); + of_node_put(endpoint); + return ERR_PTR(-ENODEV); + } + + of_node_put(endpoint); + + panel = of_drm_find_panel(panel_node); + if (!panel) { + of_node_put(panel_node); + return ERR_PTR(-EPROBE_DEFER); } return panel; } #else -static struct drm_panel *detect_panel(struct drm_device *dev, const char *name) +static struct drm_panel *detect_panel(struct drm_device *dev) { // ??? maybe use a module param to specify which panel is attached? } @@ -294,7 +309,7 @@ static int modeset_init(struct mdp4_kms *mdp4_kms) * Setup the LCDC/LVDS path: RGB2 -> DMA_P -> LCDC -> LVDS: */ - panel = detect_panel(dev, "qcom,lvds-panel"); + panel = detect_panel(dev); if (IS_ERR(panel)) { ret = PTR_ERR(panel); dev_err(dev->dev, "failed to detect LVDS panel: %d\n", ret); @@ -527,6 +542,11 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev) goto fail; } + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.max_width = 2048; + dev->mode_config.max_height = 2048; + return kms; fail: diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h index c1ecb9d6bdef..8a7f6e1e2bca 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h @@ -167,7 +167,8 @@ static inline uint32_t mixercfg(uint32_t mixer_cfg, int mixer, 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_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask, + uint32_t old_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); @@ -175,29 +176,24 @@ 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 bool pipe_supports_yuv(enum mdp4_pipe pipe) +static inline uint32_t mdp4_pipe_caps(enum mdp4_pipe pipe) { switch (pipe) { case VG1: case VG2: case VG3: case VG4: - return true; + return MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | + MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC; + case RGB1: + case RGB2: + case RGB3: + return MDP_PIPE_CAP_SCALE; default: - return false; + return 0; } } -static inline -uint32_t mdp4_get_formats(enum mdp4_pipe pipe_id, uint32_t *pixel_formats, - uint32_t max_formats) -{ - return mdp_get_formats(pixel_formats, max_formats, - !pipe_supports_yuv(pipe_id)); -} - -void mdp4_plane_install_properties(struct drm_plane *plane, - struct drm_mode_object *obj); 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); diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c index c04843376c54..4cd6e721aa0a 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c @@ -346,8 +346,10 @@ static void mdp4_lcdc_encoder_disable(struct drm_encoder *encoder) mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0); - if (panel) + if (panel) { drm_panel_disable(panel); + drm_panel_unprepare(panel); + } /* * Wait for a vsync so we know the ENABLE=0 latched before @@ -412,8 +414,10 @@ static void mdp4_lcdc_encoder_enable(struct drm_encoder *encoder) if (ret) dev_err(dev->dev, "failed to enable lcdc_clk: %d\n", ret); - if (panel) + if (panel) { + drm_panel_prepare(panel); drm_panel_enable(panel); + } setup_phy(encoder); diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 247a424445f7..e9dee367b597 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -26,6 +26,7 @@ struct mdp4_plane { enum mdp4_pipe pipe; + uint32_t caps; uint32_t nformats; uint32_t formats[32]; @@ -74,7 +75,7 @@ static void mdp4_plane_destroy(struct drm_plane *plane) } /* helper to install properties which are common to planes and crtcs */ -void mdp4_plane_install_properties(struct drm_plane *plane, +static void mdp4_plane_install_properties(struct drm_plane *plane, struct drm_mode_object *obj) { // XXX @@ -382,9 +383,11 @@ struct drm_plane *mdp4_plane_init(struct drm_device *dev, mdp4_plane->pipe = pipe_id; mdp4_plane->name = pipe_names[pipe_id]; + mdp4_plane->caps = mdp4_pipe_caps(pipe_id); - mdp4_plane->nformats = mdp4_get_formats(pipe_id, mdp4_plane->formats, - ARRAY_SIZE(mdp4_plane->formats)); + mdp4_plane->nformats = mdp_get_formats(mdp4_plane->formats, + ARRAY_SIZE(mdp4_plane->formats), + !pipe_supports_yuv(mdp4_plane->caps)); type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; ret = drm_universal_plane_init(dev, plane, 0xff, &mdp4_plane_funcs, diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h index 50e17527e2e5..3469f50d5590 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h @@ -8,17 +8,17 @@ 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 ( 676 bytes, from 2014-12-05 15:34:49) -- /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 ( 20915 bytes, from 2015-03-24 22:05:22) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2352 bytes, from 2015-04-12 15:02:42) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 35083 bytes, from 2015-04-12 15:04:03) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 22094 bytes, from 2015-05-12 12:45:23) -- /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 ( 1686 bytes, from 2014-10-31 16:48:57) -- /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 ( 29012 bytes, from 2015-05-12 12:45:23) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-12 12:45:23) +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2576 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 36021 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 26057 bytes, from 2015-08-14 21:47:57) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) @@ -381,49 +381,49 @@ static inline uint32_t REG_MDP5_CTL_LAYER(uint32_t i0, uint32_t i1) { return 0x0 static inline uint32_t REG_MDP5_CTL_LAYER_REG(uint32_t i0, uint32_t i1) { return 0x00000000 + __offset_CTL(i0) + __offset_LAYER(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) +static inline uint32_t MDP5_CTL_LAYER_REG_VIG0(uint32_t 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) +static inline uint32_t MDP5_CTL_LAYER_REG_VIG1(uint32_t 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) +static inline uint32_t MDP5_CTL_LAYER_REG_VIG2(uint32_t 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) +static inline uint32_t MDP5_CTL_LAYER_REG_RGB0(uint32_t 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) +static inline uint32_t MDP5_CTL_LAYER_REG_RGB1(uint32_t 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) +static inline uint32_t MDP5_CTL_LAYER_REG_RGB2(uint32_t 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) +static inline uint32_t MDP5_CTL_LAYER_REG_DMA0(uint32_t 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) +static inline uint32_t MDP5_CTL_LAYER_REG_DMA1(uint32_t val) { return ((val) << MDP5_CTL_LAYER_REG_DMA1__SHIFT) & MDP5_CTL_LAYER_REG_DMA1__MASK; } @@ -431,13 +431,13 @@ static inline uint32_t MDP5_CTL_LAYER_REG_DMA1(enum mdp_mixer_stage_id val) #define MDP5_CTL_LAYER_REG_CURSOR_OUT 0x02000000 #define MDP5_CTL_LAYER_REG_VIG3__MASK 0x1c000000 #define MDP5_CTL_LAYER_REG_VIG3__SHIFT 26 -static inline uint32_t MDP5_CTL_LAYER_REG_VIG3(enum mdp_mixer_stage_id val) +static inline uint32_t MDP5_CTL_LAYER_REG_VIG3(uint32_t val) { return ((val) << MDP5_CTL_LAYER_REG_VIG3__SHIFT) & MDP5_CTL_LAYER_REG_VIG3__MASK; } #define MDP5_CTL_LAYER_REG_RGB3__MASK 0xe0000000 #define MDP5_CTL_LAYER_REG_RGB3__SHIFT 29 -static inline uint32_t MDP5_CTL_LAYER_REG_RGB3(enum mdp_mixer_stage_id val) +static inline uint32_t MDP5_CTL_LAYER_REG_RGB3(uint32_t val) { return ((val) << MDP5_CTL_LAYER_REG_RGB3__SHIFT) & MDP5_CTL_LAYER_REG_RGB3__MASK; } @@ -499,6 +499,44 @@ static inline uint32_t REG_MDP5_CTL_START(uint32_t i0) { return 0x0000001c + __o static inline uint32_t REG_MDP5_CTL_PACK_3D(uint32_t i0) { return 0x00000020 + __offset_CTL(i0); } +static inline uint32_t __offset_LAYER_EXT(uint32_t idx) +{ + switch (idx) { + case 0: return 0x00000040; + case 1: return 0x00000044; + case 2: return 0x00000048; + case 3: return 0x0000004c; + case 4: return 0x00000050; + case 5: return 0x00000054; + default: return INVALID_IDX(idx); + } +} +static inline uint32_t REG_MDP5_CTL_LAYER_EXT(uint32_t i0, uint32_t i1) { return 0x00000000 + __offset_CTL(i0) + __offset_LAYER_EXT(i1); } + +static inline uint32_t REG_MDP5_CTL_LAYER_EXT_REG(uint32_t i0, uint32_t i1) { return 0x00000000 + __offset_CTL(i0) + __offset_LAYER_EXT(i1); } +#define MDP5_CTL_LAYER_EXT_REG_VIG0_BIT3 0x00000001 +#define MDP5_CTL_LAYER_EXT_REG_VIG1_BIT3 0x00000004 +#define MDP5_CTL_LAYER_EXT_REG_VIG2_BIT3 0x00000010 +#define MDP5_CTL_LAYER_EXT_REG_VIG3_BIT3 0x00000040 +#define MDP5_CTL_LAYER_EXT_REG_RGB0_BIT3 0x00000100 +#define MDP5_CTL_LAYER_EXT_REG_RGB1_BIT3 0x00000400 +#define MDP5_CTL_LAYER_EXT_REG_RGB2_BIT3 0x00001000 +#define MDP5_CTL_LAYER_EXT_REG_RGB3_BIT3 0x00004000 +#define MDP5_CTL_LAYER_EXT_REG_DMA0_BIT3 0x00010000 +#define MDP5_CTL_LAYER_EXT_REG_DMA1_BIT3 0x00040000 +#define MDP5_CTL_LAYER_EXT_REG_CURSOR0__MASK 0x00f00000 +#define MDP5_CTL_LAYER_EXT_REG_CURSOR0__SHIFT 20 +static inline uint32_t MDP5_CTL_LAYER_EXT_REG_CURSOR0(enum mdp_mixer_stage_id val) +{ + return ((val) << MDP5_CTL_LAYER_EXT_REG_CURSOR0__SHIFT) & MDP5_CTL_LAYER_EXT_REG_CURSOR0__MASK; +} +#define MDP5_CTL_LAYER_EXT_REG_CURSOR1__MASK 0x3c000000 +#define MDP5_CTL_LAYER_EXT_REG_CURSOR1__SHIFT 26 +static inline uint32_t MDP5_CTL_LAYER_EXT_REG_CURSOR1(enum mdp_mixer_stage_id val) +{ + return ((val) << MDP5_CTL_LAYER_EXT_REG_CURSOR1__SHIFT) & MDP5_CTL_LAYER_EXT_REG_CURSOR1__MASK; +} + static inline uint32_t __offset_PIPE(enum mdp5_pipe idx) { switch (idx) { @@ -803,11 +841,11 @@ static inline uint32_t MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(uint32_t val) } #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 0x00180000 -#define MDP5_PIPE_SRC_FORMAT_NUM_PLANES__SHIFT 19 -static inline uint32_t MDP5_PIPE_SRC_FORMAT_NUM_PLANES(enum mdp_fetch_type val) +#define MDP5_PIPE_SRC_FORMAT_FETCH_TYPE__MASK 0x00180000 +#define MDP5_PIPE_SRC_FORMAT_FETCH_TYPE__SHIFT 19 +static inline uint32_t MDP5_PIPE_SRC_FORMAT_FETCH_TYPE(enum mdp_fetch_type val) { - return ((val) << MDP5_PIPE_SRC_FORMAT_NUM_PLANES__SHIFT) & MDP5_PIPE_SRC_FORMAT_NUM_PLANES__MASK; + return ((val) << MDP5_PIPE_SRC_FORMAT_FETCH_TYPE__SHIFT) & MDP5_PIPE_SRC_FORMAT_FETCH_TYPE__MASK; } #define MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP__MASK 0x01800000 #define MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP__SHIFT 23 @@ -897,41 +935,41 @@ static inline uint32_t MDP5_PIPE_DECIMATION_HORZ(uint32_t val) static inline uint32_t REG_MDP5_PIPE_SCALE_CONFIG(enum mdp5_pipe i0) { return 0x00000204 + __offset_PIPE(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) +#define MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_0__MASK 0x00000300 +#define MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_0__SHIFT 8 +static inline uint32_t MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_0(enum mdp5_scale_filter val) { - return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER__MASK; + return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_0__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_0__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) +#define MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_0__MASK 0x00000c00 +#define MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_0__SHIFT 10 +static inline uint32_t MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_0(enum mdp5_scale_filter val) { - return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER__MASK; + return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_0__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_0__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) +#define MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_1_2__MASK 0x00003000 +#define MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_1_2__SHIFT 12 +static inline uint32_t MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_1_2(enum mdp5_scale_filter val) { - return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER__MASK; + return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_1_2__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_1_2__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) +#define MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_1_2__MASK 0x0000c000 +#define MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_1_2__SHIFT 14 +static inline uint32_t MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_1_2(enum mdp5_scale_filter val) { - return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER__MASK; + return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_1_2__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_1_2__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) +#define MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_3__MASK 0x00030000 +#define MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_3__SHIFT 16 +static inline uint32_t MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_3(enum mdp5_scale_filter val) { - return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER__MASK; + return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_3__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_3__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) +#define MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_3__MASK 0x000c0000 +#define MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_3__SHIFT 18 +static inline uint32_t MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_3(enum mdp5_scale_filter val) { - return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER__MASK; + return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_3__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_3__MASK; } static inline uint32_t REG_MDP5_PIPE_SCALE_PHASE_STEP_X(enum mdp5_pipe i0) { return 0x00000210 + __offset_PIPE(i0); } @@ -984,9 +1022,22 @@ static inline uint32_t REG_MDP5_LM_BORDER_COLOR_0(uint32_t i0) { return 0x000000 static inline uint32_t REG_MDP5_LM_BORDER_COLOR_1(uint32_t i0) { return 0x00000010 + __offset_LM(i0); } -static inline uint32_t REG_MDP5_LM_BLEND(uint32_t i0, uint32_t i1) { return 0x00000020 + __offset_LM(i0) + 0x30*i1; } +static inline uint32_t __offset_BLEND(uint32_t idx) +{ + switch (idx) { + case 0: return 0x00000020; + case 1: return 0x00000050; + case 2: return 0x00000080; + case 3: return 0x000000b0; + case 4: return 0x00000230; + case 5: return 0x00000260; + case 6: return 0x00000290; + default: return INVALID_IDX(idx); + } +} +static inline uint32_t REG_MDP5_LM_BLEND(uint32_t i0, uint32_t i1) { return 0x00000000 + __offset_LM(i0) + __offset_BLEND(i1); } -static inline uint32_t REG_MDP5_LM_BLEND_OP_MODE(uint32_t i0, uint32_t i1) { return 0x00000020 + __offset_LM(i0) + 0x30*i1; } +static inline uint32_t REG_MDP5_LM_BLEND_OP_MODE(uint32_t i0, uint32_t i1) { return 0x00000000 + __offset_LM(i0) + __offset_BLEND(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) @@ -1008,25 +1059,25 @@ static inline uint32_t MDP5_LM_BLEND_OP_MODE_BG_ALPHA(enum mdp_alpha_type val) #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 0x00000024 + __offset_LM(i0) + 0x30*i1; } +static inline uint32_t REG_MDP5_LM_BLEND_FG_ALPHA(uint32_t i0, uint32_t i1) { return 0x00000004 + __offset_LM(i0) + __offset_BLEND(i1); } -static inline uint32_t REG_MDP5_LM_BLEND_BG_ALPHA(uint32_t i0, uint32_t i1) { return 0x00000028 + __offset_LM(i0) + 0x30*i1; } +static inline uint32_t REG_MDP5_LM_BLEND_BG_ALPHA(uint32_t i0, uint32_t i1) { return 0x00000008 + __offset_LM(i0) + __offset_BLEND(i1); } -static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_LOW0(uint32_t i0, uint32_t i1) { return 0x0000002c + __offset_LM(i0) + 0x30*i1; } +static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_LOW0(uint32_t i0, uint32_t i1) { return 0x0000000c + __offset_LM(i0) + __offset_BLEND(i1); } -static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_LOW1(uint32_t i0, uint32_t i1) { return 0x00000030 + __offset_LM(i0) + 0x30*i1; } +static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_LOW1(uint32_t i0, uint32_t i1) { return 0x00000010 + __offset_LM(i0) + __offset_BLEND(i1); } -static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_HIGH0(uint32_t i0, uint32_t i1) { return 0x00000034 + __offset_LM(i0) + 0x30*i1; } +static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_HIGH0(uint32_t i0, uint32_t i1) { return 0x00000014 + __offset_LM(i0) + __offset_BLEND(i1); } -static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_HIGH1(uint32_t i0, uint32_t i1) { return 0x00000038 + __offset_LM(i0) + 0x30*i1; } +static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_HIGH1(uint32_t i0, uint32_t i1) { return 0x00000018 + __offset_LM(i0) + __offset_BLEND(i1); } -static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_LOW0(uint32_t i0, uint32_t i1) { return 0x0000003c + __offset_LM(i0) + 0x30*i1; } +static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_LOW0(uint32_t i0, uint32_t i1) { return 0x0000001c + __offset_LM(i0) + __offset_BLEND(i1); } -static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_LOW1(uint32_t i0, uint32_t i1) { return 0x00000040 + __offset_LM(i0) + 0x30*i1; } +static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_LOW1(uint32_t i0, uint32_t i1) { return 0x00000020 + __offset_LM(i0) + __offset_BLEND(i1); } -static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_HIGH0(uint32_t i0, uint32_t i1) { return 0x00000044 + __offset_LM(i0) + 0x30*i1; } +static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_HIGH0(uint32_t i0, uint32_t i1) { return 0x00000024 + __offset_LM(i0) + __offset_BLEND(i1); } -static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_HIGH1(uint32_t i0, uint32_t i1) { return 0x00000048 + __offset_LM(i0) + 0x30*i1; } +static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_HIGH1(uint32_t i0, uint32_t i1) { return 0x00000028 + __offset_LM(i0) + __offset_BLEND(i1); } static inline uint32_t REG_MDP5_LM_CURSOR_IMG_SIZE(uint32_t i0) { return 0x000000e0 + __offset_LM(i0); } #define MDP5_LM_CURSOR_IMG_SIZE_SRC_W__MASK 0x0000ffff @@ -1260,6 +1311,13 @@ static inline uint32_t REG_MDP5_PP_FBC_LOSSY_MODE(uint32_t i0) { return 0x000000 static inline uint32_t __offset_WB(uint32_t idx) { switch (idx) { +#if 0 /* TEMPORARY until patch that adds wb.base[] is merged */ + case 0: return (mdp5_cfg->wb.base[0]); + case 1: return (mdp5_cfg->wb.base[1]); + case 2: return (mdp5_cfg->wb.base[2]); + case 3: return (mdp5_cfg->wb.base[3]); + case 4: return (mdp5_cfg->wb.base[4]); +#endif default: return INVALID_IDX(idx); } } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c index 8b9a7931b162..a1e26f23c7cc 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c @@ -22,7 +22,76 @@ struct mdp5_cfg_handler { /* mdp5_cfg must be exposed (used in mdp5.xml.h) */ const struct mdp5_cfg_hw *mdp5_cfg = NULL; -const struct mdp5_cfg_hw msm8x74_config = { +const struct mdp5_cfg_hw msm8x74v1_config = { + .name = "msm8x74v1", + .mdp = { + .count = 1, + .base = { 0x00100 }, + }, + .smp = { + .mmb_count = 22, + .mmb_size = 4096, + .clients = { + [SSPP_VIG0] = 1, [SSPP_VIG1] = 4, [SSPP_VIG2] = 7, + [SSPP_DMA0] = 10, [SSPP_DMA1] = 13, + [SSPP_RGB0] = 16, [SSPP_RGB1] = 17, [SSPP_RGB2] = 18, + }, + }, + .ctl = { + .count = 5, + .base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 }, + .flush_hw_mask = 0x0003ffff, + }, + .pipe_vig = { + .count = 3, + .base = { 0x01200, 0x01600, 0x01a00 }, + .caps = MDP_PIPE_CAP_HFLIP | + MDP_PIPE_CAP_VFLIP | + MDP_PIPE_CAP_SCALE | + MDP_PIPE_CAP_CSC | + 0, + }, + .pipe_rgb = { + .count = 3, + .base = { 0x01e00, 0x02200, 0x02600 }, + .caps = MDP_PIPE_CAP_HFLIP | + MDP_PIPE_CAP_VFLIP | + MDP_PIPE_CAP_SCALE | + 0, + }, + .pipe_dma = { + .count = 2, + .base = { 0x02a00, 0x02e00 }, + .caps = MDP_PIPE_CAP_HFLIP | + MDP_PIPE_CAP_VFLIP | + 0, + }, + .lm = { + .count = 5, + .base = { 0x03200, 0x03600, 0x03a00, 0x03e00, 0x04200 }, + .nb_stages = 5, + }, + .dspp = { + .count = 3, + .base = { 0x04600, 0x04a00, 0x04e00 }, + }, + .pp = { + .count = 3, + .base = { 0x21b00, 0x21c00, 0x21d00 }, + }, + .intf = { + .base = { 0x21100, 0x21300, 0x21500, 0x21700 }, + .connect = { + [0] = INTF_eDP, + [1] = INTF_DSI, + [2] = INTF_DSI, + [3] = INTF_HDMI, + }, + }, + .max_clk = 200000000, +}; + +const struct mdp5_cfg_hw msm8x74v2_config = { .name = "msm8x74", .mdp = { .count = 1, @@ -45,19 +114,27 @@ const struct mdp5_cfg_hw msm8x74_config = { .pipe_vig = { .count = 3, .base = { 0x01200, 0x01600, 0x01a00 }, + .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | + MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC | + MDP_PIPE_CAP_DECIMATION, }, .pipe_rgb = { .count = 3, .base = { 0x01e00, 0x02200, 0x02600 }, + .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | + MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_DECIMATION, }, .pipe_dma = { .count = 2, .base = { 0x02a00, 0x02e00 }, + .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP, }, .lm = { .count = 5, .base = { 0x03200, 0x03600, 0x03a00, 0x03e00, 0x04200 }, .nb_stages = 5, + .max_width = 2048, + .max_height = 0xFFFF, }, .dspp = { .count = 3, @@ -65,7 +142,7 @@ const struct mdp5_cfg_hw msm8x74_config = { }, .ad = { .count = 2, - .base = { 0x13100, 0x13300 }, /* NOTE: no ad in v1.0 */ + .base = { 0x13100, 0x13300 }, }, .pp = { .count = 3, @@ -113,19 +190,27 @@ const struct mdp5_cfg_hw apq8084_config = { .pipe_vig = { .count = 4, .base = { 0x01200, 0x01600, 0x01a00, 0x01e00 }, + .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | + MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC | + MDP_PIPE_CAP_DECIMATION, }, .pipe_rgb = { .count = 4, .base = { 0x02200, 0x02600, 0x02a00, 0x02e00 }, + .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | + MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_DECIMATION, }, .pipe_dma = { .count = 2, .base = { 0x03200, 0x03600 }, + .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP, }, .lm = { .count = 6, .base = { 0x03a00, 0x03e00, 0x04200, 0x04600, 0x04a00, 0x04e00 }, .nb_stages = 5, + .max_width = 2048, + .max_height = 0xFFFF, }, .dspp = { .count = 4, @@ -174,19 +259,27 @@ const struct mdp5_cfg_hw msm8x16_config = { .pipe_vig = { .count = 1, .base = { 0x05000 }, + .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | + MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC | + MDP_PIPE_CAP_DECIMATION, }, .pipe_rgb = { .count = 2, .base = { 0x15000, 0x17000 }, + .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | + MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_DECIMATION, }, .pipe_dma = { .count = 1, .base = { 0x25000 }, + .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP, }, .lm = { .count = 2, /* LM0 and LM3 */ .base = { 0x45000, 0x48000 }, .nb_stages = 5, + .max_width = 2048, + .max_height = 0xFFFF, }, .dspp = { .count = 1, @@ -203,14 +296,91 @@ const struct mdp5_cfg_hw msm8x16_config = { .max_clk = 320000000, }; +const struct mdp5_cfg_hw msm8x94_config = { + .name = "msm8x94", + .mdp = { + .count = 1, + .base = { 0x01000 }, + }, + .smp = { + .mmb_count = 44, + .mmb_size = 8192, + .clients = { + [SSPP_VIG0] = 1, [SSPP_VIG1] = 4, + [SSPP_VIG2] = 7, [SSPP_VIG3] = 19, + [SSPP_DMA0] = 10, [SSPP_DMA1] = 13, + [SSPP_RGB0] = 16, [SSPP_RGB1] = 17, + [SSPP_RGB2] = 18, [SSPP_RGB3] = 22, + }, + .reserved_state[0] = GENMASK(23, 0), /* first 24 MMBs */ + .reserved = { + [1] = 1, [4] = 1, [7] = 1, [19] = 1, + [16] = 5, [17] = 5, [18] = 5, [22] = 5, + }, + }, + .ctl = { + .count = 5, + .base = { 0x02000, 0x02200, 0x02400, 0x02600, 0x02800 }, + .flush_hw_mask = 0xf0ffffff, + }, + .pipe_vig = { + .count = 4, + .base = { 0x05000, 0x07000, 0x09000, 0x0b000 }, + .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | + MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC | + MDP_PIPE_CAP_DECIMATION, + }, + .pipe_rgb = { + .count = 4, + .base = { 0x15000, 0x17000, 0x19000, 0x1b000 }, + .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | + MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_DECIMATION, + }, + .pipe_dma = { + .count = 2, + .base = { 0x25000, 0x27000 }, + .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP, + }, + .lm = { + .count = 6, + .base = { 0x45000, 0x46000, 0x47000, 0x48000, 0x49000, 0x4a000 }, + .nb_stages = 8, + .max_width = 2048, + .max_height = 0xFFFF, + }, + .dspp = { + .count = 4, + .base = { 0x55000, 0x57000, 0x59000, 0x5b000 }, + + }, + .ad = { + .count = 3, + .base = { 0x79000, 0x79800, 0x7a000 }, + }, + .pp = { + .count = 4, + .base = { 0x71000, 0x71800, 0x72000, 0x72800 }, + }, + .intf = { + .base = { 0x6b000, 0x6b800, 0x6c000, 0x6c800, 0x6d000 }, + .connect = { + [0] = INTF_DISABLED, + [1] = INTF_DSI, + [2] = INTF_DSI, + [3] = INTF_HDMI, + }, + }, + .max_clk = 320000000, +}; + static const struct mdp5_cfg_handler cfg_handlers[] = { - { .revision = 0, .config = { .hw = &msm8x74_config } }, - { .revision = 2, .config = { .hw = &msm8x74_config } }, + { .revision = 0, .config = { .hw = &msm8x74v1_config } }, + { .revision = 2, .config = { .hw = &msm8x74v2_config } }, { .revision = 3, .config = { .hw = &apq8084_config } }, { .revision = 6, .config = { .hw = &msm8x16_config } }, + { .revision = 9, .config = { .hw = &msm8x94_config } }, }; - static struct mdp5_cfg_platform *mdp5_get_config(struct platform_device *dev); const struct mdp5_cfg_hw *mdp5_cfg_get_hw_config(struct mdp5_cfg_handler *cfg_handler) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h index 69349abe59f2..efb918d9f68b 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h @@ -42,6 +42,13 @@ struct mdp5_sub_block { struct mdp5_lm_block { MDP5_SUB_BLOCK_DEFINITION; uint32_t nb_stages; /* number of stages per blender */ + uint32_t max_width; /* Maximum output resolution */ + uint32_t max_height; +}; + +struct mdp5_pipe_block { + MDP5_SUB_BLOCK_DEFINITION; + uint32_t caps; /* pipe capabilities */ }; struct mdp5_ctl_block { @@ -70,9 +77,9 @@ struct mdp5_cfg_hw { struct mdp5_sub_block mdp; struct mdp5_smp_block smp; struct mdp5_ctl_block ctl; - struct mdp5_sub_block pipe_vig; - struct mdp5_sub_block pipe_rgb; - struct mdp5_sub_block pipe_dma; + struct mdp5_pipe_block pipe_vig; + struct mdp5_pipe_block pipe_rgb; + struct mdp5_pipe_block pipe_dma; struct mdp5_lm_block lm; struct mdp5_sub_block dspp; struct mdp5_sub_block ad; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c index ee31b16fe7ea..8e6c9b598a57 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c @@ -21,6 +21,8 @@ struct mdp5_cmd_encoder { struct mdp5_interface intf; bool enabled; uint32_t bsc; + + struct mdp5_ctl *ctl; }; #define to_mdp5_cmd_encoder(x) container_of(x, struct mdp5_cmd_encoder, base) @@ -210,13 +212,14 @@ static void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder, mode->vsync_end, mode->vtotal, mode->type, mode->flags); pingpong_tearcheck_setup(encoder, mode); - mdp5_crtc_set_intf(encoder->crtc, &mdp5_cmd_enc->intf); + mdp5_crtc_set_pipeline(encoder->crtc, &mdp5_cmd_enc->intf, + mdp5_cmd_enc->ctl); } static void mdp5_cmd_encoder_disable(struct drm_encoder *encoder) { struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder); - struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc); + struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl; struct mdp5_interface *intf = &mdp5_cmd_enc->intf; if (WARN_ON(!mdp5_cmd_enc->enabled)) @@ -235,7 +238,7 @@ static void mdp5_cmd_encoder_disable(struct drm_encoder *encoder) static void mdp5_cmd_encoder_enable(struct drm_encoder *encoder) { struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder); - struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc); + struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl; struct mdp5_interface *intf = &mdp5_cmd_enc->intf; if (WARN_ON(mdp5_cmd_enc->enabled)) @@ -300,7 +303,7 @@ int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder, /* initialize command mode encoder */ struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev, - struct mdp5_interface *intf) + struct mdp5_interface *intf, struct mdp5_ctl *ctl) { struct drm_encoder *encoder = NULL; struct mdp5_cmd_encoder *mdp5_cmd_enc; @@ -320,6 +323,7 @@ struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev, memcpy(&mdp5_cmd_enc->intf, intf, sizeof(mdp5_cmd_enc->intf)); encoder = &mdp5_cmd_enc->base; + mdp5_cmd_enc->ctl = ctl; drm_encoder_init(dev, encoder, &mdp5_cmd_encoder_funcs, DRM_MODE_ENCODER_DSI); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index dea3d2e559b1..7f9f4ac88029 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -160,8 +160,7 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) if (mdp5_crtc->ctl && !crtc->state->enable) { /* set STAGE_UNUSED for all layers */ - mdp5_ctl_blend(mdp5_crtc->ctl, mdp5_crtc->lm, 0x00000000); - mdp5_ctl_release(mdp5_crtc->ctl); + mdp5_ctl_blend(mdp5_crtc->ctl, NULL, 0, 0); mdp5_crtc->ctl = NULL; } } @@ -196,13 +195,9 @@ static bool mdp5_crtc_mode_fixup(struct drm_crtc *crtc, /* * blend_setup() - blend all the planes of a CRTC * - * When border is enabled, the border color will ALWAYS be the base layer. - * Therefore, the first plane (private RGB pipe) will start at STAGE0. - * If disabled, the first plane starts at STAGE_BASE. - * - * Note: - * Border is not enabled here because the private plane is exactly - * the CRTC resolution. + * If no base layer is available, border will be enabled as the base layer. + * Otherwise all layers will be blended based on their stage calculated + * in mdp5_crtc_atomic_check. */ static void blend_setup(struct drm_crtc *crtc) { @@ -210,9 +205,14 @@ static void blend_setup(struct drm_crtc *crtc) struct mdp5_kms *mdp5_kms = get_kms(crtc); struct drm_plane *plane; const struct mdp5_cfg_hw *hw_cfg; - uint32_t lm = mdp5_crtc->lm, blend_cfg = 0; + struct mdp5_plane_state *pstate, *pstates[STAGE_MAX + 1] = {NULL}; + const struct mdp_format *format; + uint32_t lm = mdp5_crtc->lm; + uint32_t blend_op, fg_alpha, bg_alpha, ctl_blend_flags = 0; unsigned long flags; -#define blender(stage) ((stage) - STAGE_BASE) + uint8_t stage[STAGE_MAX + 1]; + int i, plane_cnt = 0; +#define blender(stage) ((stage) - STAGE0) hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); @@ -222,33 +222,73 @@ static void blend_setup(struct drm_crtc *crtc) if (!mdp5_crtc->ctl) goto out; + /* Collect all plane information */ drm_atomic_crtc_for_each_plane(plane, crtc) { - enum mdp_mixer_stage_id stage = - to_mdp5_plane_state(plane->state)->stage; + pstate = to_mdp5_plane_state(plane->state); + pstates[pstate->stage] = pstate; + stage[pstate->stage] = mdp5_plane_pipe(plane); + plane_cnt++; + } - /* - * Note: This cannot happen with current implementation but - * we need to check this condition once z property is added - */ - BUG_ON(stage > hw_cfg->lm.nb_stages); + /* + * If there is no base layer, enable border color. + * Although it's not possbile in current blend logic, + * put it here as a reminder. + */ + if (!pstates[STAGE_BASE] && plane_cnt) { + ctl_blend_flags |= MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT; + DBG("Border Color is enabled"); + } - /* LM */ - mdp5_write(mdp5_kms, - REG_MDP5_LM_BLEND_OP_MODE(lm, blender(stage)), - MDP5_LM_BLEND_OP_MODE_FG_ALPHA(FG_CONST) | - MDP5_LM_BLEND_OP_MODE_BG_ALPHA(BG_CONST)); + /* The reset for blending */ + for (i = STAGE0; i <= STAGE_MAX; i++) { + if (!pstates[i]) + continue; + + format = to_mdp_format( + msm_framebuffer_format(pstates[i]->base.fb)); + plane = pstates[i]->base.plane; + blend_op = MDP5_LM_BLEND_OP_MODE_FG_ALPHA(FG_CONST) | + MDP5_LM_BLEND_OP_MODE_BG_ALPHA(BG_CONST); + fg_alpha = pstates[i]->alpha; + bg_alpha = 0xFF - pstates[i]->alpha; + DBG("Stage %d fg_alpha %x bg_alpha %x", i, fg_alpha, bg_alpha); + + if (format->alpha_enable && pstates[i]->premultiplied) { + blend_op = MDP5_LM_BLEND_OP_MODE_FG_ALPHA(FG_CONST) | + MDP5_LM_BLEND_OP_MODE_BG_ALPHA(FG_PIXEL); + if (fg_alpha != 0xff) { + bg_alpha = fg_alpha; + blend_op |= + MDP5_LM_BLEND_OP_MODE_BG_MOD_ALPHA | + MDP5_LM_BLEND_OP_MODE_BG_INV_MOD_ALPHA; + } else { + blend_op |= MDP5_LM_BLEND_OP_MODE_BG_INV_ALPHA; + } + } else if (format->alpha_enable) { + blend_op = MDP5_LM_BLEND_OP_MODE_FG_ALPHA(FG_PIXEL) | + MDP5_LM_BLEND_OP_MODE_BG_ALPHA(FG_PIXEL); + if (fg_alpha != 0xff) { + bg_alpha = fg_alpha; + blend_op |= + MDP5_LM_BLEND_OP_MODE_FG_MOD_ALPHA | + MDP5_LM_BLEND_OP_MODE_FG_INV_MOD_ALPHA | + MDP5_LM_BLEND_OP_MODE_BG_MOD_ALPHA | + MDP5_LM_BLEND_OP_MODE_BG_INV_MOD_ALPHA; + } else { + blend_op |= MDP5_LM_BLEND_OP_MODE_BG_INV_ALPHA; + } + } + + mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_OP_MODE(lm, + blender(i)), blend_op); mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_FG_ALPHA(lm, - blender(stage)), 0xff); + blender(i)), fg_alpha); mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_BG_ALPHA(lm, - blender(stage)), 0x00); - /* CTL */ - blend_cfg |= mdp_ctl_blend_mask(mdp5_plane_pipe(plane), stage); - DBG("%s: blending pipe %s on stage=%d", mdp5_crtc->name, - pipe2name(mdp5_plane_pipe(plane)), stage); + blender(i)), bg_alpha); } - DBG("%s: lm%d: blend config = 0x%08x", mdp5_crtc->name, lm, blend_cfg); - mdp5_ctl_blend(mdp5_crtc->ctl, lm, blend_cfg); + mdp5_ctl_blend(mdp5_crtc->ctl, stage, plane_cnt, ctl_blend_flags); out: spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags); @@ -339,25 +379,19 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, struct mdp5_kms *mdp5_kms = get_kms(crtc); struct drm_plane *plane; struct drm_device *dev = crtc->dev; - struct plane_state pstates[STAGE3 + 1]; + struct plane_state pstates[STAGE_MAX + 1]; + const struct mdp5_cfg_hw *hw_cfg; int cnt = 0, i; DBG("%s: check", mdp5_crtc->name); - /* request a free CTL, if none is already allocated for this CRTC */ - if (state->enable && !mdp5_crtc->ctl) { - mdp5_crtc->ctl = mdp5_ctlm_request(mdp5_kms->ctlm, crtc); - if (WARN_ON(!mdp5_crtc->ctl)) - return -EINVAL; - } - /* verify that there are not too many planes attached to crtc * and that we don't have conflicting mixer stages: */ + hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); drm_atomic_crtc_state_for_each_plane(plane, state) { struct drm_plane_state *pstate; - - if (cnt >= ARRAY_SIZE(pstates)) { + if (cnt >= (hw_cfg->lm.nb_stages)) { dev_err(dev->dev, "too many planes!\n"); return -EINVAL; } @@ -369,13 +403,13 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, */ if (!pstate) pstate = plane->state; - pstates[cnt].plane = plane; pstates[cnt].state = to_mdp5_plane_state(pstate); cnt++; } + /* assign a stage based on sorted zpos property */ sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL); for (i = 0; i < cnt; i++) { @@ -388,13 +422,15 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, return 0; } -static void mdp5_crtc_atomic_begin(struct drm_crtc *crtc) +static void mdp5_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); DBG("%s: begin", mdp5_crtc->name); } -static void mdp5_crtc_atomic_flush(struct drm_crtc *crtc) +static void mdp5_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct drm_device *dev = crtc->dev; @@ -691,8 +727,8 @@ void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file) complete_flip(crtc, file); } -/* set interface for routing crtc->encoder: */ -void mdp5_crtc_set_intf(struct drm_crtc *crtc, struct mdp5_interface *intf) +void mdp5_crtc_set_pipeline(struct drm_crtc *crtc, + struct mdp5_interface *intf, struct mdp5_ctl *ctl) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct mdp5_kms *mdp5_kms = get_kms(crtc); @@ -715,7 +751,8 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, struct mdp5_interface *intf) mdp_irq_update(&mdp5_kms->base); - mdp5_ctl_set_intf(mdp5_crtc->ctl, intf); + mdp5_crtc->ctl = ctl; + mdp5_ctl_set_pipeline(ctl, intf, lm); } int mdp5_crtc_get_lm(struct drm_crtc *crtc) @@ -724,12 +761,6 @@ int mdp5_crtc_get_lm(struct drm_crtc *crtc) return WARN_ON(!crtc) ? -EINVAL : mdp5_crtc->lm; } -struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc) -{ - struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); - return WARN_ON(!crtc) ? NULL : mdp5_crtc->ctl; -} - void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); @@ -774,7 +805,5 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs); plane->crtc = crtc; - mdp5_plane_install_properties(plane, &crtc->base); - return crtc; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c index f2530f224a76..4e81ca4f964a 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c @@ -17,7 +17,7 @@ /* * CTL - MDP Control Pool Manager * - * Controls are shared between all CRTCs. + * Controls are shared between all display interfaces. * * They are intended to be used for data path configuration. * The top level register programming describes the complete data path for @@ -27,12 +27,11 @@ * * In certain use cases (high-resolution dual pipe), one single CTL can be * shared across multiple CRTCs. - * - * Because the number of CTLs can be less than the number of CRTCs, - * CTLs are dynamically allocated from a pool of CTLs, only once a CRTC is - * requested by the client (in mdp5_crtc_mode_set()). */ +#define CTL_STAT_BUSY 0x1 +#define CTL_STAT_BOOKED 0x2 + struct op_mode { struct mdp5_interface intf; @@ -46,8 +45,8 @@ struct mdp5_ctl { u32 id; int lm; - /* whether this CTL has been allocated or not: */ - bool busy; + /* CTL status bitmask */ + u32 status; /* Operation Mode Configuration for the Pipeline */ struct op_mode pipeline; @@ -61,7 +60,10 @@ struct mdp5_ctl { bool cursor_on; - struct drm_crtc *crtc; + /* True if the current CTL has FLUSH bits pending for single FLUSH. */ + bool flush_pending; + + struct mdp5_ctl *pair; /* Paired CTL to be flushed together */ }; struct mdp5_ctl_manager { @@ -74,6 +76,10 @@ struct mdp5_ctl_manager { /* to filter out non-present bits in the current hardware config */ u32 flush_hw_mask; + /* status for single FLUSH */ + bool single_flush_supported; + u32 single_flush_pending_mask; + /* pool of CTLs + lock to protect resource allocation (ctls[i].busy) */ spinlock_t pool_lock; struct mdp5_ctl ctls[MAX_CTL]; @@ -168,11 +174,21 @@ static void set_ctl_op(struct mdp5_ctl *ctl, struct mdp5_interface *intf) spin_unlock_irqrestore(&ctl->hw_lock, flags); } -int mdp5_ctl_set_intf(struct mdp5_ctl *ctl, struct mdp5_interface *intf) +int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, + struct mdp5_interface *intf, int lm) { struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr); + if (unlikely(WARN_ON(intf->num != ctl->pipeline.intf.num))) { + dev_err(mdp5_kms->dev->dev, + "CTL %d is allocated by INTF %d, but used by INTF %d\n", + ctl->id, ctl->pipeline.intf.num, intf->num); + return -EINVAL; + } + + ctl->lm = lm; + memcpy(&ctl->pipeline.intf, intf, sizeof(*intf)); ctl->pipeline.start_mask = mdp_ctl_flush_mask_lm(ctl->lm) | @@ -287,29 +303,85 @@ int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, int cursor_id, bool enable) blend_cfg &= ~MDP5_CTL_LAYER_REG_CURSOR_OUT; ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, lm), blend_cfg); + ctl->cursor_on = enable; spin_unlock_irqrestore(&ctl->hw_lock, flags); ctl->pending_ctl_trigger = mdp_ctl_flush_mask_cursor(cursor_id); - ctl->cursor_on = enable; return 0; } -int mdp5_ctl_blend(struct mdp5_ctl *ctl, u32 lm, u32 blend_cfg) +static u32 mdp_ctl_blend_mask(enum mdp5_pipe pipe, + enum mdp_mixer_stage_id stage) +{ + switch (pipe) { + case SSPP_VIG0: return MDP5_CTL_LAYER_REG_VIG0(stage); + case SSPP_VIG1: return MDP5_CTL_LAYER_REG_VIG1(stage); + case SSPP_VIG2: return MDP5_CTL_LAYER_REG_VIG2(stage); + case SSPP_RGB0: return MDP5_CTL_LAYER_REG_RGB0(stage); + case SSPP_RGB1: return MDP5_CTL_LAYER_REG_RGB1(stage); + case SSPP_RGB2: return MDP5_CTL_LAYER_REG_RGB2(stage); + case SSPP_DMA0: return MDP5_CTL_LAYER_REG_DMA0(stage); + case SSPP_DMA1: return MDP5_CTL_LAYER_REG_DMA1(stage); + case SSPP_VIG3: return MDP5_CTL_LAYER_REG_VIG3(stage); + case SSPP_RGB3: return MDP5_CTL_LAYER_REG_RGB3(stage); + default: return 0; + } +} + +static u32 mdp_ctl_blend_ext_mask(enum mdp5_pipe pipe, + enum mdp_mixer_stage_id stage) +{ + if (stage < STAGE6) + return 0; + + switch (pipe) { + case SSPP_VIG0: return MDP5_CTL_LAYER_EXT_REG_VIG0_BIT3; + case SSPP_VIG1: return MDP5_CTL_LAYER_EXT_REG_VIG1_BIT3; + case SSPP_VIG2: return MDP5_CTL_LAYER_EXT_REG_VIG2_BIT3; + case SSPP_RGB0: return MDP5_CTL_LAYER_EXT_REG_RGB0_BIT3; + case SSPP_RGB1: return MDP5_CTL_LAYER_EXT_REG_RGB1_BIT3; + case SSPP_RGB2: return MDP5_CTL_LAYER_EXT_REG_RGB2_BIT3; + case SSPP_DMA0: return MDP5_CTL_LAYER_EXT_REG_DMA0_BIT3; + case SSPP_DMA1: return MDP5_CTL_LAYER_EXT_REG_DMA1_BIT3; + case SSPP_VIG3: return MDP5_CTL_LAYER_EXT_REG_VIG3_BIT3; + case SSPP_RGB3: return MDP5_CTL_LAYER_EXT_REG_RGB3_BIT3; + default: return 0; + } +} + +int mdp5_ctl_blend(struct mdp5_ctl *ctl, u8 *stage, u32 stage_cnt, + u32 ctl_blend_op_flags) { unsigned long flags; + u32 blend_cfg = 0, blend_ext_cfg = 0; + int i, start_stage; + + if (ctl_blend_op_flags & MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT) { + start_stage = STAGE0; + blend_cfg |= MDP5_CTL_LAYER_REG_BORDER_COLOR; + } else { + start_stage = STAGE_BASE; + } + for (i = start_stage; i < start_stage + stage_cnt; i++) { + blend_cfg |= mdp_ctl_blend_mask(stage[i], i); + blend_ext_cfg |= mdp_ctl_blend_ext_mask(stage[i], i); + } + + spin_lock_irqsave(&ctl->hw_lock, flags); if (ctl->cursor_on) blend_cfg |= MDP5_CTL_LAYER_REG_CURSOR_OUT; - else - blend_cfg &= ~MDP5_CTL_LAYER_REG_CURSOR_OUT; - spin_lock_irqsave(&ctl->hw_lock, flags); - ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, lm), blend_cfg); + ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, ctl->lm), blend_cfg); + ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, ctl->lm), blend_ext_cfg); spin_unlock_irqrestore(&ctl->hw_lock, flags); - ctl->pending_ctl_trigger = mdp_ctl_flush_mask_lm(lm); + ctl->pending_ctl_trigger = mdp_ctl_flush_mask_lm(ctl->lm); + + DBG("lm%d: blend config = 0x%08x. ext_cfg = 0x%08x", ctl->lm, + blend_cfg, blend_ext_cfg); return 0; } @@ -379,6 +451,31 @@ static u32 fix_sw_flush(struct mdp5_ctl *ctl, u32 flush_mask) return sw_mask; } +static void fix_for_single_flush(struct mdp5_ctl *ctl, u32 *flush_mask, + u32 *flush_id) +{ + struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; + + if (ctl->pair) { + DBG("CTL %d FLUSH pending mask %x", ctl->id, *flush_mask); + ctl->flush_pending = true; + ctl_mgr->single_flush_pending_mask |= (*flush_mask); + *flush_mask = 0; + + if (ctl->pair->flush_pending) { + *flush_id = min_t(u32, ctl->id, ctl->pair->id); + *flush_mask = ctl_mgr->single_flush_pending_mask; + + ctl->flush_pending = false; + ctl->pair->flush_pending = false; + ctl_mgr->single_flush_pending_mask = 0; + + DBG("Single FLUSH mask %x,ID %d", *flush_mask, + *flush_id); + } + } +} + /** * mdp5_ctl_commit() - Register Flush * @@ -400,6 +497,8 @@ u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask) struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; struct op_mode *pipeline = &ctl->pipeline; unsigned long flags; + u32 flush_id = ctl->id; + u32 curr_ctl_flush_mask; pipeline->start_mask &= ~flush_mask; @@ -415,9 +514,13 @@ u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask) flush_mask &= ctl_mgr->flush_hw_mask; + curr_ctl_flush_mask = flush_mask; + + fix_for_single_flush(ctl, &flush_mask, &flush_id); + if (flush_mask) { spin_lock_irqsave(&ctl->hw_lock, flags); - ctl_write(ctl, REG_MDP5_CTL_FLUSH(ctl->id), flush_mask); + ctl_write(ctl, REG_MDP5_CTL_FLUSH(flush_id), flush_mask); spin_unlock_irqrestore(&ctl->hw_lock, flags); } @@ -426,7 +529,7 @@ u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask) refill_start_mask(ctl); } - return flush_mask; + return curr_ctl_flush_mask; } u32 mdp5_ctl_get_commit_status(struct mdp5_ctl *ctl) @@ -434,59 +537,85 @@ u32 mdp5_ctl_get_commit_status(struct mdp5_ctl *ctl) return ctl_read(ctl, REG_MDP5_CTL_FLUSH(ctl->id)); } -void mdp5_ctl_release(struct mdp5_ctl *ctl) +int mdp5_ctl_get_ctl_id(struct mdp5_ctl *ctl) { - struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; - unsigned long flags; + return WARN_ON(!ctl) ? -EINVAL : ctl->id; +} - if (unlikely(WARN_ON(ctl->id >= MAX_CTL) || !ctl->busy)) { - dev_err(ctl_mgr->dev->dev, "CTL %d in bad state (%d)", - ctl->id, ctl->busy); - return; +/* + * mdp5_ctl_pair() - Associate 2 booked CTLs for single FLUSH + */ +int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable) +{ + struct mdp5_ctl_manager *ctl_mgr = ctlx->ctlm; + struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr); + + /* do nothing silently if hw doesn't support */ + if (!ctl_mgr->single_flush_supported) + return 0; + + if (!enable) { + ctlx->pair = NULL; + ctly->pair = NULL; + mdp5_write(mdp5_kms, REG_MDP5_MDP_SPARE_0(0), 0); + return 0; + } else if ((ctlx->pair != NULL) || (ctly->pair != NULL)) { + dev_err(ctl_mgr->dev->dev, "CTLs already paired\n"); + return -EINVAL; + } else if (!(ctlx->status & ctly->status & CTL_STAT_BOOKED)) { + dev_err(ctl_mgr->dev->dev, "Only pair booked CTLs\n"); + return -EINVAL; } - spin_lock_irqsave(&ctl_mgr->pool_lock, flags); - ctl->busy = false; - spin_unlock_irqrestore(&ctl_mgr->pool_lock, flags); + ctlx->pair = ctly; + ctly->pair = ctlx; - DBG("CTL %d released", ctl->id); -} + mdp5_write(mdp5_kms, REG_MDP5_MDP_SPARE_0(0), + MDP5_MDP_SPARE_0_SPLIT_DPL_SINGLE_FLUSH_EN); -int mdp5_ctl_get_ctl_id(struct mdp5_ctl *ctl) -{ - return WARN_ON(!ctl) ? -EINVAL : ctl->id; + return 0; } /* - * mdp5_ctl_request() - CTL dynamic allocation + * mdp5_ctl_request() - CTL allocation * - * Note: Current implementation considers that we can only have one CRTC per CTL + * Try to return booked CTL for @intf_num is 1 or 2, unbooked for other INTFs. + * If no CTL is available in preferred category, allocate from the other one. * - * @return first free CTL + * @return fail if no CTL is available. */ struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctl_mgr, - struct drm_crtc *crtc) + int intf_num) { struct mdp5_ctl *ctl = NULL; + const u32 checkm = CTL_STAT_BUSY | CTL_STAT_BOOKED; + u32 match = ((intf_num == 1) || (intf_num == 2)) ? CTL_STAT_BOOKED : 0; unsigned long flags; int c; spin_lock_irqsave(&ctl_mgr->pool_lock, flags); + /* search the preferred */ for (c = 0; c < ctl_mgr->nctl; c++) - if (!ctl_mgr->ctls[c].busy) - break; + if ((ctl_mgr->ctls[c].status & checkm) == match) + goto found; - if (unlikely(c >= ctl_mgr->nctl)) { - dev_err(ctl_mgr->dev->dev, "No more CTL available!"); - goto unlock; - } + dev_warn(ctl_mgr->dev->dev, + "fall back to the other CTL category for INTF %d!\n", intf_num); - ctl = &ctl_mgr->ctls[c]; + match ^= CTL_STAT_BOOKED; + for (c = 0; c < ctl_mgr->nctl; c++) + if ((ctl_mgr->ctls[c].status & checkm) == match) + goto found; - ctl->lm = mdp5_crtc_get_lm(crtc); - ctl->crtc = crtc; - ctl->busy = true; + dev_err(ctl_mgr->dev->dev, "No more CTL available!"); + goto unlock; + +found: + ctl = &ctl_mgr->ctls[c]; + ctl->pipeline.intf.num = intf_num; + ctl->lm = -1; + ctl->status |= CTL_STAT_BUSY; ctl->pending_ctl_trigger = 0; DBG("CTL %d allocated", ctl->id); @@ -515,9 +644,11 @@ void mdp5_ctlm_destroy(struct mdp5_ctl_manager *ctl_mgr) } struct mdp5_ctl_manager *mdp5_ctlm_init(struct drm_device *dev, - void __iomem *mmio_base, const struct mdp5_cfg_hw *hw_cfg) + void __iomem *mmio_base, struct mdp5_cfg_handler *cfg_hnd) { struct mdp5_ctl_manager *ctl_mgr; + const struct mdp5_cfg_hw *hw_cfg = mdp5_cfg_get_hw_config(cfg_hnd); + int rev = mdp5_cfg_get_hw_rev(cfg_hnd); const struct mdp5_ctl_block *ctl_cfg = &hw_cfg->ctl; unsigned long flags; int c, ret; @@ -551,14 +682,28 @@ struct mdp5_ctl_manager *mdp5_ctlm_init(struct drm_device *dev, if (WARN_ON(!ctl_cfg->base[c])) { dev_err(dev->dev, "CTL_%d: base is null!\n", c); ret = -EINVAL; + spin_unlock_irqrestore(&ctl_mgr->pool_lock, flags); goto fail; } ctl->ctlm = ctl_mgr; ctl->id = c; ctl->reg_offset = ctl_cfg->base[c]; - ctl->busy = false; + ctl->status = 0; spin_lock_init(&ctl->hw_lock); } + + /* + * In Dual DSI case, CTL0 and CTL1 are always assigned to two DSI + * interfaces to support single FLUSH feature (Flush CTL0 and CTL1 when + * only write into CTL0's FLUSH register) to keep two DSI pipes in sync. + * Single FLUSH is supported from hw rev v3.0. + */ + if (rev >= 3) { + ctl_mgr->single_flush_supported = true; + /* Reserve CTL0/1 for INTF1/2 */ + ctl_mgr->ctls[0].status |= CTL_STAT_BOOKED; + ctl_mgr->ctls[1].status |= CTL_STAT_BOOKED; + } spin_unlock_irqrestore(&ctl_mgr->pool_lock, flags); DBG("Pool of %d CTLs created.", ctl_mgr->nctl); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h index 4678228c4f14..96148c6f863c 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h @@ -23,7 +23,7 @@ */ struct mdp5_ctl_manager; struct mdp5_ctl_manager *mdp5_ctlm_init(struct drm_device *dev, - void __iomem *mmio_base, const struct mdp5_cfg_hw *hw_cfg); + void __iomem *mmio_base, struct mdp5_cfg_handler *cfg_hnd); void mdp5_ctlm_hw_reset(struct mdp5_ctl_manager *ctlm); void mdp5_ctlm_destroy(struct mdp5_ctl_manager *ctlm); @@ -32,49 +32,32 @@ void mdp5_ctlm_destroy(struct mdp5_ctl_manager *ctlm); * mdp5_ctl_request(ctlm, ...) returns a ctl (CTL resource) handler, * which is then used to call the other mdp5_ctl_*(ctl, ...) functions. */ -struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctlm, struct drm_crtc *crtc); +struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctlm, int intf_num); + int mdp5_ctl_get_ctl_id(struct mdp5_ctl *ctl); struct mdp5_interface; -int mdp5_ctl_set_intf(struct mdp5_ctl *ctl, struct mdp5_interface *intf); +int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_interface *intf, + int lm); int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, bool enabled); int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, int cursor_id, bool enable); - -/* - * blend_cfg (LM blender config): - * - * The function below allows the caller of mdp5_ctl_blend() to specify how pipes - * are being blended according to their stage (z-order), through @blend_cfg arg. - */ -static inline u32 mdp_ctl_blend_mask(enum mdp5_pipe pipe, - enum mdp_mixer_stage_id stage) -{ - switch (pipe) { - case SSPP_VIG0: return MDP5_CTL_LAYER_REG_VIG0(stage); - case SSPP_VIG1: return MDP5_CTL_LAYER_REG_VIG1(stage); - case SSPP_VIG2: return MDP5_CTL_LAYER_REG_VIG2(stage); - case SSPP_RGB0: return MDP5_CTL_LAYER_REG_RGB0(stage); - case SSPP_RGB1: return MDP5_CTL_LAYER_REG_RGB1(stage); - case SSPP_RGB2: return MDP5_CTL_LAYER_REG_RGB2(stage); - case SSPP_DMA0: return MDP5_CTL_LAYER_REG_DMA0(stage); - case SSPP_DMA1: return MDP5_CTL_LAYER_REG_DMA1(stage); - case SSPP_VIG3: return MDP5_CTL_LAYER_REG_VIG3(stage); - case SSPP_RGB3: return MDP5_CTL_LAYER_REG_RGB3(stage); - default: return 0; - } -} +int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable); /* * mdp5_ctl_blend() - Blend multiple layers on a Layer Mixer (LM) * - * @blend_cfg: see LM blender config definition below + * @stage: array to contain the pipe num for each stage + * @stage_cnt: valid stage number in stage array + * @ctl_blend_op_flags: blender operation mode flags * * Note: * CTL registers need to be flushed after calling this function * (call mdp5_ctl_commit() with mdp_ctl_flush_mask_ctl() mask) */ -int mdp5_ctl_blend(struct mdp5_ctl *ctl, u32 lm, u32 blend_cfg); +#define MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT BIT(0) +int mdp5_ctl_blend(struct mdp5_ctl *ctl, u8 *stage, u32 stage_cnt, + u32 ctl_blend_op_flags); /** * mdp_ctl_flush_mask...() - Register FLUSH masks @@ -91,8 +74,6 @@ u32 mdp_ctl_flush_mask_encoder(struct mdp5_interface *intf); u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask); u32 mdp5_ctl_get_commit_status(struct mdp5_ctl *ctl); -void mdp5_ctl_release(struct mdp5_ctl *ctl); - #endif /* __MDP5_CTL_H__ */ diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c index de97c08f3f1f..c9e32b08a7a0 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c @@ -27,6 +27,8 @@ struct mdp5_encoder { spinlock_t intf_lock; /* protect REG_MDP5_INTF_* registers */ bool enabled; uint32_t bsc; + + struct mdp5_ctl *ctl; }; #define to_mdp5_encoder(x) container_of(x, struct mdp5_encoder, base) @@ -222,14 +224,15 @@ static void mdp5_encoder_mode_set(struct drm_encoder *encoder, spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); - mdp5_crtc_set_intf(encoder->crtc, &mdp5_encoder->intf); + mdp5_crtc_set_pipeline(encoder->crtc, &mdp5_encoder->intf, + mdp5_encoder->ctl); } static void mdp5_encoder_disable(struct drm_encoder *encoder) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); - struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc); + struct mdp5_ctl *ctl = mdp5_encoder->ctl; int lm = mdp5_crtc_get_lm(encoder->crtc); struct mdp5_interface *intf = &mdp5_encoder->intf; int intfn = mdp5_encoder->intf.num; @@ -264,7 +267,7 @@ static void mdp5_encoder_enable(struct drm_encoder *encoder) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); - struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc); + struct mdp5_ctl *ctl = mdp5_encoder->ctl; struct mdp5_interface *intf = &mdp5_encoder->intf; int intfn = mdp5_encoder->intf.num; unsigned long flags; @@ -294,6 +297,7 @@ int mdp5_encoder_set_split_display(struct drm_encoder *encoder, struct drm_encoder *slave_encoder) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); + struct mdp5_encoder *mdp5_slave_enc = to_mdp5_encoder(slave_encoder); struct mdp5_kms *mdp5_kms; int intf_num; u32 data = 0; @@ -316,12 +320,13 @@ int mdp5_encoder_set_split_display(struct drm_encoder *encoder, /* Make sure clocks are on when connectors calling this function. */ mdp5_enable(mdp5_kms); - mdp5_write(mdp5_kms, REG_MDP5_MDP_SPARE_0(0), - MDP5_MDP_SPARE_0_SPLIT_DPL_SINGLE_FLUSH_EN); /* Dumb Panel, Sync mode */ mdp5_write(mdp5_kms, REG_MDP5_MDP_SPLIT_DPL_UPPER(0), 0); mdp5_write(mdp5_kms, REG_MDP5_MDP_SPLIT_DPL_LOWER(0), data); mdp5_write(mdp5_kms, REG_MDP5_MDP_SPLIT_DPL_EN(0), 1); + + mdp5_ctl_pair(mdp5_encoder->ctl, mdp5_slave_enc->ctl, true); + mdp5_disable(mdp5_kms); return 0; @@ -329,7 +334,7 @@ int mdp5_encoder_set_split_display(struct drm_encoder *encoder, /* initialize encoder */ struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, - struct mdp5_interface *intf) + struct mdp5_interface *intf, struct mdp5_ctl *ctl) { struct drm_encoder *encoder = NULL; struct mdp5_encoder *mdp5_encoder; @@ -345,6 +350,7 @@ struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, memcpy(&mdp5_encoder->intf, intf, sizeof(mdp5_encoder->intf)); encoder = &mdp5_encoder->base; + mdp5_encoder->ctl = ctl; spin_lock_init(&mdp5_encoder->intf_lock); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c index 33bd4c6160dd..b1f73bee1368 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c @@ -21,8 +21,11 @@ #include "msm_drv.h" #include "mdp5_kms.h" -void mdp5_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask) +void mdp5_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask, + uint32_t old_irqmask) { + mdp5_write(to_mdp5_kms(mdp_kms), REG_MDP5_MDP_INTR_CLEAR(0), + irqmask ^ (irqmask & old_irqmask)); mdp5_write(to_mdp5_kms(mdp_kms), REG_MDP5_MDP_INTR_EN(0), irqmask); } @@ -71,9 +74,10 @@ static void mdp5_irq_mdp(struct mdp_kms *mdp_kms) struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; unsigned int id; - uint32_t status; + uint32_t status, enable; - status = mdp5_read(mdp5_kms, REG_MDP5_MDP_INTR_STATUS(0)); + enable = mdp5_read(mdp5_kms, REG_MDP5_MDP_INTR_EN(0)); + status = mdp5_read(mdp5_kms, REG_MDP5_MDP_INTR_STATUS(0)) & enable; mdp5_write(mdp5_kms, REG_MDP5_MDP_INTR_CLEAR(0), status); VERB("status=%08x", status); @@ -112,15 +116,24 @@ irqreturn_t mdp5_irq(struct msm_kms *kms) int mdp5_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) { + struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); + + mdp5_enable(mdp5_kms); mdp_update_vblank_mask(to_mdp_kms(kms), mdp5_crtc_vblank(crtc), true); + mdp5_disable(mdp5_kms); + return 0; } void mdp5_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) { + struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); + + mdp5_enable(mdp5_kms); mdp_update_vblank_mask(to_mdp_kms(kms), mdp5_crtc_vblank(crtc), false); + mdp5_disable(mdp5_kms); } /* diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index e253db5de5aa..047cb0433ccb 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -177,7 +177,8 @@ int mdp5_disable(struct mdp5_kms *mdp5_kms) 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); + if (mdp5_kms->lut_clk) + clk_disable_unprepare(mdp5_kms->lut_clk); return 0; } @@ -189,14 +190,15 @@ int mdp5_enable(struct mdp5_kms *mdp5_kms) 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); + if (mdp5_kms->lut_clk) + clk_prepare_enable(mdp5_kms->lut_clk); return 0; } static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms, enum mdp5_intf_type intf_type, int intf_num, - enum mdp5_intf_mode intf_mode) + enum mdp5_intf_mode intf_mode, struct mdp5_ctl *ctl) { struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; @@ -209,9 +211,9 @@ static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms, if ((intf_type == INTF_DSI) && (intf_mode == MDP5_INTF_DSI_MODE_COMMAND)) - encoder = mdp5_cmd_encoder_init(dev, &intf); + encoder = mdp5_cmd_encoder_init(dev, &intf, ctl); else - encoder = mdp5_encoder_init(dev, &intf); + encoder = mdp5_encoder_init(dev, &intf, ctl); if (IS_ERR(encoder)) { dev_err(dev->dev, "failed to construct encoder\n"); @@ -249,6 +251,8 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) const struct mdp5_cfg_hw *hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); enum mdp5_intf_type intf_type = hw_cfg->intf.connect[intf_num]; + struct mdp5_ctl_manager *ctlm = mdp5_kms->ctlm; + struct mdp5_ctl *ctl; struct drm_encoder *encoder; int ret = 0; @@ -259,8 +263,14 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) if (!priv->edp) break; + ctl = mdp5_ctlm_request(ctlm, intf_num); + if (!ctl) { + ret = -EINVAL; + break; + } + encoder = construct_encoder(mdp5_kms, INTF_eDP, intf_num, - MDP5_INTF_MODE_NONE); + MDP5_INTF_MODE_NONE, ctl); if (IS_ERR(encoder)) { ret = PTR_ERR(encoder); break; @@ -272,8 +282,14 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) if (!priv->hdmi) break; + ctl = mdp5_ctlm_request(ctlm, intf_num); + if (!ctl) { + ret = -EINVAL; + break; + } + encoder = construct_encoder(mdp5_kms, INTF_HDMI, intf_num, - MDP5_INTF_MODE_NONE); + MDP5_INTF_MODE_NONE, ctl); if (IS_ERR(encoder)) { ret = PTR_ERR(encoder); break; @@ -298,14 +314,20 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) if (!priv->dsi[dsi_id]) break; + ctl = mdp5_ctlm_request(ctlm, intf_num); + if (!ctl) { + ret = -EINVAL; + break; + } + for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) { mode = (i == MSM_DSI_CMD_ENCODER_ID) ? MDP5_INTF_DSI_MODE_COMMAND : MDP5_INTF_DSI_MODE_VIDEO; dsi_encs[i] = construct_encoder(mdp5_kms, INTF_DSI, - intf_num, mode); - if (IS_ERR(dsi_encs)) { - ret = PTR_ERR(dsi_encs); + intf_num, mode, ctl); + if (IS_ERR(dsi_encs[i])) { + ret = PTR_ERR(dsi_encs[i]); break; } } @@ -327,9 +349,12 @@ static int modeset_init(struct mdp5_kms *mdp5_kms) static const enum mdp5_pipe crtcs[] = { SSPP_RGB0, SSPP_RGB1, SSPP_RGB2, SSPP_RGB3, }; - static const enum mdp5_pipe pub_planes[] = { + static const enum mdp5_pipe vig_planes[] = { SSPP_VIG0, SSPP_VIG1, SSPP_VIG2, SSPP_VIG3, }; + static const enum mdp5_pipe dma_planes[] = { + SSPP_DMA0, SSPP_DMA1, + }; struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; const struct mdp5_cfg_hw *hw_cfg; @@ -350,7 +375,7 @@ static int modeset_init(struct mdp5_kms *mdp5_kms) struct drm_crtc *crtc; plane = mdp5_plane_init(dev, crtcs[i], true, - hw_cfg->pipe_rgb.base[i]); + hw_cfg->pipe_rgb.base[i], hw_cfg->pipe_rgb.caps); if (IS_ERR(plane)) { ret = PTR_ERR(plane); dev_err(dev->dev, "failed to construct plane for %s (%d)\n", @@ -368,16 +393,30 @@ static int modeset_init(struct mdp5_kms *mdp5_kms) priv->crtcs[priv->num_crtcs++] = crtc; } - /* Construct public planes: */ + /* Construct video planes: */ for (i = 0; i < hw_cfg->pipe_vig.count; i++) { struct drm_plane *plane; - plane = mdp5_plane_init(dev, pub_planes[i], false, - hw_cfg->pipe_vig.base[i]); + plane = mdp5_plane_init(dev, vig_planes[i], false, + hw_cfg->pipe_vig.base[i], hw_cfg->pipe_vig.caps); + if (IS_ERR(plane)) { + ret = PTR_ERR(plane); + dev_err(dev->dev, "failed to construct %s plane: %d\n", + pipe2name(vig_planes[i]), ret); + goto fail; + } + } + + /* DMA planes */ + for (i = 0; i < hw_cfg->pipe_dma.count; i++) { + struct drm_plane *plane; + + plane = mdp5_plane_init(dev, dma_planes[i], false, + hw_cfg->pipe_dma.base[i], hw_cfg->pipe_dma.caps); if (IS_ERR(plane)) { ret = PTR_ERR(plane); dev_err(dev->dev, "failed to construct %s plane: %d\n", - pipe2name(pub_planes[i]), ret); + pipe2name(dma_planes[i]), ret); goto fail; } } @@ -489,7 +528,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev) goto fail; ret = get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk"); if (ret) - goto fail; + DBG("failed to get (optional) lut_clk clock"); ret = get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk"); if (ret) goto fail; @@ -521,7 +560,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev) goto fail; } - mdp5_kms->ctlm = mdp5_ctlm_init(dev, mdp5_kms->mmio, config->hw); + mdp5_kms->ctlm = mdp5_ctlm_init(dev, mdp5_kms->mmio, mdp5_kms->cfg); if (IS_ERR(mdp5_kms->ctlm)) { ret = PTR_ERR(mdp5_kms->ctlm); mdp5_kms->ctlm = NULL; @@ -577,6 +616,11 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev) goto fail; } + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.max_width = config->hw->lm.max_width; + dev->mode_config.max_height = config->hw->lm.max_height; + return kms; fail: diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index e79ac09b7216..0bb62423586e 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -70,18 +70,12 @@ struct mdp5_kms { struct mdp5_plane_state { struct drm_plane_state base; - /* "virtual" zpos.. we calculate actual mixer-stage at runtime - * by sorting the attached planes by zpos and then assigning - * mixer stage lowest to highest. Private planes get default - * zpos of zero, and public planes a unique value that is - * greater than zero. This way, things work out if a naive - * userspace assigns planes to a crtc without setting zpos. - */ - int zpos; + /* aligned with property */ + uint8_t premultiplied; + uint8_t zpos; + uint8_t alpha; - /* the actual mixer stage, calculated in crtc->atomic_check() - * NOTE: this should move to mdp5_crtc_state, when that exists - */ + /* assigned by crtc blender */ enum mdp_mixer_stage_id stage; /* some additional transactional status to help us know in the @@ -192,7 +186,8 @@ static inline uint32_t lm2ppdone(int lm) 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_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask, + uint32_t old_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); @@ -202,60 +197,38 @@ void mdp5_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc); int mdp5_irq_domain_init(struct mdp5_kms *mdp5_kms); void mdp5_irq_domain_fini(struct mdp5_kms *mdp5_kms); -static inline bool pipe_supports_yuv(enum mdp5_pipe pipe) -{ - switch (pipe) { - case SSPP_VIG0: - case SSPP_VIG1: - case SSPP_VIG2: - case SSPP_VIG3: - return true; - default: - return false; - } -} - -static inline -uint32_t mdp5_get_formats(enum mdp5_pipe pipe, uint32_t *pixel_formats, - uint32_t max_formats) -{ - return mdp_get_formats(pixel_formats, max_formats, - !pipe_supports_yuv(pipe)); -} - -void mdp5_plane_install_properties(struct drm_plane *plane, - struct drm_mode_object *obj); uint32_t mdp5_plane_get_flush(struct drm_plane *plane); void mdp5_plane_complete_flip(struct drm_plane *plane); void mdp5_plane_complete_commit(struct drm_plane *plane, struct drm_plane_state *state); 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 reg_offset); + enum mdp5_pipe pipe, bool private_plane, + uint32_t reg_offset, uint32_t caps); uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc); int mdp5_crtc_get_lm(struct drm_crtc *crtc); -struct mdp5_ctl *mdp5_crtc_get_ctl(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, struct mdp5_interface *intf); +void mdp5_crtc_set_pipeline(struct drm_crtc *crtc, + struct mdp5_interface *intf, struct mdp5_ctl *ctl); void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc); 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, - struct mdp5_interface *intf); + struct mdp5_interface *intf, struct mdp5_ctl *ctl); int mdp5_encoder_set_split_display(struct drm_encoder *encoder, struct drm_encoder *slave_encoder); #ifdef CONFIG_DRM_MSM_DSI struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev, - struct mdp5_interface *intf); + struct mdp5_interface *intf, struct mdp5_ctl *ctl); int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder, struct drm_encoder *slave_encoder); #else -static inline struct drm_encoder *mdp5_cmd_encoder_init( - struct drm_device *dev, struct mdp5_interface *intf) +static inline struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev, + struct mdp5_interface *intf, struct mdp5_ctl *ctl) { return ERR_PTR(-EINVAL); } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 22275568ab8b..07fb62fea6dc 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 The Linux Foundation. All rights reserved. + * Copyright (C) 2014-2015 The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> * @@ -26,6 +26,7 @@ struct mdp5_plane { spinlock_t pipe_lock; /* protect REG_MDP5_PIPE_* registers */ uint32_t reg_offset; + uint32_t caps; uint32_t flush_mask; /* used to commit pipe registers */ @@ -40,6 +41,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, unsigned int crtc_w, unsigned int crtc_h, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h); + static void set_scanout_locked(struct drm_plane *plane, struct drm_framebuffer *fb); @@ -64,18 +66,122 @@ static void mdp5_plane_destroy(struct drm_plane *plane) kfree(mdp5_plane); } +static void mdp5_plane_install_rotation_property(struct drm_device *dev, + struct drm_plane *plane) +{ + struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); + + if (!(mdp5_plane->caps & MDP_PIPE_CAP_HFLIP) && + !(mdp5_plane->caps & MDP_PIPE_CAP_VFLIP)) + return; + + if (!dev->mode_config.rotation_property) + dev->mode_config.rotation_property = + drm_mode_create_rotation_property(dev, + BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y)); + + if (dev->mode_config.rotation_property) + drm_object_attach_property(&plane->base, + dev->mode_config.rotation_property, + 0); +} + /* helper to install properties which are common to planes and crtcs */ -void mdp5_plane_install_properties(struct drm_plane *plane, +static void mdp5_plane_install_properties(struct drm_plane *plane, struct drm_mode_object *obj) { - // XXX + struct drm_device *dev = plane->dev; + struct msm_drm_private *dev_priv = dev->dev_private; + struct drm_property *prop; + +#define INSTALL_PROPERTY(name, NAME, init_val, fnc, ...) do { \ + prop = dev_priv->plane_property[PLANE_PROP_##NAME]; \ + if (!prop) { \ + prop = drm_property_##fnc(dev, 0, #name, \ + ##__VA_ARGS__); \ + if (!prop) { \ + dev_warn(dev->dev, \ + "Create property %s failed\n", \ + #name); \ + return; \ + } \ + dev_priv->plane_property[PLANE_PROP_##NAME] = prop; \ + } \ + drm_object_attach_property(&plane->base, prop, init_val); \ + } while (0) + +#define INSTALL_RANGE_PROPERTY(name, NAME, min, max, init_val) \ + INSTALL_PROPERTY(name, NAME, init_val, \ + create_range, min, max) + +#define INSTALL_ENUM_PROPERTY(name, NAME, init_val) \ + INSTALL_PROPERTY(name, NAME, init_val, \ + create_enum, name##_prop_enum_list, \ + ARRAY_SIZE(name##_prop_enum_list)) + + INSTALL_RANGE_PROPERTY(zpos, ZPOS, 1, 255, 1); + + mdp5_plane_install_rotation_property(dev, plane); + +#undef INSTALL_RANGE_PROPERTY +#undef INSTALL_ENUM_PROPERTY +#undef INSTALL_PROPERTY +} + +static int mdp5_plane_atomic_set_property(struct drm_plane *plane, + struct drm_plane_state *state, struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = plane->dev; + struct mdp5_plane_state *pstate; + struct msm_drm_private *dev_priv = dev->dev_private; + int ret = 0; + + pstate = to_mdp5_plane_state(state); + +#define SET_PROPERTY(name, NAME, type) do { \ + if (dev_priv->plane_property[PLANE_PROP_##NAME] == property) { \ + pstate->name = (type)val; \ + DBG("Set property %s %d", #name, (type)val); \ + goto done; \ + } \ + } while (0) + + SET_PROPERTY(zpos, ZPOS, uint8_t); + + dev_err(dev->dev, "Invalid property\n"); + ret = -EINVAL; +done: + return ret; +#undef SET_PROPERTY } -int mdp5_plane_set_property(struct drm_plane *plane, - struct drm_property *property, uint64_t val) +static int mdp5_plane_atomic_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, uint64_t *val) { - // XXX - return -EINVAL; + struct drm_device *dev = plane->dev; + struct mdp5_plane_state *pstate; + struct msm_drm_private *dev_priv = dev->dev_private; + int ret = 0; + + pstate = to_mdp5_plane_state(state); + +#define GET_PROPERTY(name, NAME, type) do { \ + if (dev_priv->plane_property[PLANE_PROP_##NAME] == property) { \ + *val = pstate->name; \ + DBG("Get property %s %lld", #name, *val); \ + goto done; \ + } \ + } while (0) + + GET_PROPERTY(zpos, ZPOS, uint8_t); + + dev_err(dev->dev, "Invalid property\n"); + ret = -EINVAL; +done: + return ret; +#undef SET_PROPERTY } static void mdp5_plane_reset(struct drm_plane *plane) @@ -88,11 +194,15 @@ static void mdp5_plane_reset(struct drm_plane *plane) kfree(to_mdp5_plane_state(plane->state)); mdp5_state = kzalloc(sizeof(*mdp5_state), GFP_KERNEL); - if (plane->type == DRM_PLANE_TYPE_PRIMARY) { - mdp5_state->zpos = 0; - } else { - mdp5_state->zpos = 1 + drm_plane_index(plane); - } + /* assign default blend parameters */ + mdp5_state->alpha = 255; + mdp5_state->premultiplied = 0; + + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + mdp5_state->zpos = STAGE_BASE; + else + mdp5_state->zpos = STAGE0 + drm_plane_index(plane); + mdp5_state->base.plane = plane; plane->state = &mdp5_state->base; @@ -131,7 +241,9 @@ static const struct drm_plane_funcs mdp5_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = mdp5_plane_destroy, - .set_property = mdp5_plane_set_property, + .set_property = drm_atomic_helper_plane_set_property, + .atomic_set_property = mdp5_plane_atomic_set_property, + .atomic_get_property = mdp5_plane_atomic_get_property, .reset = mdp5_plane_reset, .atomic_duplicate_state = mdp5_plane_duplicate_state, .atomic_destroy_state = mdp5_plane_destroy_state, @@ -164,10 +276,44 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane, { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct drm_plane_state *old_state = plane->state; + const struct mdp_format *format; + bool vflip, hflip; DBG("%s: check (%d -> %d)", mdp5_plane->name, plane_enabled(old_state), plane_enabled(state)); + if (plane_enabled(state)) { + format = to_mdp_format(msm_framebuffer_format(state->fb)); + if (MDP_FORMAT_IS_YUV(format) && + !pipe_supports_yuv(mdp5_plane->caps)) { + dev_err(plane->dev->dev, + "Pipe doesn't support YUV\n"); + + return -EINVAL; + } + + if (!(mdp5_plane->caps & MDP_PIPE_CAP_SCALE) && + (((state->src_w >> 16) != state->crtc_w) || + ((state->src_h >> 16) != state->crtc_h))) { + dev_err(plane->dev->dev, + "Pipe doesn't support scaling (%dx%d -> %dx%d)\n", + state->src_w >> 16, state->src_h >> 16, + state->crtc_w, state->crtc_h); + + return -EINVAL; + } + + hflip = !!(state->rotation & BIT(DRM_REFLECT_X)); + vflip = !!(state->rotation & BIT(DRM_REFLECT_Y)); + if ((vflip && !(mdp5_plane->caps & MDP_PIPE_CAP_VFLIP)) || + (hflip && !(mdp5_plane->caps & MDP_PIPE_CAP_HFLIP))) { + dev_err(plane->dev->dev, + "Pipe doesn't support flip\n"); + + return -EINVAL; + } + } + if (plane_enabled(state) && plane_enabled(old_state)) { /* we cannot change SMP block configuration during scanout: */ bool full_modeset = false; @@ -346,16 +492,21 @@ static int calc_phase_step(uint32_t src, uint32_t dst, uint32_t *out_phase) return 0; } -static int calc_scalex_steps(uint32_t pixel_format, uint32_t src, uint32_t dest, +static int calc_scalex_steps(struct drm_plane *plane, + uint32_t pixel_format, uint32_t src, uint32_t dest, uint32_t phasex_steps[2]) { + struct mdp5_kms *mdp5_kms = get_kms(plane); + struct device *dev = mdp5_kms->dev->dev; uint32_t phasex_step; unsigned int hsub; int ret; ret = calc_phase_step(src, dest, &phasex_step); - if (ret) + if (ret) { + dev_err(dev, "X scaling (%d->%d) failed: %d\n", src, dest, ret); return ret; + } hsub = drm_format_horz_chroma_subsampling(pixel_format); @@ -365,16 +516,21 @@ static int calc_scalex_steps(uint32_t pixel_format, uint32_t src, uint32_t dest, return 0; } -static int calc_scaley_steps(uint32_t pixel_format, uint32_t src, uint32_t dest, +static int calc_scaley_steps(struct drm_plane *plane, + uint32_t pixel_format, uint32_t src, uint32_t dest, uint32_t phasey_steps[2]) { + struct mdp5_kms *mdp5_kms = get_kms(plane); + struct device *dev = mdp5_kms->dev->dev; uint32_t phasey_step; unsigned int vsub; int ret; ret = calc_phase_step(src, dest, &phasey_step); - if (ret) + if (ret) { + dev_err(dev, "Y scaling (%d->%d) failed: %d\n", src, dest, ret); return ret; + } vsub = drm_format_vert_chroma_subsampling(pixel_format); @@ -384,28 +540,38 @@ static int calc_scaley_steps(uint32_t pixel_format, uint32_t src, uint32_t dest, return 0; } -static uint32_t get_scalex_config(uint32_t src, uint32_t dest) -{ - uint32_t filter; - - filter = (src <= dest) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN; - - return MDP5_PIPE_SCALE_CONFIG_SCALEX_EN | - MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER(filter) | - MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER(filter) | - MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER(filter); -} - -static uint32_t get_scaley_config(uint32_t src, uint32_t dest) +static uint32_t get_scale_config(enum mdp_chroma_samp_type chroma_sample, + uint32_t src, uint32_t dest, bool hor) { - uint32_t filter; - - filter = (src <= dest) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN; + uint32_t y_filter = (src <= dest) ? SCALE_FILTER_CA : SCALE_FILTER_PCMN; + uint32_t y_a_filter = (src <= dest) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN; + uint32_t uv_filter = ((src / 2) <= dest) ? /* 2x upsample */ + SCALE_FILTER_BIL : SCALE_FILTER_PCMN; + uint32_t value = 0; + + if (chroma_sample == CHROMA_420 || chroma_sample == CHROMA_H2V1) { + if (hor) + value = MDP5_PIPE_SCALE_CONFIG_SCALEX_EN | + MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_0(y_filter) | + MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_3(y_a_filter) | + MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_1_2(uv_filter); + else + value = MDP5_PIPE_SCALE_CONFIG_SCALEY_EN | + MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_0(y_filter) | + MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_3(y_a_filter) | + MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_1_2(uv_filter); + } else if (src != dest) { + if (hor) + value = MDP5_PIPE_SCALE_CONFIG_SCALEX_EN | + MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_0(y_a_filter) | + MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_3(y_a_filter); + else + value = MDP5_PIPE_SCALE_CONFIG_SCALEY_EN | + MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_0(y_a_filter) | + MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_3(y_a_filter); + } - return MDP5_PIPE_SCALE_CONFIG_SCALEY_EN | - MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER(filter) | - MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER(filter) | - MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER(filter); + return value; } static int mdp5_plane_mode_set(struct drm_plane *plane, @@ -416,8 +582,8 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, uint32_t src_w, uint32_t src_h) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); + struct drm_plane_state *pstate = plane->state; struct mdp5_kms *mdp5_kms = get_kms(plane); - struct device *dev = mdp5_kms->dev->dev; enum mdp5_pipe pipe = mdp5_plane->pipe; const struct mdp_format *format; uint32_t nplanes, config = 0; @@ -425,6 +591,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, uint32_t phasex_step[2] = {0,}, phasey_step[2] = {0,}; uint32_t hdecm = 0, vdecm = 0; uint32_t pix_format; + bool vflip, hflip; unsigned long flags; int ret; @@ -449,7 +616,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, /* Request some memory from the SMP: */ ret = mdp5_smp_request(mdp5_kms->smp, - mdp5_plane->pipe, fb->pixel_format, src_w); + mdp5_plane->pipe, format, src_w, false); if (ret) return ret; @@ -461,29 +628,23 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, */ mdp5_smp_configure(mdp5_kms->smp, pipe); - /* SCALE is used to both scale and up-sample chroma components */ + ret = calc_scalex_steps(plane, pix_format, src_w, crtc_w, phasex_step); + if (ret) + return ret; - if ((src_w != crtc_w) || MDP_FORMAT_IS_YUV(format)) { - /* TODO calc hdecm */ - ret = calc_scalex_steps(pix_format, src_w, crtc_w, phasex_step); - if (ret) { - dev_err(dev, "X scaling (%d -> %d) failed: %d\n", - src_w, crtc_w, ret); - return ret; - } - config |= get_scalex_config(src_w, crtc_w); - } + ret = calc_scaley_steps(plane, pix_format, src_h, crtc_h, phasey_step); + if (ret) + return ret; - if ((src_h != crtc_h) || MDP_FORMAT_IS_YUV(format)) { - /* TODO calc vdecm */ - ret = calc_scaley_steps(pix_format, src_h, crtc_h, phasey_step); - if (ret) { - dev_err(dev, "Y scaling (%d -> %d) failed: %d\n", - src_h, crtc_h, ret); - return ret; - } - config |= get_scaley_config(src_h, crtc_h); - } + /* TODO calc hdecm, vdecm */ + + /* SCALE is used to both scale and up-sample chroma components */ + config |= get_scale_config(format->chroma_sample, src_w, crtc_w, true); + config |= get_scale_config(format->chroma_sample, src_h, crtc_h, false); + DBG("scale config = %x", config); + + hflip = !!(pstate->rotation & BIT(DRM_REFLECT_X)); + vflip = !!(pstate->rotation & BIT(DRM_REFLECT_Y)); spin_lock_irqsave(&mdp5_plane->pipe_lock, flags); @@ -516,7 +677,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, 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(format->fetch_type) | + MDP5_PIPE_SRC_FORMAT_FETCH_TYPE(format->fetch_type) | MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(format->chroma_sample)); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe), @@ -526,29 +687,35 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, MDP5_PIPE_SRC_UNPACK_ELEM3(format->unpack[3])); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe), + (hflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_LR : 0) | + (vflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_UD : 0) | 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[0]); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe), - phasey_step[0]); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_X(pipe), - phasex_step[1]); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_Y(pipe), - phasey_step[1]); - 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), config); - - if (MDP_FORMAT_IS_YUV(format)) - csc_enable(mdp5_kms, pipe, - mdp_get_default_csc_cfg(CSC_YUV2RGB)); - else - csc_disable(mdp5_kms, pipe); + if (mdp5_plane->caps & MDP_PIPE_CAP_SCALE) { + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe), + phasex_step[0]); + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe), + phasey_step[0]); + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_X(pipe), + phasex_step[1]); + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_Y(pipe), + phasey_step[1]); + 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), config); + } + + if (mdp5_plane->caps & MDP_PIPE_CAP_CSC) { + if (MDP_FORMAT_IS_YUV(format)) + csc_enable(mdp5_kms, pipe, + mdp_get_default_csc_cfg(CSC_YUV2RGB)); + else + csc_disable(mdp5_kms, pipe); + } set_scanout_locked(plane, fb); @@ -599,7 +766,8 @@ void mdp5_plane_complete_commit(struct drm_plane *plane, /* initialize plane */ struct drm_plane *mdp5_plane_init(struct drm_device *dev, - enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset) + enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset, + uint32_t caps) { struct drm_plane *plane = NULL; struct mdp5_plane *mdp5_plane; @@ -616,9 +784,11 @@ struct drm_plane *mdp5_plane_init(struct drm_device *dev, mdp5_plane->pipe = pipe; mdp5_plane->name = pipe2name(pipe); + mdp5_plane->caps = caps; - mdp5_plane->nformats = mdp5_get_formats(pipe, mdp5_plane->formats, - ARRAY_SIZE(mdp5_plane->formats)); + mdp5_plane->nformats = mdp_get_formats(mdp5_plane->formats, + ARRAY_SIZE(mdp5_plane->formats), + !pipe_supports_yuv(mdp5_plane->caps)); mdp5_plane->flush_mask = mdp_ctl_flush_mask_pipe(pipe); mdp5_plane->reg_offset = reg_offset; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c index 64a27d86f2f5..563cca972dcb 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c @@ -90,6 +90,8 @@ struct mdp5_smp { struct drm_device *dev; + const struct mdp5_smp_block *cfg; + int blk_cnt; int blk_size; @@ -137,14 +139,12 @@ static int smp_request_block(struct mdp5_smp *smp, u32 cid, int nblks) { struct mdp5_kms *mdp5_kms = get_kms(smp); - const struct mdp5_cfg_hw *hw_cfg; struct mdp5_client_smp_state *ps = &smp->client_state[cid]; int i, ret, avail, cur_nblks, cnt = smp->blk_cnt; int reserved; unsigned long flags; - hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); - reserved = hw_cfg->smp.reserved[cid]; + reserved = smp->cfg->reserved[cid]; spin_lock_irqsave(&smp->state_lock, flags); @@ -209,12 +209,14 @@ static void set_fifo_thresholds(struct mdp5_smp *smp, * decimated width. Ie. SMP buffering sits downstream of decimation (which * presumably happens during the dma from scanout buffer). */ -int mdp5_smp_request(struct mdp5_smp *smp, enum mdp5_pipe pipe, u32 fmt, u32 width) +int mdp5_smp_request(struct mdp5_smp *smp, enum mdp5_pipe pipe, + const struct mdp_format *format, u32 width, bool hdecim) { struct mdp5_kms *mdp5_kms = get_kms(smp); struct drm_device *dev = mdp5_kms->dev; int rev = mdp5_cfg_get_hw_rev(mdp5_kms->cfg); int i, hsub, nplanes, nlines, nblks, ret; + u32 fmt = format->base.pixel_format; nplanes = drm_format_num_planes(fmt); hsub = drm_format_horz_chroma_subsampling(fmt); @@ -222,6 +224,21 @@ int mdp5_smp_request(struct mdp5_smp *smp, enum mdp5_pipe pipe, u32 fmt, u32 wid /* different if BWC (compressed framebuffer?) enabled: */ nlines = 2; + /* Newer MDPs have split/packing logic, which fetches sub-sampled + * U and V components (splits them from Y if necessary) and packs + * them together, writes to SMP using a single client. + */ + if ((rev > 0) && (format->chroma_sample > CHROMA_FULL)) { + fmt = DRM_FORMAT_NV24; + nplanes = 2; + + /* if decimation is enabled, HW decimates less on the + * sub sampled chroma components + */ + if (hdecim && (hsub > 1)) + hsub = 1; + } + for (i = 0, nblks = 0; i < nplanes; i++) { int n, fetch_stride, cpp; @@ -388,6 +405,7 @@ struct mdp5_smp *mdp5_smp_init(struct drm_device *dev, const struct mdp5_smp_blo } smp->dev = dev; + smp->cfg = cfg; smp->blk_cnt = cfg->mmb_count; smp->blk_size = cfg->mmb_size; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h index 5b6c2363f592..20b87e800ea3 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h @@ -39,7 +39,8 @@ struct mdp5_smp; struct mdp5_smp *mdp5_smp_init(struct drm_device *dev, const struct mdp5_smp_block *cfg); void mdp5_smp_destroy(struct mdp5_smp *smp); -int mdp5_smp_request(struct mdp5_smp *smp, enum mdp5_pipe pipe, u32 fmt, u32 width); +int mdp5_smp_request(struct mdp5_smp *smp, enum mdp5_pipe pipe, + const struct mdp_format *format, u32 width, bool hdecim); void mdp5_smp_configure(struct mdp5_smp *smp, enum mdp5_pipe pipe); void mdp5_smp_commit(struct mdp5_smp *smp, enum mdp5_pipe pipe); void mdp5_smp_release(struct mdp5_smp *smp, enum mdp5_pipe pipe); diff --git a/drivers/gpu/drm/msm/mdp/mdp_common.xml.h b/drivers/gpu/drm/msm/mdp/mdp_common.xml.h index 641d036c5bcb..4f792c4e40f4 100644 --- a/drivers/gpu/drm/msm/mdp/mdp_common.xml.h +++ b/drivers/gpu/drm/msm/mdp/mdp_common.xml.h @@ -8,17 +8,17 @@ 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 ( 676 bytes, from 2014-12-05 15:34:49) -- /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 ( 20915 bytes, from 2015-03-24 22:05:22) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2352 bytes, from 2015-04-12 15:02:42) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 35083 bytes, from 2015-04-12 15:04:03) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 22094 bytes, from 2015-05-12 12:45:23) -- /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 ( 1686 bytes, from 2014-10-31 16:48:57) -- /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 ( 29012 bytes, from 2015-05-12 12:45:23) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-12 12:45:23) +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2576 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 36021 bytes, from 2015-07-09 22:10:24) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 26057 bytes, from 2015-08-14 21:47:57) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) @@ -46,7 +46,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. enum mdp_chroma_samp_type { - CHROMA_RGB = 0, + CHROMA_FULL = 0, CHROMA_H2V1 = 1, CHROMA_H1V2 = 2, CHROMA_420 = 3, @@ -65,6 +65,10 @@ enum mdp_mixer_stage_id { STAGE1 = 3, STAGE2 = 4, STAGE3 = 5, + STAGE4 = 6, + STAGE5 = 7, + STAGE6 = 8, + STAGE_MAX = 8, }; enum mdp_alpha_type { diff --git a/drivers/gpu/drm/msm/mdp/mdp_format.c b/drivers/gpu/drm/msm/mdp/mdp_format.c index 7b0524dc1872..1c2caffc97e4 100644 --- a/drivers/gpu/drm/msm/mdp/mdp_format.c +++ b/drivers/gpu/drm/msm/mdp/mdp_format.c @@ -71,7 +71,7 @@ static struct csc_cfg csc_convert[CSC_MAX] = { }, }; -#define FMT(name, a, r, g, b, e0, e1, e2, e3, alpha, tight, c, cnt, fp, cs) { \ +#define FMT(name, a, r, g, b, e0, e1, e2, e3, alpha, tight, c, cnt, fp, cs, yuv) { \ .base = { .pixel_format = DRM_FORMAT_ ## name }, \ .bpc_a = BPC ## a ## A, \ .bpc_r = BPC ## r, \ @@ -83,7 +83,8 @@ static struct csc_cfg csc_convert[CSC_MAX] = { .cpp = c, \ .unpack_count = cnt, \ .fetch_type = fp, \ - .chroma_sample = cs \ + .chroma_sample = cs, \ + .is_yuv = yuv, \ } #define BPC0A 0 @@ -95,30 +96,49 @@ static struct csc_cfg csc_convert[CSC_MAX] = { 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, - MDP_PLANE_INTERLEAVED, CHROMA_RGB), + MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), FMT(ABGR8888, 8, 8, 8, 8, 2, 0, 1, 3, true, true, 4, 4, - MDP_PLANE_INTERLEAVED, CHROMA_RGB), + MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), FMT(RGBA8888, 8, 8, 8, 8, 3, 1, 0, 2, true, true, 4, 4, - MDP_PLANE_INTERLEAVED, CHROMA_RGB), + MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), FMT(BGRA8888, 8, 8, 8, 8, 3, 2, 0, 1, true, true, 4, 4, - MDP_PLANE_INTERLEAVED, CHROMA_RGB), + MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), FMT(XRGB8888, 8, 8, 8, 8, 1, 0, 2, 3, false, true, 4, 4, - MDP_PLANE_INTERLEAVED, CHROMA_RGB), + MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), FMT(RGB888, 0, 8, 8, 8, 1, 0, 2, 0, false, true, 3, 3, - MDP_PLANE_INTERLEAVED, CHROMA_RGB), + MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), FMT(BGR888, 0, 8, 8, 8, 2, 0, 1, 0, false, true, 3, 3, - MDP_PLANE_INTERLEAVED, CHROMA_RGB), + MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), FMT(RGB565, 0, 5, 6, 5, 1, 0, 2, 0, false, true, 2, 3, - MDP_PLANE_INTERLEAVED, CHROMA_RGB), + MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), FMT(BGR565, 0, 5, 6, 5, 2, 0, 1, 0, false, true, 2, 3, - MDP_PLANE_INTERLEAVED, CHROMA_RGB), + MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), /* --- RGB formats above / YUV formats below this line --- */ + /* 2 plane YUV */ FMT(NV12, 0, 8, 8, 8, 1, 2, 0, 0, false, true, 2, 2, - MDP_PLANE_PSEUDO_PLANAR, CHROMA_420), + MDP_PLANE_PSEUDO_PLANAR, CHROMA_420, true), FMT(NV21, 0, 8, 8, 8, 2, 1, 0, 0, false, true, 2, 2, - MDP_PLANE_PSEUDO_PLANAR, CHROMA_420), + MDP_PLANE_PSEUDO_PLANAR, CHROMA_420, true), + FMT(NV16, 0, 8, 8, 8, 1, 2, 0, 0, false, true, 2, 2, + MDP_PLANE_PSEUDO_PLANAR, CHROMA_H2V1, true), + FMT(NV61, 0, 8, 8, 8, 2, 1, 0, 0, false, true, 2, 2, + MDP_PLANE_PSEUDO_PLANAR, CHROMA_H2V1, true), + /* 1 plane YUV */ + FMT(VYUY, 0, 8, 8, 8, 2, 0, 1, 0, false, true, 2, 4, + MDP_PLANE_INTERLEAVED, CHROMA_H2V1, true), + FMT(UYVY, 0, 8, 8, 8, 1, 0, 2, 0, false, true, 2, 4, + MDP_PLANE_INTERLEAVED, CHROMA_H2V1, true), + FMT(YUYV, 0, 8, 8, 8, 0, 1, 0, 2, false, true, 2, 4, + MDP_PLANE_INTERLEAVED, CHROMA_H2V1, true), + FMT(YVYU, 0, 8, 8, 8, 0, 2, 0, 1, false, true, 2, 4, + MDP_PLANE_INTERLEAVED, CHROMA_H2V1, true), + /* 3 plane YUV */ + FMT(YUV420, 0, 8, 8, 8, 2, 1, 0, 0, false, true, 1, 1, + MDP_PLANE_PLANAR, CHROMA_420, true), + FMT(YVU420, 0, 8, 8, 8, 1, 2, 0, 0, false, true, 1, 1, + MDP_PLANE_PLANAR, CHROMA_420, true), }; /* diff --git a/drivers/gpu/drm/msm/mdp/mdp_kms.c b/drivers/gpu/drm/msm/mdp/mdp_kms.c index 1988c243f437..64287304054d 100644 --- a/drivers/gpu/drm/msm/mdp/mdp_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp_kms.c @@ -39,7 +39,8 @@ static void update_irq(struct mdp_kms *mdp_kms) list_for_each_entry(irq, &mdp_kms->irq_list, node) irqmask |= irq->irqmask; - mdp_kms->funcs->set_irqmask(mdp_kms, irqmask); + mdp_kms->funcs->set_irqmask(mdp_kms, irqmask, mdp_kms->cur_irq_mask); + mdp_kms->cur_irq_mask = irqmask; } /* if an mdp_irq's irqmask has changed, such as when mdp5 crtc<->encoder diff --git a/drivers/gpu/drm/msm/mdp/mdp_kms.h b/drivers/gpu/drm/msm/mdp/mdp_kms.h index 2d3428cb74d0..46a94e7d50e2 100644 --- a/drivers/gpu/drm/msm/mdp/mdp_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp_kms.h @@ -30,7 +30,8 @@ struct mdp_kms; struct mdp_kms_funcs { struct msm_kms_funcs base; - void (*set_irqmask)(struct mdp_kms *mdp_kms, uint32_t irqmask); + void (*set_irqmask)(struct mdp_kms *mdp_kms, uint32_t irqmask, + uint32_t old_irqmask); }; struct mdp_kms { @@ -42,6 +43,7 @@ struct mdp_kms { bool in_irq; struct list_head irq_list; /* list of mdp4_irq */ uint32_t vblank_mask; /* irq bits set for userspace vblank */ + uint32_t cur_irq_mask; /* current irq mask */ }; #define to_mdp_kms(x) container_of(x, struct mdp_kms, base) @@ -90,13 +92,27 @@ struct mdp_format { uint8_t cpp, unpack_count; enum mdp_fetch_type fetch_type; enum mdp_chroma_samp_type chroma_sample; + bool is_yuv; }; #define to_mdp_format(x) container_of(x, struct mdp_format, base) -#define MDP_FORMAT_IS_YUV(mdp_format) ((mdp_format)->chroma_sample > CHROMA_RGB) +#define MDP_FORMAT_IS_YUV(mdp_format) ((mdp_format)->is_yuv) uint32_t mdp_get_formats(uint32_t *formats, uint32_t max_formats, bool rgb_only); const struct msm_format *mdp_get_format(struct msm_kms *kms, uint32_t format); +/* MDP pipe capabilities */ +#define MDP_PIPE_CAP_HFLIP BIT(0) +#define MDP_PIPE_CAP_VFLIP BIT(1) +#define MDP_PIPE_CAP_SCALE BIT(2) +#define MDP_PIPE_CAP_CSC BIT(3) +#define MDP_PIPE_CAP_DECIMATION BIT(4) + +static inline bool pipe_supports_yuv(uint32_t pipe_caps) +{ + return (pipe_caps & MDP_PIPE_CAP_SCALE) && + (pipe_caps & MDP_PIPE_CAP_CSC); +} + enum csc_type { CSC_RGB2RGB = 0, CSC_YUV2RGB, diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index d3467b115e04..0339c5d82d37 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -116,6 +116,65 @@ u32 msm_readl(const void __iomem *addr) return val; } +struct vblank_event { + struct list_head node; + int crtc_id; + bool enable; +}; + +static void vblank_ctrl_worker(struct work_struct *work) +{ + struct msm_vblank_ctrl *vbl_ctrl = container_of(work, + struct msm_vblank_ctrl, work); + struct msm_drm_private *priv = container_of(vbl_ctrl, + struct msm_drm_private, vblank_ctrl); + struct msm_kms *kms = priv->kms; + struct vblank_event *vbl_ev, *tmp; + unsigned long flags; + + spin_lock_irqsave(&vbl_ctrl->lock, flags); + list_for_each_entry_safe(vbl_ev, tmp, &vbl_ctrl->event_list, node) { + list_del(&vbl_ev->node); + spin_unlock_irqrestore(&vbl_ctrl->lock, flags); + + if (vbl_ev->enable) + kms->funcs->enable_vblank(kms, + priv->crtcs[vbl_ev->crtc_id]); + else + kms->funcs->disable_vblank(kms, + priv->crtcs[vbl_ev->crtc_id]); + + kfree(vbl_ev); + + spin_lock_irqsave(&vbl_ctrl->lock, flags); + } + + spin_unlock_irqrestore(&vbl_ctrl->lock, flags); +} + +static int vblank_ctrl_queue_work(struct msm_drm_private *priv, + int crtc_id, bool enable) +{ + struct msm_vblank_ctrl *vbl_ctrl = &priv->vblank_ctrl; + struct vblank_event *vbl_ev; + unsigned long flags; + + vbl_ev = kzalloc(sizeof(*vbl_ev), GFP_ATOMIC); + if (!vbl_ev) + return -ENOMEM; + + vbl_ev->crtc_id = crtc_id; + vbl_ev->enable = enable; + + spin_lock_irqsave(&vbl_ctrl->lock, flags); + list_add_tail(&vbl_ev->node, &vbl_ctrl->event_list); + spin_unlock_irqrestore(&vbl_ctrl->lock, flags); + + queue_work(priv->wq, &vbl_ctrl->work); + + return 0; +} + /* * DRM operations: */ @@ -125,6 +184,18 @@ static int msm_unload(struct drm_device *dev) struct msm_drm_private *priv = dev->dev_private; struct msm_kms *kms = priv->kms; struct msm_gpu *gpu = priv->gpu; + struct msm_vblank_ctrl *vbl_ctrl = &priv->vblank_ctrl; + struct vblank_event *vbl_ev, *tmp; + + /* We must cancel and cleanup any pending vblank enable/disable + * work before drm_irq_uninstall() to avoid work re-enabling an + * irq after uninstall has disabled it. + */ + cancel_work_sync(&vbl_ctrl->work); + list_for_each_entry_safe(vbl_ev, tmp, &vbl_ctrl->event_list, node) { + list_del(&vbl_ev->node); + kfree(vbl_ev); + } drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); @@ -282,6 +353,9 @@ static int msm_load(struct drm_device *dev, unsigned long flags) INIT_LIST_HEAD(&priv->inactive_list); INIT_LIST_HEAD(&priv->fence_cbs); + INIT_LIST_HEAD(&priv->vblank_ctrl.event_list); + INIT_WORK(&priv->vblank_ctrl.work, vblank_ctrl_worker); + spin_lock_init(&priv->vblank_ctrl.lock); drm_mode_config_init(dev); @@ -331,10 +405,6 @@ static int msm_load(struct drm_device *dev, unsigned long flags) } } - dev->mode_config.min_width = 0; - dev->mode_config.min_height = 0; - dev->mode_config.max_width = 2048; - dev->mode_config.max_height = 2048; dev->mode_config.funcs = &mode_config_funcs; ret = drm_vblank_init(dev, priv->num_crtcs); @@ -468,7 +538,7 @@ static int msm_enable_vblank(struct drm_device *dev, int crtc_id) if (!kms) return -ENXIO; DBG("dev=%p, crtc=%d", dev, crtc_id); - return kms->funcs->enable_vblank(kms, priv->crtcs[crtc_id]); + return vblank_ctrl_queue_work(priv, crtc_id, true); } static void msm_disable_vblank(struct drm_device *dev, int crtc_id) @@ -478,7 +548,7 @@ static void msm_disable_vblank(struct drm_device *dev, int crtc_id) if (!kms) return; DBG("dev=%p, crtc=%d", dev, crtc_id); - kms->funcs->disable_vblank(kms, priv->crtcs[crtc_id]); + vblank_ctrl_queue_work(priv, crtc_id, false); } /* diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 4ff0ec9c994b..3be7a56b14f1 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -30,6 +30,7 @@ #include <linux/list.h> #include <linux/iommu.h> #include <linux/types.h> +#include <linux/of_graph.h> #include <asm/sizes.h> #ifndef CONFIG_OF @@ -64,6 +65,19 @@ struct msm_file_private { int dummy; }; +enum msm_mdp_plane_property { + PLANE_PROP_ZPOS, + PLANE_PROP_ALPHA, + PLANE_PROP_PREMULTIPLIED, + PLANE_PROP_MAX_NUM +}; + +struct msm_vblank_ctrl { + struct work_struct work; + struct list_head event_list; + spinlock_t lock; +}; + struct msm_drm_private { struct msm_kms *kms; @@ -128,6 +142,9 @@ struct msm_drm_private { unsigned int num_connectors; struct drm_connector *connectors[8]; + /* Properties */ + struct drm_property *plane_property[PLANE_PROP_MAX_NUM]; + /* VRAM carveout, used when no IOMMU: */ struct { unsigned long size; @@ -137,6 +154,8 @@ struct msm_drm_private { */ struct drm_mm mm; } vram; + + struct msm_vblank_ctrl vblank_ctrl; }; struct msm_format { diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index 95f6532df02d..f97a1964ef39 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -43,11 +43,11 @@ static struct fb_ops msm_fb_ops = { /* Note: to properly handle manual update displays, we wrap the * basic fbdev ops which write to the framebuffer */ - .fb_read = fb_sys_read, - .fb_write = fb_sys_write, - .fb_fillrect = sys_fillrect, - .fb_copyarea = sys_copyarea, - .fb_imageblit = sys_imageblit, + .fb_read = drm_fb_helper_sys_read, + .fb_write = drm_fb_helper_sys_write, + .fb_fillrect = drm_fb_helper_sys_fillrect, + .fb_copyarea = drm_fb_helper_sys_copyarea, + .fb_imageblit = drm_fb_helper_sys_imageblit, .fb_mmap = msm_fbdev_mmap, .fb_check_var = drm_fb_helper_check_var, @@ -144,10 +144,10 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, goto fail_unlock; } - fbi = framebuffer_alloc(0, dev->dev); - if (!fbi) { + fbi = drm_fb_helper_alloc_fbi(helper); + if (IS_ERR(fbi)) { dev_err(dev->dev, "failed to allocate fb info\n"); - ret = -ENOMEM; + ret = PTR_ERR(fbi); goto fail_unlock; } @@ -155,7 +155,6 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, fbdev->fb = fb; helper->fb = fb; - helper->fbdev = fbi; fbi->par = helper; fbi->flags = FBINFO_DEFAULT; @@ -163,12 +162,6 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, strcpy(fbi->fix.id, "msm"); - ret = fb_alloc_cmap(&fbi->cmap, 256, 0); - if (ret) { - ret = -ENOMEM; - goto fail_unlock; - } - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); @@ -191,7 +184,6 @@ fail_unlock: fail: if (ret) { - framebuffer_release(fbi); if (fb) { drm_framebuffer_unregister_private(fb); drm_framebuffer_remove(fb); @@ -266,17 +258,11 @@ void msm_fbdev_free(struct drm_device *dev) struct msm_drm_private *priv = dev->dev_private; struct drm_fb_helper *helper = priv->fbdev; struct msm_fbdev *fbdev; - struct fb_info *fbi; DBG(); - fbi = helper->fbdev; - - /* only cleanup framebuffer if it is present */ - if (fbi) { - unregister_framebuffer(fbi); - framebuffer_release(fbi); - } + drm_fb_helper_unregister_fbi(helper); + drm_fb_helper_release_fbi(helper); drm_fb_helper_fini(helper); |