This patchset brings support for Analogix ANX6345 RGB-(e)DP bridge, which is used by some Allwinner A64 laptops, such as Pinebook and Olimex TERES-I.
It reuses some definitions from ANX78xx driver that already exists in the kernel tree, but the driver code itself is rewritten due to significant difference between ANX6345 and ANX78xx.
This patchset also adds support for LCD panels used in Pinebook, dt property to disable strict dot clock check in sun4i drm driver and adds nodes necessary for LCD support into Pinebook dts.
v2: - sort Kconfig and Makefile entries alphabetically - remove panel supply from anx6345 - add support for panels into anx6345 driver - add compatible and binding for generic eDP panel - replace patch that adds 5% tolerance for dotclock check in sun4i driver for panel and bridges with patch that adds 1% tolerance in case if bridge is connected. v3: - replace 1% tolerance patch with patch that adds dt property to disable strict dot clock check - add support for LCD panels used in Pinebook - drop generic eDP panel driver - drop Teres-I changes, I don't have hardware to test them anyway.
Icenowy Zheng (7): drm/bridge: move ANA78xx driver to analogix subdirectory drm/bridge: split some definitions of ANX78xx to dedicated headers drm/bridge: extract some Analogix I2C DP common code dt-bindings: Add ANX6345 DP/eDP transmitter binding drm/bridge: Add Analogix anx6345 support arm64: allwinner: a64: add pinmux for RGB666 LCD arm64: allwinner: a64: enable LCD-related hardware for Pinebook
Vasily Khoruzhick (4): drm/sun4i: rgb: Add DT property to disable strict clock rate check drm/panel: simple: Add BOE HB140WX1-501 panel support dt-bindings: Add Guangdong Neweast Optoelectronics CO. LTD vendor prefix drm/panel: simple: Add NewEast Optoelectronics CO., LTD WJFH116008A panel support
.../bindings/display/bridge/anx6345.txt | 56 ++ .../display/panel/boe,hb140wx1-501.txt | 7 + .../display/panel/neweast,wjfh116008a.txt | 7 + .../bindings/display/sunxi/sun4i-drm.txt | 2 + .../devicetree/bindings/vendor-prefixes.txt | 1 + .../dts/allwinner/sun50i-a64-pinebook.dts | 76 ++ arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 9 + drivers/gpu/drm/bridge/Kconfig | 10 - drivers/gpu/drm/bridge/Makefile | 4 +- drivers/gpu/drm/bridge/analogix-anx78xx.h | 719 --------------- drivers/gpu/drm/bridge/analogix/Kconfig | 25 + drivers/gpu/drm/bridge/analogix/Makefile | 4 + .../drm/bridge/analogix/analogix-anx6345.c | 845 ++++++++++++++++++ .../bridge/{ => analogix}/analogix-anx78xx.c | 146 +-- .../drm/bridge/analogix/analogix-anx78xx.h | 265 ++++++ .../drm/bridge/analogix/analogix-i2c-dptx.c | 169 ++++ .../drm/bridge/analogix/analogix-i2c-dptx.h | 258 ++++++ .../bridge/analogix/analogix-i2c-txcommon.h | 240 +++++ drivers/gpu/drm/panel/panel-simple.c | 65 ++ drivers/gpu/drm/sun4i/sun4i_rgb.c | 5 + drivers/gpu/drm/sun4i/sun4i_tcon.c | 3 + drivers/gpu/drm/sun4i/sun4i_tcon.h | 1 + 22 files changed, 2041 insertions(+), 876 deletions(-) create mode 100644 Documentation/devicetree/bindings/display/bridge/anx6345.txt create mode 100644 Documentation/devicetree/bindings/display/panel/boe,hb140wx1-501.txt create mode 100644 Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt delete mode 100644 drivers/gpu/drm/bridge/analogix-anx78xx.h create mode 100644 drivers/gpu/drm/bridge/analogix/analogix-anx6345.c rename drivers/gpu/drm/bridge/{ => analogix}/analogix-anx78xx.c (90%) create mode 100644 drivers/gpu/drm/bridge/analogix/analogix-anx78xx.h create mode 100644 drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c create mode 100644 drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h create mode 100644 drivers/gpu/drm/bridge/analogix/analogix-i2c-txcommon.h
From: Icenowy Zheng icenowy@aosc.io
As ANA78xx chips are designed and produced by Analogix Semiconductor, Inc, move their driver codes into analogix subdirectory.
Signed-off-by: Icenowy Zheng icenowy@aosc.io Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/bridge/Kconfig | 10 ---------- drivers/gpu/drm/bridge/Makefile | 4 ++-- drivers/gpu/drm/bridge/analogix/Kconfig | 10 ++++++++++ drivers/gpu/drm/bridge/analogix/Makefile | 1 + .../gpu/drm/bridge/{ => analogix}/analogix-anx78xx.c | 0 .../gpu/drm/bridge/{ => analogix}/analogix-anx78xx.h | 0 6 files changed, 13 insertions(+), 12 deletions(-) rename drivers/gpu/drm/bridge/{ => analogix}/analogix-anx78xx.c (100%) rename drivers/gpu/drm/bridge/{ => analogix}/analogix-anx78xx.h (100%)
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 2fee47b0d50b..4922c1ceffef 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -15,16 +15,6 @@ config DRM_PANEL_BRIDGE menu "Display Interface Bridges" depends on DRM && DRM_BRIDGE
-config DRM_ANALOGIX_ANX78XX - tristate "Analogix ANX78XX bridge" - select DRM_KMS_HELPER - select REGMAP_I2C - ---help--- - ANX78XX is an ultra-low Full-HD SlimPort transmitter - designed for portable devices. The ANX78XX transforms - the HDMI output of an application processor to MyDP - or DisplayPort. - config DRM_CDNS_DSI tristate "Cadence DPI/DSI bridge" select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 4934fcf5a6f8..a6c7dd7727ea 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o @@ -12,8 +11,9 @@ obj-$(CONFIG_DRM_SII9234) += sii9234.o obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o -obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/ obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/ obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o + +obj-y += analogix/ obj-y += synopsys/ diff --git a/drivers/gpu/drm/bridge/analogix/Kconfig b/drivers/gpu/drm/bridge/analogix/Kconfig index 80f286fa3a69..c4d343a2f04d 100644 --- a/drivers/gpu/drm/bridge/analogix/Kconfig +++ b/drivers/gpu/drm/bridge/analogix/Kconfig @@ -1,3 +1,13 @@ +config DRM_ANALOGIX_ANX78XX + tristate "Analogix ANX78XX bridge" + select DRM_KMS_HELPER + select REGMAP_I2C + help + ANX78XX is an ultra-low Full-HD SlimPort transmitter + designed for portable devices. The ANX78XX transforms + the HDMI output of an application processor to MyDP + or DisplayPort. + config DRM_ANALOGIX_DP tristate depends on DRM diff --git a/drivers/gpu/drm/bridge/analogix/Makefile b/drivers/gpu/drm/bridge/analogix/Makefile index cd4010ba6890..ce1687e60975 100644 --- a/drivers/gpu/drm/bridge/analogix/Makefile +++ b/drivers/gpu/drm/bridge/analogix/Makefile @@ -1,2 +1,3 @@ analogix_dp-objs := analogix_dp_core.o analogix_dp_reg.o +obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix_dp.o diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c similarity index 100% rename from drivers/gpu/drm/bridge/analogix-anx78xx.c rename to drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.h b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.h similarity index 100% rename from drivers/gpu/drm/bridge/analogix-anx78xx.h rename to drivers/gpu/drm/bridge/analogix/analogix-anx78xx.h
From: Icenowy Zheng icenowy@aosc.io
Some definitions currently in analogix-anx78xx.h are not restricted to the ANX78xx series, but also applicable to other DisplayPort transmitters by Analogix.
Split out them to dedicated headers, and make analogix-anx78xx.h include them.
Signed-off-by: Icenowy Zheng icenowy@aosc.io Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com --- .../drm/bridge/analogix/analogix-anx78xx.h | 464 +----------------- .../drm/bridge/analogix/analogix-i2c-dptx.h | 248 ++++++++++ .../bridge/analogix/analogix-i2c-txcommon.h | 237 +++++++++ 3 files changed, 490 insertions(+), 459 deletions(-) create mode 100644 drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h create mode 100644 drivers/gpu/drm/bridge/analogix/analogix-i2c-txcommon.h
diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.h b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.h index 38753c870137..8aa1eca306d3 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.h +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.h @@ -15,9 +15,12 @@ #ifndef __ANX78xx_H #define __ANX78xx_H
-#define TX_P0 0x70 +#include "analogix-i2c-dptx.h" +#include "analogix-i2c-txcommon.h" + +#define TX_P0 ANALOGIX_I2C_DPTX #define TX_P1 0x7a -#define TX_P2 0x72 +#define TX_P2 ANALOGIX_I2C_TXCOMMON
#define RX_P0 0x7e #define RX_P1 0x80 @@ -225,463 +228,6 @@ #define SP_CLEAR_AVMUTE BIT(4) #define SP_SET_AVMUTE BIT(0)
-/***************************************************************/ -/* Register definition of device address 0x70 */ -/***************************************************************/ - -/* HDCP Status Register */ -#define SP_TX_HDCP_STATUS_REG 0x00 -#define SP_AUTH_FAIL BIT(5) -#define SP_AUTHEN_PASS BIT(1) - -/* HDCP Control Register 0 */ -#define SP_HDCP_CTRL0_REG 0x01 -#define SP_RX_REPEATER BIT(6) -#define SP_RE_AUTH BIT(5) -#define SP_SW_AUTH_OK BIT(4) -#define SP_HARD_AUTH_EN BIT(3) -#define SP_HDCP_ENC_EN BIT(2) -#define SP_BKSV_SRM_PASS BIT(1) -#define SP_KSVLIST_VLD BIT(0) -/* HDCP Function Enabled */ -#define SP_HDCP_FUNCTION_ENABLED (BIT(0) | BIT(1) | BIT(2) | BIT(3)) - -/* HDCP Receiver BSTATUS Register 0 */ -#define SP_HDCP_RX_BSTATUS0_REG 0x1b -/* HDCP Receiver BSTATUS Register 1 */ -#define SP_HDCP_RX_BSTATUS1_REG 0x1c - -/* HDCP Embedded "Blue Screen" Content Registers */ -#define SP_HDCP_VID0_BLUE_SCREEN_REG 0x2c -#define SP_HDCP_VID1_BLUE_SCREEN_REG 0x2d -#define SP_HDCP_VID2_BLUE_SCREEN_REG 0x2e - -/* HDCP Wait R0 Timing Register */ -#define SP_HDCP_WAIT_R0_TIME_REG 0x40 - -/* HDCP Link Integrity Check Timer Register */ -#define SP_HDCP_LINK_CHECK_TIMER_REG 0x41 - -/* HDCP Repeater Ready Wait Timer Register */ -#define SP_HDCP_RPTR_RDY_WAIT_TIME_REG 0x42 - -/* HDCP Auto Timer Register */ -#define SP_HDCP_AUTO_TIMER_REG 0x51 - -/* HDCP Key Status Register */ -#define SP_HDCP_KEY_STATUS_REG 0x5e - -/* HDCP Key Command Register */ -#define SP_HDCP_KEY_COMMAND_REG 0x5f -#define SP_DISABLE_SYNC_HDCP BIT(2) - -/* OTP Memory Key Protection Registers */ -#define SP_OTP_KEY_PROTECT1_REG 0x60 -#define SP_OTP_KEY_PROTECT2_REG 0x61 -#define SP_OTP_KEY_PROTECT3_REG 0x62 -#define SP_OTP_PSW1 0xa2 -#define SP_OTP_PSW2 0x7e -#define SP_OTP_PSW3 0xc6 - -/* DP System Control Registers */ -#define SP_DP_SYSTEM_CTRL_BASE (0x80 - 1) -/* Bits for DP System Control Register 2 */ -#define SP_CHA_STA BIT(2) -/* Bits for DP System Control Register 3 */ -#define SP_HPD_STATUS BIT(6) -#define SP_STRM_VALID BIT(2) -/* Bits for DP System Control Register 4 */ -#define SP_ENHANCED_MODE BIT(3) - -/* DP Video Control Register */ -#define SP_DP_VIDEO_CTRL_REG 0x84 -#define SP_COLOR_F_MASK 0x06 -#define SP_COLOR_F_SHIFT 1 -#define SP_BPC_MASK 0xe0 -#define SP_BPC_SHIFT 5 -# define SP_BPC_6BITS 0x00 -# define SP_BPC_8BITS 0x01 -# define SP_BPC_10BITS 0x02 -# define SP_BPC_12BITS 0x03 - -/* DP Audio Control Register */ -#define SP_DP_AUDIO_CTRL_REG 0x87 -#define SP_AUD_EN BIT(0) - -/* 10us Pulse Generate Timer Registers */ -#define SP_I2C_GEN_10US_TIMER0_REG 0x88 -#define SP_I2C_GEN_10US_TIMER1_REG 0x89 - -/* Packet Send Control Register */ -#define SP_PACKET_SEND_CTRL_REG 0x90 -#define SP_AUD_IF_UP BIT(7) -#define SP_AVI_IF_UD BIT(6) -#define SP_MPEG_IF_UD BIT(5) -#define SP_SPD_IF_UD BIT(4) -#define SP_AUD_IF_EN BIT(3) -#define SP_AVI_IF_EN BIT(2) -#define SP_MPEG_IF_EN BIT(1) -#define SP_SPD_IF_EN BIT(0) - -/* DP HDCP Control Register */ -#define SP_DP_HDCP_CTRL_REG 0x92 -#define SP_AUTO_EN BIT(7) -#define SP_AUTO_START BIT(5) -#define SP_LINK_POLLING BIT(1) - -/* DP Main Link Bandwidth Setting Register */ -#define SP_DP_MAIN_LINK_BW_SET_REG 0xa0 -#define SP_LINK_BW_SET_MASK 0x1f -#define SP_INITIAL_SLIM_M_AUD_SEL BIT(5) - -/* DP Training Pattern Set Register */ -#define SP_DP_TRAINING_PATTERN_SET_REG 0xa2 - -/* DP Lane 0 Link Training Control Register */ -#define SP_DP_LANE0_LT_CTRL_REG 0xa3 -#define SP_TX_SW_SET_MASK 0x1b -#define SP_MAX_PRE_REACH BIT(5) -#define SP_MAX_DRIVE_REACH BIT(4) -#define SP_PRE_EMP_LEVEL1 BIT(3) -#define SP_DRVIE_CURRENT_LEVEL1 BIT(0) - -/* DP Link Training Control Register */ -#define SP_DP_LT_CTRL_REG 0xa8 -#define SP_LT_ERROR_TYPE_MASK 0x70 -# define SP_LT_NO_ERROR 0x00 -# define SP_LT_AUX_WRITE_ERROR 0x01 -# define SP_LT_MAX_DRIVE_REACHED 0x02 -# define SP_LT_WRONG_LANE_COUNT_SET 0x03 -# define SP_LT_LOOP_SAME_5_TIME 0x04 -# define SP_LT_CR_FAIL_IN_EQ 0x05 -# define SP_LT_EQ_LOOP_5_TIME 0x06 -#define SP_LT_EN BIT(0) - -/* DP CEP Training Control Registers */ -#define SP_DP_CEP_TRAINING_CTRL0_REG 0xa9 -#define SP_DP_CEP_TRAINING_CTRL1_REG 0xaa - -/* DP Debug Register 1 */ -#define SP_DP_DEBUG1_REG 0xb0 -#define SP_DEBUG_PLL_LOCK BIT(4) -#define SP_POLLING_EN BIT(1) - -/* DP Polling Control Register */ -#define SP_DP_POLLING_CTRL_REG 0xb4 -#define SP_AUTO_POLLING_DISABLE BIT(0) - -/* DP Link Debug Control Register */ -#define SP_DP_LINK_DEBUG_CTRL_REG 0xb8 -#define SP_M_VID_DEBUG BIT(5) -#define SP_NEW_PRBS7 BIT(4) -#define SP_INSERT_ER BIT(1) -#define SP_PRBS31_EN BIT(0) - -/* AUX Misc control Register */ -#define SP_AUX_MISC_CTRL_REG 0xbf - -/* DP PLL control Register */ -#define SP_DP_PLL_CTRL_REG 0xc7 -#define SP_PLL_RST BIT(6) - -/* DP Analog Power Down Register */ -#define SP_DP_ANALOG_POWER_DOWN_REG 0xc8 -#define SP_CH0_PD BIT(0) - -/* DP Misc Control Register */ -#define SP_DP_MISC_CTRL_REG 0xcd -#define SP_EQ_TRAINING_LOOP BIT(6) - -/* DP Extra I2C Device Address Register */ -#define SP_DP_EXTRA_I2C_DEV_ADDR_REG 0xce -#define SP_I2C_STRETCH_DISABLE BIT(7) - -#define SP_I2C_EXTRA_ADDR 0x50 - -/* DP Downspread Control Register 1 */ -#define SP_DP_DOWNSPREAD_CTRL1_REG 0xd0 - -/* DP M Value Calculation Control Register */ -#define SP_DP_M_CALCULATION_CTRL_REG 0xd9 -#define SP_M_GEN_CLK_SEL BIT(0) - -/* AUX Channel Access Status Register */ -#define SP_AUX_CH_STATUS_REG 0xe0 -#define SP_AUX_STATUS 0x0f - -/* AUX Channel DEFER Control Register */ -#define SP_AUX_DEFER_CTRL_REG 0xe2 -#define SP_DEFER_CTRL_EN BIT(7) - -/* DP Buffer Data Count Register */ -#define SP_BUF_DATA_COUNT_REG 0xe4 -#define SP_BUF_DATA_COUNT_MASK 0x1f -#define SP_BUF_CLR BIT(7) - -/* DP AUX Channel Control Register 1 */ -#define SP_DP_AUX_CH_CTRL1_REG 0xe5 -#define SP_AUX_TX_COMM_MASK 0x0f -#define SP_AUX_LENGTH_MASK 0xf0 -#define SP_AUX_LENGTH_SHIFT 4 - -/* DP AUX CH Address Register 0 */ -#define SP_AUX_ADDR_7_0_REG 0xe6 - -/* DP AUX CH Address Register 1 */ -#define SP_AUX_ADDR_15_8_REG 0xe7 - -/* DP AUX CH Address Register 2 */ -#define SP_AUX_ADDR_19_16_REG 0xe8 -#define SP_AUX_ADDR_19_16_MASK 0x0f - -/* DP AUX Channel Control Register 2 */ -#define SP_DP_AUX_CH_CTRL2_REG 0xe9 -#define SP_AUX_SEL_RXCM BIT(6) -#define SP_AUX_CHSEL BIT(3) -#define SP_AUX_PN_INV BIT(2) -#define SP_ADDR_ONLY BIT(1) -#define SP_AUX_EN BIT(0) - -/* DP Video Stream Control InfoFrame Register */ -#define SP_DP_3D_VSC_CTRL_REG 0xea -#define SP_INFO_FRAME_VSC_EN BIT(0) - -/* DP Video Stream Data Byte 1 Register */ -#define SP_DP_VSC_DB1_REG 0xeb - -/* DP AUX Channel Control Register 3 */ -#define SP_DP_AUX_CH_CTRL3_REG 0xec -#define SP_WAIT_COUNTER_7_0_MASK 0xff - -/* DP AUX Channel Control Register 4 */ -#define SP_DP_AUX_CH_CTRL4_REG 0xed - -/* DP AUX Buffer Data Registers */ -#define SP_DP_BUF_DATA0_REG 0xf0 - -/***************************************************************/ -/* Register definition of device address 0x72 */ -/***************************************************************/ - -/* - * Core Register Definitions - */ - -/* Device ID Low Byte Register */ -#define SP_DEVICE_IDL_REG 0x02 - -/* Device ID High Byte Register */ -#define SP_DEVICE_IDH_REG 0x03 - -/* Device version register */ -#define SP_DEVICE_VERSION_REG 0x04 - -/* Power Down Control Register */ -#define SP_POWERDOWN_CTRL_REG 0x05 -#define SP_REGISTER_PD BIT(7) -#define SP_HDCP_PD BIT(5) -#define SP_AUDIO_PD BIT(4) -#define SP_VIDEO_PD BIT(3) -#define SP_LINK_PD BIT(2) -#define SP_TOTAL_PD BIT(1) - -/* Reset Control Register 1 */ -#define SP_RESET_CTRL1_REG 0x06 -#define SP_MISC_RST BIT(7) -#define SP_VIDCAP_RST BIT(6) -#define SP_VIDFIF_RST BIT(5) -#define SP_AUDFIF_RST BIT(4) -#define SP_AUDCAP_RST BIT(3) -#define SP_HDCP_RST BIT(2) -#define SP_SW_RST BIT(1) -#define SP_HW_RST BIT(0) - -/* Reset Control Register 2 */ -#define SP_RESET_CTRL2_REG 0x07 -#define SP_AUX_RST BIT(2) -#define SP_SERDES_FIFO_RST BIT(1) -#define SP_I2C_REG_RST BIT(0) - -/* Video Control Register 1 */ -#define SP_VID_CTRL1_REG 0x08 -#define SP_VIDEO_EN BIT(7) -#define SP_VIDEO_MUTE BIT(2) -#define SP_DE_GEN BIT(1) -#define SP_DEMUX BIT(0) - -/* Video Control Register 2 */ -#define SP_VID_CTRL2_REG 0x09 -#define SP_IN_COLOR_F_MASK 0x03 -#define SP_IN_YC_BIT_SEL BIT(2) -#define SP_IN_BPC_MASK 0x70 -#define SP_IN_BPC_SHIFT 4 -# define SP_IN_BPC_12BIT 0x03 -# define SP_IN_BPC_10BIT 0x02 -# define SP_IN_BPC_8BIT 0x01 -# define SP_IN_BPC_6BIT 0x00 -#define SP_IN_D_RANGE BIT(7) - -/* Video Control Register 3 */ -#define SP_VID_CTRL3_REG 0x0a -#define SP_HPD_OUT BIT(6) - -/* Video Control Register 5 */ -#define SP_VID_CTRL5_REG 0x0c -#define SP_CSC_STD_SEL BIT(7) -#define SP_XVYCC_RNG_LMT BIT(6) -#define SP_RANGE_Y2R BIT(5) -#define SP_CSPACE_Y2R BIT(4) -#define SP_RGB_RNG_LMT BIT(3) -#define SP_Y_RNG_LMT BIT(2) -#define SP_RANGE_R2Y BIT(1) -#define SP_CSPACE_R2Y BIT(0) - -/* Video Control Register 6 */ -#define SP_VID_CTRL6_REG 0x0d -#define SP_TEST_PATTERN_EN BIT(7) -#define SP_VIDEO_PROCESS_EN BIT(6) -#define SP_VID_US_MODE BIT(3) -#define SP_VID_DS_MODE BIT(2) -#define SP_UP_SAMPLE BIT(1) -#define SP_DOWN_SAMPLE BIT(0) - -/* Video Control Register 8 */ -#define SP_VID_CTRL8_REG 0x0f -#define SP_VID_VRES_TH BIT(0) - -/* Total Line Status Low Byte Register */ -#define SP_TOTAL_LINE_STAL_REG 0x24 - -/* Total Line Status High Byte Register */ -#define SP_TOTAL_LINE_STAH_REG 0x25 - -/* Active Line Status Low Byte Register */ -#define SP_ACT_LINE_STAL_REG 0x26 - -/* Active Line Status High Byte Register */ -#define SP_ACT_LINE_STAH_REG 0x27 - -/* Vertical Front Porch Status Register */ -#define SP_V_F_PORCH_STA_REG 0x28 - -/* Vertical SYNC Width Status Register */ -#define SP_V_SYNC_STA_REG 0x29 - -/* Vertical Back Porch Status Register */ -#define SP_V_B_PORCH_STA_REG 0x2a - -/* Total Pixel Status Low Byte Register */ -#define SP_TOTAL_PIXEL_STAL_REG 0x2b - -/* Total Pixel Status High Byte Register */ -#define SP_TOTAL_PIXEL_STAH_REG 0x2c - -/* Active Pixel Status Low Byte Register */ -#define SP_ACT_PIXEL_STAL_REG 0x2d - -/* Active Pixel Status High Byte Register */ -#define SP_ACT_PIXEL_STAH_REG 0x2e - -/* Horizontal Front Porch Status Low Byte Register */ -#define SP_H_F_PORCH_STAL_REG 0x2f - -/* Horizontal Front Porch Statys High Byte Register */ -#define SP_H_F_PORCH_STAH_REG 0x30 - -/* Horizontal SYNC Width Status Low Byte Register */ -#define SP_H_SYNC_STAL_REG 0x31 - -/* Horizontal SYNC Width Status High Byte Register */ -#define SP_H_SYNC_STAH_REG 0x32 - -/* Horizontal Back Porch Status Low Byte Register */ -#define SP_H_B_PORCH_STAL_REG 0x33 - -/* Horizontal Back Porch Status High Byte Register */ -#define SP_H_B_PORCH_STAH_REG 0x34 - -/* InfoFrame AVI Packet DB1 Register */ -#define SP_INFOFRAME_AVI_DB1_REG 0x70 - -/* Bit Control Specific Register */ -#define SP_BIT_CTRL_SPECIFIC_REG 0x80 -#define SP_BIT_CTRL_SELECT_SHIFT 1 -#define SP_ENABLE_BIT_CTRL BIT(0) - -/* InfoFrame Audio Packet DB1 Register */ -#define SP_INFOFRAME_AUD_DB1_REG 0x83 - -/* InfoFrame MPEG Packet DB1 Register */ -#define SP_INFOFRAME_MPEG_DB1_REG 0xb0 - -/* Audio Channel Status Registers */ -#define SP_AUD_CH_STATUS_BASE 0xd0 - -/* Audio Channel Num Register 5 */ -#define SP_I2S_CHANNEL_NUM_MASK 0xe0 -# define SP_I2S_CH_NUM_1 (0x00 << 5) -# define SP_I2S_CH_NUM_2 (0x01 << 5) -# define SP_I2S_CH_NUM_3 (0x02 << 5) -# define SP_I2S_CH_NUM_4 (0x03 << 5) -# define SP_I2S_CH_NUM_5 (0x04 << 5) -# define SP_I2S_CH_NUM_6 (0x05 << 5) -# define SP_I2S_CH_NUM_7 (0x06 << 5) -# define SP_I2S_CH_NUM_8 (0x07 << 5) -#define SP_EXT_VUCP BIT(2) -#define SP_VBIT BIT(1) -#define SP_AUDIO_LAYOUT BIT(0) - -/* Analog Debug Register 2 */ -#define SP_ANALOG_DEBUG2_REG 0xdd -#define SP_FORCE_SW_OFF_BYPASS 0x20 -#define SP_XTAL_FRQ 0x1c -# define SP_XTAL_FRQ_19M2 (0x00 << 2) -# define SP_XTAL_FRQ_24M (0x01 << 2) -# define SP_XTAL_FRQ_25M (0x02 << 2) -# define SP_XTAL_FRQ_26M (0x03 << 2) -# define SP_XTAL_FRQ_27M (0x04 << 2) -# define SP_XTAL_FRQ_38M4 (0x05 << 2) -# define SP_XTAL_FRQ_52M (0x06 << 2) -#define SP_POWERON_TIME_1P5MS 0x03 - -/* Analog Control 0 Register */ -#define SP_ANALOG_CTRL0_REG 0xe1 - -/* Common Interrupt Status Register 1 */ -#define SP_COMMON_INT_STATUS_BASE (0xf1 - 1) -#define SP_PLL_LOCK_CHG 0x40 - -/* Common Interrupt Status Register 2 */ -#define SP_COMMON_INT_STATUS2 0xf2 -#define SP_HDCP_AUTH_CHG BIT(1) -#define SP_HDCP_AUTH_DONE BIT(0) - -#define SP_HDCP_LINK_CHECK_FAIL BIT(0) - -/* Common Interrupt Status Register 4 */ -#define SP_COMMON_INT_STATUS4_REG 0xf4 -#define SP_HPD_IRQ BIT(6) -#define SP_HPD_ESYNC_ERR BIT(4) -#define SP_HPD_CHG BIT(2) -#define SP_HPD_LOST BIT(1) -#define SP_HPD_PLUG BIT(0) - -/* DP Interrupt Status Register */ -#define SP_DP_INT_STATUS1_REG 0xf7 -#define SP_TRAINING_FINISH BIT(5) -#define SP_POLLING_ERR BIT(4) - -/* Common Interrupt Mask Register */ -#define SP_COMMON_INT_MASK_BASE (0xf8 - 1) - -#define SP_COMMON_INT_MASK4_REG 0xfb - -/* DP Interrupts Mask Register */ -#define SP_DP_INT_MASK1_REG 0xfe - -/* Interrupt Control Register */ -#define SP_INT_CTRL_REG 0xff - /***************************************************************/ /* Register definition of device address 0x7a */ /***************************************************************/ diff --git a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h new file mode 100644 index 000000000000..bc0831b127bf --- /dev/null +++ b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2017 Icenowy Zheng icenowy@aosc.io + * + * Based on analogix-anx78xx.h, which is: + * Copyright(c) 2016, Analogix Semiconductor. All rights reserved. + */ + +#ifndef _ANALOGIX_I2C_DPTX_H_ +#define _ANALOGIX_I2C_DPTX_H_ + +#define ANALOGIX_I2C_DPTX 0x70 + +/***************************************************************/ +/* Register definition of device address 0x70 */ +/***************************************************************/ + +/* HDCP Status Register */ +#define SP_TX_HDCP_STATUS_REG 0x00 +#define SP_AUTH_FAIL BIT(5) +#define SP_AUTHEN_PASS BIT(1) + +/* HDCP Control Register 0 */ +#define SP_HDCP_CTRL0_REG 0x01 +#define SP_RX_REPEATER BIT(6) +#define SP_RE_AUTH BIT(5) +#define SP_SW_AUTH_OK BIT(4) +#define SP_HARD_AUTH_EN BIT(3) +#define SP_HDCP_ENC_EN BIT(2) +#define SP_BKSV_SRM_PASS BIT(1) +#define SP_KSVLIST_VLD BIT(0) +/* HDCP Function Enabled */ +#define SP_HDCP_FUNCTION_ENABLED (BIT(0) | BIT(1) | BIT(2) | BIT(3)) + +/* HDCP Receiver BSTATUS Register 0 */ +#define SP_HDCP_RX_BSTATUS0_REG 0x1b +/* HDCP Receiver BSTATUS Register 1 */ +#define SP_HDCP_RX_BSTATUS1_REG 0x1c + +/* HDCP Embedded "Blue Screen" Content Registers */ +#define SP_HDCP_VID0_BLUE_SCREEN_REG 0x2c +#define SP_HDCP_VID1_BLUE_SCREEN_REG 0x2d +#define SP_HDCP_VID2_BLUE_SCREEN_REG 0x2e + +/* HDCP Wait R0 Timing Register */ +#define SP_HDCP_WAIT_R0_TIME_REG 0x40 + +/* HDCP Link Integrity Check Timer Register */ +#define SP_HDCP_LINK_CHECK_TIMER_REG 0x41 + +/* HDCP Repeater Ready Wait Timer Register */ +#define SP_HDCP_RPTR_RDY_WAIT_TIME_REG 0x42 + +/* HDCP Auto Timer Register */ +#define SP_HDCP_AUTO_TIMER_REG 0x51 + +/* HDCP Key Status Register */ +#define SP_HDCP_KEY_STATUS_REG 0x5e + +/* HDCP Key Command Register */ +#define SP_HDCP_KEY_COMMAND_REG 0x5f +#define SP_DISABLE_SYNC_HDCP BIT(2) + +/* OTP Memory Key Protection Registers */ +#define SP_OTP_KEY_PROTECT1_REG 0x60 +#define SP_OTP_KEY_PROTECT2_REG 0x61 +#define SP_OTP_KEY_PROTECT3_REG 0x62 +#define SP_OTP_PSW1 0xa2 +#define SP_OTP_PSW2 0x7e +#define SP_OTP_PSW3 0xc6 + +/* DP System Control Registers */ +#define SP_DP_SYSTEM_CTRL_BASE (0x80 - 1) +/* Bits for DP System Control Register 2 */ +#define SP_CHA_STA BIT(2) +/* Bits for DP System Control Register 3 */ +#define SP_HPD_STATUS BIT(6) +#define SP_STRM_VALID BIT(2) +/* Bits for DP System Control Register 4 */ +#define SP_ENHANCED_MODE BIT(3) + +/* DP Video Control Register */ +#define SP_DP_VIDEO_CTRL_REG 0x84 +#define SP_COLOR_F_MASK 0x06 +#define SP_COLOR_F_SHIFT 1 +#define SP_BPC_MASK 0xe0 +#define SP_BPC_SHIFT 5 +# define SP_BPC_6BITS 0x00 +# define SP_BPC_8BITS 0x01 +# define SP_BPC_10BITS 0x02 +# define SP_BPC_12BITS 0x03 + +/* DP Audio Control Register */ +#define SP_DP_AUDIO_CTRL_REG 0x87 +#define SP_AUD_EN BIT(0) + +/* 10us Pulse Generate Timer Registers */ +#define SP_I2C_GEN_10US_TIMER0_REG 0x88 +#define SP_I2C_GEN_10US_TIMER1_REG 0x89 + +/* Packet Send Control Register */ +#define SP_PACKET_SEND_CTRL_REG 0x90 +#define SP_AUD_IF_UP BIT(7) +#define SP_AVI_IF_UD BIT(6) +#define SP_MPEG_IF_UD BIT(5) +#define SP_SPD_IF_UD BIT(4) +#define SP_AUD_IF_EN BIT(3) +#define SP_AVI_IF_EN BIT(2) +#define SP_MPEG_IF_EN BIT(1) +#define SP_SPD_IF_EN BIT(0) + +/* DP HDCP Control Register */ +#define SP_DP_HDCP_CTRL_REG 0x92 +#define SP_AUTO_EN BIT(7) +#define SP_AUTO_START BIT(5) +#define SP_LINK_POLLING BIT(1) + +/* DP Main Link Bandwidth Setting Register */ +#define SP_DP_MAIN_LINK_BW_SET_REG 0xa0 +#define SP_LINK_BW_SET_MASK 0x1f +#define SP_INITIAL_SLIM_M_AUD_SEL BIT(5) + +/* DP Training Pattern Set Register */ +#define SP_DP_TRAINING_PATTERN_SET_REG 0xa2 + +/* DP Lane 0 Link Training Control Register */ +#define SP_DP_LANE0_LT_CTRL_REG 0xa3 +#define SP_TX_SW_SET_MASK 0x1b +#define SP_MAX_PRE_REACH BIT(5) +#define SP_MAX_DRIVE_REACH BIT(4) +#define SP_PRE_EMP_LEVEL1 BIT(3) +#define SP_DRVIE_CURRENT_LEVEL1 BIT(0) + +/* DP Link Training Control Register */ +#define SP_DP_LT_CTRL_REG 0xa8 +#define SP_LT_ERROR_TYPE_MASK 0x70 +# define SP_LT_NO_ERROR 0x00 +# define SP_LT_AUX_WRITE_ERROR 0x01 +# define SP_LT_MAX_DRIVE_REACHED 0x02 +# define SP_LT_WRONG_LANE_COUNT_SET 0x03 +# define SP_LT_LOOP_SAME_5_TIME 0x04 +# define SP_LT_CR_FAIL_IN_EQ 0x05 +# define SP_LT_EQ_LOOP_5_TIME 0x06 +#define SP_LT_EN BIT(0) + +/* DP CEP Training Control Registers */ +#define SP_DP_CEP_TRAINING_CTRL0_REG 0xa9 +#define SP_DP_CEP_TRAINING_CTRL1_REG 0xaa + +/* DP Debug Register 1 */ +#define SP_DP_DEBUG1_REG 0xb0 +#define SP_DEBUG_PLL_LOCK BIT(4) +#define SP_POLLING_EN BIT(1) + +/* DP Polling Control Register */ +#define SP_DP_POLLING_CTRL_REG 0xb4 +#define SP_AUTO_POLLING_DISABLE BIT(0) + +/* DP Link Debug Control Register */ +#define SP_DP_LINK_DEBUG_CTRL_REG 0xb8 +#define SP_M_VID_DEBUG BIT(5) +#define SP_NEW_PRBS7 BIT(4) +#define SP_INSERT_ER BIT(1) +#define SP_PRBS31_EN BIT(0) + +/* AUX Misc control Register */ +#define SP_AUX_MISC_CTRL_REG 0xbf + +/* DP PLL control Register */ +#define SP_DP_PLL_CTRL_REG 0xc7 +#define SP_PLL_RST BIT(6) + +/* DP Analog Power Down Register */ +#define SP_DP_ANALOG_POWER_DOWN_REG 0xc8 +#define SP_CH0_PD BIT(0) + +/* DP Misc Control Register */ +#define SP_DP_MISC_CTRL_REG 0xcd +#define SP_EQ_TRAINING_LOOP BIT(6) + +/* DP Extra I2C Device Address Register */ +#define SP_DP_EXTRA_I2C_DEV_ADDR_REG 0xce +#define SP_I2C_STRETCH_DISABLE BIT(7) + +#define SP_I2C_EXTRA_ADDR 0x50 + +/* DP Downspread Control Register 1 */ +#define SP_DP_DOWNSPREAD_CTRL1_REG 0xd0 + +/* DP M Value Calculation Control Register */ +#define SP_DP_M_CALCULATION_CTRL_REG 0xd9 +#define SP_M_GEN_CLK_SEL BIT(0) + +/* AUX Channel Access Status Register */ +#define SP_AUX_CH_STATUS_REG 0xe0 +#define SP_AUX_STATUS 0x0f + +/* AUX Channel DEFER Control Register */ +#define SP_AUX_DEFER_CTRL_REG 0xe2 +#define SP_DEFER_CTRL_EN BIT(7) + +/* DP Buffer Data Count Register */ +#define SP_BUF_DATA_COUNT_REG 0xe4 +#define SP_BUF_DATA_COUNT_MASK 0x1f +#define SP_BUF_CLR BIT(7) + +/* DP AUX Channel Control Register 1 */ +#define SP_DP_AUX_CH_CTRL1_REG 0xe5 +#define SP_AUX_TX_COMM_MASK 0x0f +#define SP_AUX_LENGTH_MASK 0xf0 +#define SP_AUX_LENGTH_SHIFT 4 + +/* DP AUX CH Address Register 0 */ +#define SP_AUX_ADDR_7_0_REG 0xe6 + +/* DP AUX CH Address Register 1 */ +#define SP_AUX_ADDR_15_8_REG 0xe7 + +/* DP AUX CH Address Register 2 */ +#define SP_AUX_ADDR_19_16_REG 0xe8 +#define SP_AUX_ADDR_19_16_MASK 0x0f + +/* DP AUX Channel Control Register 2 */ +#define SP_DP_AUX_CH_CTRL2_REG 0xe9 +#define SP_AUX_SEL_RXCM BIT(6) +#define SP_AUX_CHSEL BIT(3) +#define SP_AUX_PN_INV BIT(2) +#define SP_ADDR_ONLY BIT(1) +#define SP_AUX_EN BIT(0) + +/* DP Video Stream Control InfoFrame Register */ +#define SP_DP_3D_VSC_CTRL_REG 0xea +#define SP_INFO_FRAME_VSC_EN BIT(0) + +/* DP Video Stream Data Byte 1 Register */ +#define SP_DP_VSC_DB1_REG 0xeb + +/* DP AUX Channel Control Register 3 */ +#define SP_DP_AUX_CH_CTRL3_REG 0xec +#define SP_WAIT_COUNTER_7_0_MASK 0xff + +/* DP AUX Channel Control Register 4 */ +#define SP_DP_AUX_CH_CTRL4_REG 0xed + +/* DP AUX Buffer Data Registers */ +#define SP_DP_BUF_DATA0_REG 0xf0 + +#endif diff --git a/drivers/gpu/drm/bridge/analogix/analogix-i2c-txcommon.h b/drivers/gpu/drm/bridge/analogix/analogix-i2c-txcommon.h new file mode 100644 index 000000000000..7d683573e970 --- /dev/null +++ b/drivers/gpu/drm/bridge/analogix/analogix-i2c-txcommon.h @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2017 Icenowy Zheng icenowy@aosc.io + * + * Based on analogix-anx78xx.h, which is: + * Copyright(c) 2016, Analogix Semiconductor. All rights reserved. + */ + +#ifndef _ANALOGIX_I2C_TXCOMMON_H_ +#define _ANALOGIX_I2C_TXCOMMON_H_ + +#define ANALOGIX_I2C_TXCOMMON 0x72 + +/***************************************************************/ +/* Register definition of device address 0x72 */ +/***************************************************************/ + +/* + * Core Register Definitions + */ + +/* Device ID Low Byte Register */ +#define SP_DEVICE_IDL_REG 0x02 + +/* Device ID High Byte Register */ +#define SP_DEVICE_IDH_REG 0x03 + +/* Device version register */ +#define SP_DEVICE_VERSION_REG 0x04 + +/* Power Down Control Register */ +#define SP_POWERDOWN_CTRL_REG 0x05 +#define SP_REGISTER_PD BIT(7) +#define SP_HDCP_PD BIT(5) +#define SP_AUDIO_PD BIT(4) +#define SP_VIDEO_PD BIT(3) +#define SP_LINK_PD BIT(2) +#define SP_TOTAL_PD BIT(1) + +/* Reset Control Register 1 */ +#define SP_RESET_CTRL1_REG 0x06 +#define SP_MISC_RST BIT(7) +#define SP_VIDCAP_RST BIT(6) +#define SP_VIDFIF_RST BIT(5) +#define SP_AUDFIF_RST BIT(4) +#define SP_AUDCAP_RST BIT(3) +#define SP_HDCP_RST BIT(2) +#define SP_SW_RST BIT(1) +#define SP_HW_RST BIT(0) + +/* Reset Control Register 2 */ +#define SP_RESET_CTRL2_REG 0x07 +#define SP_AUX_RST BIT(2) +#define SP_SERDES_FIFO_RST BIT(1) +#define SP_I2C_REG_RST BIT(0) + +/* Video Control Register 1 */ +#define SP_VID_CTRL1_REG 0x08 +#define SP_VIDEO_EN BIT(7) +#define SP_VIDEO_MUTE BIT(2) +#define SP_DE_GEN BIT(1) +#define SP_DEMUX BIT(0) + +/* Video Control Register 2 */ +#define SP_VID_CTRL2_REG 0x09 +#define SP_IN_COLOR_F_MASK 0x03 +#define SP_IN_YC_BIT_SEL BIT(2) +#define SP_IN_BPC_MASK 0x70 +#define SP_IN_BPC_SHIFT 4 +# define SP_IN_BPC_12BIT 0x03 +# define SP_IN_BPC_10BIT 0x02 +# define SP_IN_BPC_8BIT 0x01 +# define SP_IN_BPC_6BIT 0x00 +#define SP_IN_D_RANGE BIT(7) + +/* Video Control Register 3 */ +#define SP_VID_CTRL3_REG 0x0a +#define SP_HPD_OUT BIT(6) + +/* Video Control Register 5 */ +#define SP_VID_CTRL5_REG 0x0c +#define SP_CSC_STD_SEL BIT(7) +#define SP_XVYCC_RNG_LMT BIT(6) +#define SP_RANGE_Y2R BIT(5) +#define SP_CSPACE_Y2R BIT(4) +#define SP_RGB_RNG_LMT BIT(3) +#define SP_Y_RNG_LMT BIT(2) +#define SP_RANGE_R2Y BIT(1) +#define SP_CSPACE_R2Y BIT(0) + +/* Video Control Register 6 */ +#define SP_VID_CTRL6_REG 0x0d +#define SP_TEST_PATTERN_EN BIT(7) +#define SP_VIDEO_PROCESS_EN BIT(6) +#define SP_VID_US_MODE BIT(3) +#define SP_VID_DS_MODE BIT(2) +#define SP_UP_SAMPLE BIT(1) +#define SP_DOWN_SAMPLE BIT(0) + +/* Video Control Register 8 */ +#define SP_VID_CTRL8_REG 0x0f +#define SP_VID_VRES_TH BIT(0) + +/* Total Line Status Low Byte Register */ +#define SP_TOTAL_LINE_STAL_REG 0x24 + +/* Total Line Status High Byte Register */ +#define SP_TOTAL_LINE_STAH_REG 0x25 + +/* Active Line Status Low Byte Register */ +#define SP_ACT_LINE_STAL_REG 0x26 + +/* Active Line Status High Byte Register */ +#define SP_ACT_LINE_STAH_REG 0x27 + +/* Vertical Front Porch Status Register */ +#define SP_V_F_PORCH_STA_REG 0x28 + +/* Vertical SYNC Width Status Register */ +#define SP_V_SYNC_STA_REG 0x29 + +/* Vertical Back Porch Status Register */ +#define SP_V_B_PORCH_STA_REG 0x2a + +/* Total Pixel Status Low Byte Register */ +#define SP_TOTAL_PIXEL_STAL_REG 0x2b + +/* Total Pixel Status High Byte Register */ +#define SP_TOTAL_PIXEL_STAH_REG 0x2c + +/* Active Pixel Status Low Byte Register */ +#define SP_ACT_PIXEL_STAL_REG 0x2d + +/* Active Pixel Status High Byte Register */ +#define SP_ACT_PIXEL_STAH_REG 0x2e + +/* Horizontal Front Porch Status Low Byte Register */ +#define SP_H_F_PORCH_STAL_REG 0x2f + +/* Horizontal Front Porch Statys High Byte Register */ +#define SP_H_F_PORCH_STAH_REG 0x30 + +/* Horizontal SYNC Width Status Low Byte Register */ +#define SP_H_SYNC_STAL_REG 0x31 + +/* Horizontal SYNC Width Status High Byte Register */ +#define SP_H_SYNC_STAH_REG 0x32 + +/* Horizontal Back Porch Status Low Byte Register */ +#define SP_H_B_PORCH_STAL_REG 0x33 + +/* Horizontal Back Porch Status High Byte Register */ +#define SP_H_B_PORCH_STAH_REG 0x34 + +/* InfoFrame AVI Packet DB1 Register */ +#define SP_INFOFRAME_AVI_DB1_REG 0x70 + +/* Bit Control Specific Register */ +#define SP_BIT_CTRL_SPECIFIC_REG 0x80 +#define SP_BIT_CTRL_SELECT_SHIFT 1 +#define SP_ENABLE_BIT_CTRL BIT(0) + +/* InfoFrame Audio Packet DB1 Register */ +#define SP_INFOFRAME_AUD_DB1_REG 0x83 + +/* InfoFrame MPEG Packet DB1 Register */ +#define SP_INFOFRAME_MPEG_DB1_REG 0xb0 + +/* Audio Channel Status Registers */ +#define SP_AUD_CH_STATUS_BASE 0xd0 + +/* Audio Channel Num Register 5 */ +#define SP_I2S_CHANNEL_NUM_MASK 0xe0 +# define SP_I2S_CH_NUM_1 (0x00 << 5) +# define SP_I2S_CH_NUM_2 (0x01 << 5) +# define SP_I2S_CH_NUM_3 (0x02 << 5) +# define SP_I2S_CH_NUM_4 (0x03 << 5) +# define SP_I2S_CH_NUM_5 (0x04 << 5) +# define SP_I2S_CH_NUM_6 (0x05 << 5) +# define SP_I2S_CH_NUM_7 (0x06 << 5) +# define SP_I2S_CH_NUM_8 (0x07 << 5) +#define SP_EXT_VUCP BIT(2) +#define SP_VBIT BIT(1) +#define SP_AUDIO_LAYOUT BIT(0) + +/* Analog Debug Register 2 */ +#define SP_ANALOG_DEBUG2_REG 0xdd +#define SP_FORCE_SW_OFF_BYPASS 0x20 +#define SP_XTAL_FRQ 0x1c +# define SP_XTAL_FRQ_19M2 (0x00 << 2) +# define SP_XTAL_FRQ_24M (0x01 << 2) +# define SP_XTAL_FRQ_25M (0x02 << 2) +# define SP_XTAL_FRQ_26M (0x03 << 2) +# define SP_XTAL_FRQ_27M (0x04 << 2) +# define SP_XTAL_FRQ_38M4 (0x05 << 2) +# define SP_XTAL_FRQ_52M (0x06 << 2) +#define SP_POWERON_TIME_1P5MS 0x03 + +/* Analog Control 0 Register */ +#define SP_ANALOG_CTRL0_REG 0xe1 + +/* Common Interrupt Status Register 1 */ +#define SP_COMMON_INT_STATUS_BASE (0xf1 - 1) +#define SP_PLL_LOCK_CHG 0x40 + +/* Common Interrupt Status Register 2 */ +#define SP_COMMON_INT_STATUS2 0xf2 +#define SP_HDCP_AUTH_CHG BIT(1) +#define SP_HDCP_AUTH_DONE BIT(0) + +#define SP_HDCP_LINK_CHECK_FAIL BIT(0) + +/* Common Interrupt Status Register 4 */ +#define SP_COMMON_INT_STATUS4_REG 0xf4 +#define SP_HPD_IRQ BIT(6) +#define SP_HPD_ESYNC_ERR BIT(4) +#define SP_HPD_CHG BIT(2) +#define SP_HPD_LOST BIT(1) +#define SP_HPD_PLUG BIT(0) + +/* DP Interrupt Status Register */ +#define SP_DP_INT_STATUS1_REG 0xf7 +#define SP_TRAINING_FINISH BIT(5) +#define SP_POLLING_ERR BIT(4) + +/* Common Interrupt Mask Register */ +#define SP_COMMON_INT_MASK_BASE (0xf8 - 1) + +#define SP_COMMON_INT_MASK4_REG 0xfb + +/* DP Interrupts Mask Register */ +#define SP_DP_INT_MASK1_REG 0xfe + +/* Interrupt Control Register */ +#define SP_INT_CTRL_REG 0xff + +#endif /* _ANALOGIX_I2C_TXCOMMON_H_ */
From: Icenowy Zheng icenowy@aosc.io
Some code can be shared within different DP bridges by Analogix.
Extract them to a new module.
Signed-off-by: Icenowy Zheng icenowy@aosc.io Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com --- drivers/gpu/drm/bridge/analogix/Kconfig | 4 + drivers/gpu/drm/bridge/analogix/Makefile | 2 + .../drm/bridge/analogix/analogix-anx78xx.c | 146 +-------------- .../drm/bridge/analogix/analogix-i2c-dptx.c | 169 ++++++++++++++++++ .../drm/bridge/analogix/analogix-i2c-dptx.h | 2 + 5 files changed, 178 insertions(+), 145 deletions(-) create mode 100644 drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c
diff --git a/drivers/gpu/drm/bridge/analogix/Kconfig b/drivers/gpu/drm/bridge/analogix/Kconfig index c4d343a2f04d..ed2d05c12546 100644 --- a/drivers/gpu/drm/bridge/analogix/Kconfig +++ b/drivers/gpu/drm/bridge/analogix/Kconfig @@ -1,5 +1,6 @@ config DRM_ANALOGIX_ANX78XX tristate "Analogix ANX78XX bridge" + select DRM_ANALOGIX_DP_I2C select DRM_KMS_HELPER select REGMAP_I2C help @@ -11,3 +12,6 @@ config DRM_ANALOGIX_ANX78XX config DRM_ANALOGIX_DP tristate depends on DRM + +config DRM_ANALOGIX_DP_I2C + tristate diff --git a/drivers/gpu/drm/bridge/analogix/Makefile b/drivers/gpu/drm/bridge/analogix/Makefile index ce1687e60975..2d523b67487d 100644 --- a/drivers/gpu/drm/bridge/analogix/Makefile +++ b/drivers/gpu/drm/bridge/analogix/Makefile @@ -1,3 +1,5 @@ analogix_dp-objs := analogix_dp_core.o analogix_dp_reg.o +analogix_dp_i2c-objs := analogix-i2c-dptx.o obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix_dp.o +obj-$(CONFIG_DRM_ANALOGIX_DP_I2C) += analogix_dp_i2c.o diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c index f8433c93f463..bf8291d0ddd0 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c @@ -45,8 +45,6 @@ #define I2C_IDX_RX_P1 4
#define XTAL_CLK 270 /* 27M */ -#define AUX_CH_BUFFER_SIZE 16 -#define AUX_WAIT_TIMEOUT_MS 15
static const u8 anx78xx_i2c_addresses[] = { [I2C_IDX_TX_P0] = TX_P0, @@ -109,153 +107,11 @@ static int anx78xx_clear_bits(struct regmap *map, u8 reg, u8 mask) return regmap_update_bits(map, reg, mask, 0); }
-static bool anx78xx_aux_op_finished(struct anx78xx *anx78xx) -{ - unsigned int value; - int err; - - err = regmap_read(anx78xx->map[I2C_IDX_TX_P0], SP_DP_AUX_CH_CTRL2_REG, - &value); - if (err < 0) - return false; - - return (value & SP_AUX_EN) == 0; -} - -static int anx78xx_aux_wait(struct anx78xx *anx78xx) -{ - unsigned long timeout; - unsigned int status; - int err; - - timeout = jiffies + msecs_to_jiffies(AUX_WAIT_TIMEOUT_MS) + 1; - - while (!anx78xx_aux_op_finished(anx78xx)) { - if (time_after(jiffies, timeout)) { - if (!anx78xx_aux_op_finished(anx78xx)) { - DRM_ERROR("Timed out waiting AUX to finish\n"); - return -ETIMEDOUT; - } - - break; - } - - usleep_range(1000, 2000); - } - - /* Read the AUX channel access status */ - err = regmap_read(anx78xx->map[I2C_IDX_TX_P0], SP_AUX_CH_STATUS_REG, - &status); - if (err < 0) { - DRM_ERROR("Failed to read from AUX channel: %d\n", err); - return err; - } - - if (status & SP_AUX_STATUS) { - DRM_ERROR("Failed to wait for AUX channel (status: %02x)\n", - status); - return -ETIMEDOUT; - } - - return 0; -} - -static int anx78xx_aux_address(struct anx78xx *anx78xx, unsigned int addr) -{ - int err; - - err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_AUX_ADDR_7_0_REG, - addr & 0xff); - if (err) - return err; - - err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_AUX_ADDR_15_8_REG, - (addr & 0xff00) >> 8); - if (err) - return err; - - /* - * DP AUX CH Address Register #2, only update bits[3:0] - * [7:4] RESERVED - * [3:0] AUX_ADDR[19:16], Register control AUX CH address. - */ - err = regmap_update_bits(anx78xx->map[I2C_IDX_TX_P0], - SP_AUX_ADDR_19_16_REG, - SP_AUX_ADDR_19_16_MASK, - (addr & 0xf0000) >> 16); - - if (err) - return err; - - return 0; -} - static ssize_t anx78xx_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) { struct anx78xx *anx78xx = container_of(aux, struct anx78xx, aux); - u8 ctrl1 = msg->request; - u8 ctrl2 = SP_AUX_EN; - u8 *buffer = msg->buffer; - int err; - - /* The DP AUX transmit and receive buffer has 16 bytes. */ - if (WARN_ON(msg->size > AUX_CH_BUFFER_SIZE)) - return -E2BIG; - - /* Zero-sized messages specify address-only transactions. */ - if (msg->size < 1) - ctrl2 |= SP_ADDR_ONLY; - else /* For non-zero-sized set the length field. */ - ctrl1 |= (msg->size - 1) << SP_AUX_LENGTH_SHIFT; - - if ((msg->request & DP_AUX_I2C_READ) == 0) { - /* When WRITE | MOT write values to data buffer */ - err = regmap_bulk_write(anx78xx->map[I2C_IDX_TX_P0], - SP_DP_BUF_DATA0_REG, buffer, - msg->size); - if (err) - return err; - } - - /* Write address and request */ - err = anx78xx_aux_address(anx78xx, msg->address); - if (err) - return err; - - err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], SP_DP_AUX_CH_CTRL1_REG, - ctrl1); - if (err) - return err; - - /* Start transaction */ - err = regmap_update_bits(anx78xx->map[I2C_IDX_TX_P0], - SP_DP_AUX_CH_CTRL2_REG, SP_ADDR_ONLY | - SP_AUX_EN, ctrl2); - if (err) - return err; - - err = anx78xx_aux_wait(anx78xx); - if (err) - return err; - - msg->reply = DP_AUX_I2C_REPLY_ACK; - - if ((msg->size > 0) && (msg->request & DP_AUX_I2C_READ)) { - /* Read values from data buffer */ - err = regmap_bulk_read(anx78xx->map[I2C_IDX_TX_P0], - SP_DP_BUF_DATA0_REG, buffer, - msg->size); - if (err) - return err; - } - - err = anx78xx_clear_bits(anx78xx->map[I2C_IDX_TX_P0], - SP_DP_AUX_CH_CTRL2_REG, SP_ADDR_ONLY); - if (err) - return err; - - return msg->size; + return anx_aux_transfer(anx78xx->map[I2C_IDX_TX_P0], msg); }
static int anx78xx_set_hpd(struct anx78xx *anx78xx) diff --git a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c new file mode 100644 index 000000000000..9cb30962032e --- /dev/null +++ b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2017 Icenowy Zheng icenowy@aosc.io + * + * Based on analogix-anx78xx.c, which is: + * Copyright(c) 2016, Analogix Semiconductor. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/regmap.h> + +#include <drm/drm.h> +#include <drm/drmP.h> +#include <drm/drm_dp_helper.h> + +#include "analogix-i2c-dptx.h" + +#define AUX_WAIT_TIMEOUT_MS 15 +#define AUX_CH_BUFFER_SIZE 16 + +static int anx_clear_bits(struct regmap *map, u8 reg, u8 mask) +{ + return regmap_update_bits(map, reg, mask, 0); +} + +static bool anx_aux_op_finished(struct regmap *map_dptx) +{ + unsigned int value; + int err; + + err = regmap_read(map_dptx, SP_DP_AUX_CH_CTRL2_REG, &value); + if (err < 0) + return false; + + return (value & SP_AUX_EN) == 0; +} + +static int anx_aux_wait(struct regmap *map_dptx) +{ + unsigned long timeout; + unsigned int status; + int err; + + timeout = jiffies + msecs_to_jiffies(AUX_WAIT_TIMEOUT_MS) + 1; + + while (!anx_aux_op_finished(map_dptx)) { + if (time_after(jiffies, timeout)) { + if (!anx_aux_op_finished(map_dptx)) { + DRM_ERROR("Timed out waiting AUX to finish\n"); + return -ETIMEDOUT; + } + + break; + } + + usleep_range(1000, 2000); + } + + /* Read the AUX channel access status */ + err = regmap_read(map_dptx, SP_AUX_CH_STATUS_REG, &status); + if (err < 0) { + DRM_ERROR("Failed to read from AUX channel: %d\n", err); + return err; + } + + if (status & SP_AUX_STATUS) { + DRM_ERROR("Failed to wait for AUX channel (status: %02x)\n", + status); + return -ETIMEDOUT; + } + + return 0; +} + +static int anx_aux_address(struct regmap *map_dptx, unsigned int addr) +{ + int err; + + err = regmap_write(map_dptx, SP_AUX_ADDR_7_0_REG, addr & 0xff); + if (err) + return err; + + err = regmap_write(map_dptx, SP_AUX_ADDR_15_8_REG, + (addr & 0xff00) >> 8); + if (err) + return err; + + /* + * DP AUX CH Address Register #2, only update bits[3:0] + * [7:4] RESERVED + * [3:0] AUX_ADDR[19:16], Register control AUX CH address. + */ + err = regmap_update_bits(map_dptx, SP_AUX_ADDR_19_16_REG, + SP_AUX_ADDR_19_16_MASK, + (addr & 0xf0000) >> 16); + + if (err) + return err; + + return 0; +} + +ssize_t anx_aux_transfer(struct regmap *map_dptx, struct drm_dp_aux_msg *msg) +{ + u8 ctrl1 = msg->request; + u8 ctrl2 = SP_AUX_EN; + u8 *buffer = msg->buffer; + int err; + + /* The DP AUX transmit and receive buffer has 16 bytes. */ + if (WARN_ON(msg->size > AUX_CH_BUFFER_SIZE)) + return -E2BIG; + + /* Zero-sized messages specify address-only transactions. */ + if (msg->size < 1) + ctrl2 |= SP_ADDR_ONLY; + else /* For non-zero-sized set the length field. */ + ctrl1 |= (msg->size - 1) << SP_AUX_LENGTH_SHIFT; + + if ((msg->request & DP_AUX_I2C_READ) == 0) { + /* When WRITE | MOT write values to data buffer */ + err = regmap_bulk_write(map_dptx, + SP_DP_BUF_DATA0_REG, buffer, + msg->size); + if (err) + return err; + } + + /* Write address and request */ + err = anx_aux_address(map_dptx, msg->address); + if (err) + return err; + + err = regmap_write(map_dptx, SP_DP_AUX_CH_CTRL1_REG, ctrl1); + if (err) + return err; + + /* Start transaction */ + err = regmap_update_bits(map_dptx, SP_DP_AUX_CH_CTRL2_REG, + SP_ADDR_ONLY | SP_AUX_EN, ctrl2); + if (err) + return err; + + err = anx_aux_wait(map_dptx); + if (err) + return err; + + msg->reply = DP_AUX_I2C_REPLY_ACK; + + if ((msg->size > 0) && (msg->request & DP_AUX_I2C_READ)) { + /* Read values from data buffer */ + err = regmap_bulk_read(map_dptx, + SP_DP_BUF_DATA0_REG, buffer, + msg->size); + if (err) + return err; + } + + err = anx_clear_bits(map_dptx, SP_DP_AUX_CH_CTRL2_REG, SP_ADDR_ONLY); + if (err) + return err; + + return msg->size; +} +EXPORT_SYMBOL(anx_aux_transfer); + +MODULE_DESCRIPTION("Analogix DisplayPort Transmitter common code"); +MODULE_AUTHOR("Icenowy Zheng icenowy@aosc.io"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h index bc0831b127bf..c2ca854613a0 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h +++ b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h @@ -245,4 +245,6 @@ /* DP AUX Buffer Data Registers */ #define SP_DP_BUF_DATA0_REG 0xf0
+ssize_t anx_aux_transfer(struct regmap *map_dptx, struct drm_dp_aux_msg *msg); + #endif
From: Icenowy Zheng icenowy@aosc.io
The ANX6345 is an ultra-low power DisplayPort/eDP transmitter designed for portable devices.
Add a binding document for it.
Signed-off-by: Icenowy Zheng icenowy@aosc.io Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com --- .../bindings/display/bridge/anx6345.txt | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/bridge/anx6345.txt
diff --git a/Documentation/devicetree/bindings/display/bridge/anx6345.txt b/Documentation/devicetree/bindings/display/bridge/anx6345.txt new file mode 100644 index 000000000000..e79a11348d11 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/anx6345.txt @@ -0,0 +1,56 @@ +Analogix ANX6345 eDP Transmitter +-------------------------------- + +The ANX6345 is an ultra-low power Full-HD eDP transmitter designed for +portable devices. + +Required properties: + + - compatible : "analogix,anx6345" + - reg : I2C address of the device + - reset-gpios : Which GPIO to use for reset + +Optional properties: + + - dvdd12-supply : Regulator for 1.2V digital core power. + - dvdd25-supply : Regulator for 2.5V digital core power. + - Video ports for RGB input and eDP output using the DT bindings + defined in [1] + +[1]: Documentation/devicetree/bindings/media/video-interfaces.txt + +Example: + +anx6345: anx6345@38 { + compatible = "analogix,anx6345"; + reg = <0x38>; + reset-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* PD24 */ + dvdd25-supply = <®_dldo2>; + dvdd12-supply = <®_fldo1>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + anx6345_in: port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + anx6345_in_tcon0: endpoint@0 { + reg = <0>; + remote-endpoint = <&tcon0_out_anx6345>; + }; + }; + + anx6345_out: port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + anx6345_out_panel: endpoint@0 { + reg = <0>; + remote-endpoint = <&panel_in_edp>; + }; + }; + }; +};
On Thu, 14 Feb 2019 21:09:50 -0800, Vasily Khoruzhick wrote:
From: Icenowy Zheng icenowy@aosc.io
The ANX6345 is an ultra-low power DisplayPort/eDP transmitter designed for portable devices.
Add a binding document for it.
Signed-off-by: Icenowy Zheng icenowy@aosc.io Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
.../bindings/display/bridge/anx6345.txt | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/bridge/anx6345.txt
Reviewed-by: Rob Herring robh@kernel.org
From: Icenowy Zheng icenowy@aosc.io
The ANX6345 is an ultra-low power DisplayPower/eDP transmitter designed for portable devices. This driver adds initial support for RGB to eDP mode, without HPD and interrupts.
This is a configuration usually seen in eDP applications.
Signed-off-by: Icenowy Zheng icenowy@aosc.io Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com --- drivers/gpu/drm/bridge/analogix/Kconfig | 11 + drivers/gpu/drm/bridge/analogix/Makefile | 1 + .../drm/bridge/analogix/analogix-anx6345.c | 845 ++++++++++++++++++ .../drm/bridge/analogix/analogix-i2c-dptx.c | 2 +- .../drm/bridge/analogix/analogix-i2c-dptx.h | 8 + .../bridge/analogix/analogix-i2c-txcommon.h | 3 + 6 files changed, 869 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
diff --git a/drivers/gpu/drm/bridge/analogix/Kconfig b/drivers/gpu/drm/bridge/analogix/Kconfig index ed2d05c12546..3c6ec535d361 100644 --- a/drivers/gpu/drm/bridge/analogix/Kconfig +++ b/drivers/gpu/drm/bridge/analogix/Kconfig @@ -1,3 +1,14 @@ +config DRM_ANALOGIX_ANX6345 + tristate "Analogix ANX6345 bridge" + select DRM_ANALOGIX_DP_I2C + select DRM_KMS_HELPER + select REGMAP_I2C + help + ANX6345 is an ultra-low Full-HD DisplayPort/eDP + transmitter designed for portable devices. The + ANX6345 transforms the LVTTL RGB output of an + application processor to eDP or DisplayPort. + config DRM_ANALOGIX_ANX78XX tristate "Analogix ANX78XX bridge" select DRM_ANALOGIX_DP_I2C diff --git a/drivers/gpu/drm/bridge/analogix/Makefile b/drivers/gpu/drm/bridge/analogix/Makefile index 2d523b67487d..12fed7b04e1e 100644 --- a/drivers/gpu/drm/bridge/analogix/Makefile +++ b/drivers/gpu/drm/bridge/analogix/Makefile @@ -1,5 +1,6 @@ analogix_dp-objs := analogix_dp_core.o analogix_dp_reg.o analogix_dp_i2c-objs := analogix-i2c-dptx.o +obj-$(CONFIG_DRM_ANALOGIX_ANX6345) += analogix-anx6345.o obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix_dp.o obj-$(CONFIG_DRM_ANALOGIX_DP_I2C) += analogix_dp_i2c.o diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c new file mode 100644 index 000000000000..6098e245e074 --- /dev/null +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c @@ -0,0 +1,845 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) Icenowy Zheng icenowy@aosc.io + * Based on analogix-anx6345.c, which is: + * Copyright(c) 2016, Analogix Semiconductor. + */ +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <linux/types.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> + +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_dp_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> + +#include "analogix-i2c-dptx.h" +#include "analogix-i2c-txcommon.h" + +#define I2C_NUM_ADDRESSES 2 +#define I2C_IDX_DPTX 0 +#define I2C_IDX_TXCOM 1 + +#define XTAL_CLK 270 /* 27M */ + +#define POLL_DELAY 50000 /* us */ +#define POLL_TIMEOUT 5000000 /* us */ + +static const u8 anx6345_i2c_addresses[] = { + [I2C_IDX_DPTX] = ANALOGIX_I2C_DPTX, + [I2C_IDX_TXCOM] = ANALOGIX_I2C_TXCOMMON, +}; + +struct anx6345_platform_data { + struct regulator *dvdd12; + struct regulator *dvdd25; + struct gpio_desc *gpiod_reset; +}; + +struct anx6345 { + struct drm_dp_aux aux; + struct drm_bridge bridge; + struct i2c_client *client; + struct edid *edid; + struct drm_connector connector; + struct drm_dp_link link; + struct drm_panel *panel; + struct anx6345_platform_data pdata; + struct mutex lock; + + /* + * I2C Slave addresses of ANX6345 are mapped as DPTX and SYS + */ + struct i2c_client *i2c_clients[I2C_NUM_ADDRESSES]; + struct regmap *map[I2C_NUM_ADDRESSES]; + + u16 chipid; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + + bool powered; +}; + +static inline struct anx6345 *connector_to_anx6345(struct drm_connector *c) +{ + return container_of(c, struct anx6345, connector); +} + +static inline struct anx6345 *bridge_to_anx6345(struct drm_bridge *bridge) +{ + return container_of(bridge, struct anx6345, bridge); +} + +static int anx6345_set_bits(struct regmap *map, u8 reg, u8 mask) +{ + return regmap_update_bits(map, reg, mask, mask); +} + +static int anx6345_clear_bits(struct regmap *map, u8 reg, u8 mask) +{ + return regmap_update_bits(map, reg, mask, 0); +} + +static ssize_t anx6345_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct anx6345 *anx6345 = container_of(aux, struct anx6345, aux); + + return anx_aux_transfer(anx6345->map[I2C_IDX_DPTX], msg); +} + +static int anx6345_dp_link_training(struct anx6345 *anx6345) +{ + unsigned int value; + u8 dp_bw; + int err; + + err = anx6345_clear_bits(anx6345->map[I2C_IDX_TXCOM], + SP_POWERDOWN_CTRL_REG, + SP_TOTAL_PD); + if (err) + return err; + + err = drm_dp_dpcd_readb(&anx6345->aux, DP_MAX_LINK_RATE, &dp_bw); + if (err < 0) + return err; + + switch (dp_bw) { + case DP_LINK_BW_1_62: + case DP_LINK_BW_2_7: + break; + + default: + DRM_DEBUG_KMS("DP bandwidth (%#02x) not supported\n", dp_bw); + return -EINVAL; + } + + err = anx6345_set_bits(anx6345->map[I2C_IDX_TXCOM], SP_VID_CTRL1_REG, + SP_VIDEO_MUTE); + if (err) + return err; + + err = anx6345_clear_bits(anx6345->map[I2C_IDX_TXCOM], + SP_VID_CTRL1_REG, SP_VIDEO_EN); + if (err) + return err; + + /* Get DPCD info */ + err = drm_dp_dpcd_read(&anx6345->aux, DP_DPCD_REV, + &anx6345->dpcd, DP_RECEIVER_CAP_SIZE); + if (err < 0) { + DRM_ERROR("Failed to read DPCD: %d\n", err); + return err; + } + + /* Clear channel x SERDES power down */ + err = anx6345_clear_bits(anx6345->map[I2C_IDX_DPTX], + SP_DP_ANALOG_POWER_DOWN_REG, SP_CH0_PD); + if (err) + return err; + + /* Check link capabilities */ + err = drm_dp_link_probe(&anx6345->aux, &anx6345->link); + if (err < 0) { + DRM_ERROR("Failed to probe link capabilities: %d\n", err); + return err; + } + + /* Power up the sink */ + err = drm_dp_link_power_up(&anx6345->aux, &anx6345->link); + if (err < 0) { + DRM_ERROR("Failed to power up DisplayPort link: %d\n", err); + return err; + } + + /* Possibly enable downspread on the sink */ + err = regmap_write(anx6345->map[I2C_IDX_DPTX], + SP_DP_DOWNSPREAD_CTRL1_REG, 0); + if (err) + return err; + + if (anx6345->dpcd[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5) { + DRM_DEBUG("Enable downspread on the sink\n"); + /* 4000PPM */ + err = regmap_write(anx6345->map[I2C_IDX_DPTX], + SP_DP_DOWNSPREAD_CTRL1_REG, 8); + if (err) + return err; + + err = drm_dp_dpcd_writeb(&anx6345->aux, DP_DOWNSPREAD_CTRL, + DP_SPREAD_AMP_0_5); + if (err < 0) + return err; + } else { + err = drm_dp_dpcd_writeb(&anx6345->aux, DP_DOWNSPREAD_CTRL, 0); + if (err < 0) + return err; + } + + /* Set the lane count and the link rate on the sink */ + if (drm_dp_enhanced_frame_cap(anx6345->dpcd)) + err = anx6345_set_bits(anx6345->map[I2C_IDX_DPTX], + SP_DP_SYSTEM_CTRL_BASE + 4, + SP_ENHANCED_MODE); + else + err = anx6345_clear_bits(anx6345->map[I2C_IDX_DPTX], + SP_DP_SYSTEM_CTRL_BASE + 4, + SP_ENHANCED_MODE); + if (err) + return err; + + value = drm_dp_link_rate_to_bw_code(anx6345->link.rate); + err = regmap_write(anx6345->map[I2C_IDX_DPTX], + SP_DP_MAIN_LINK_BW_SET_REG, value); + if (err) + return err; + + err = regmap_write(anx6345->map[I2C_IDX_DPTX], + SP_DP_LANE_COUNT_SET_REG, anx6345->link.num_lanes); + if (err) + return err; + + err = drm_dp_link_configure(&anx6345->aux, &anx6345->link); + if (err < 0) { + DRM_ERROR("Failed to configure DisplayPort link: %d\n", err); + return err; + } + + /* Start training on the source */ + err = regmap_write(anx6345->map[I2C_IDX_DPTX], SP_DP_LT_CTRL_REG, + SP_LT_EN); + if (err) + return err; + + err = regmap_read_poll_timeout(anx6345->map[I2C_IDX_DPTX], + SP_DP_LT_CTRL_REG, + value, !(value & SP_DP_LT_INPROGRESS), + POLL_DELAY, POLL_TIMEOUT); + if (err) + return err; + + return 0; +} + +static int anx6345_tx_initialization(struct anx6345 *anx6345) +{ + int err, i; + + /* FIXME: hardcode color depth now */ + err = regmap_write(anx6345->map[I2C_IDX_TXCOM], SP_VID_CTRL2_REG, + SP_IN_BPC_6BIT << SP_IN_BPC_SHIFT); + if (err) + return err; + + err = regmap_write(anx6345->map[I2C_IDX_DPTX], SP_DP_PLL_CTRL_REG, 0); + if (err) + return err; + + err = regmap_write(anx6345->map[I2C_IDX_TXCOM], + SP_ANALOG_DEBUG1_REG, 0); + if (err) + return err; + + err = regmap_write(anx6345->map[I2C_IDX_DPTX], + SP_DP_LINK_DEBUG_CTRL_REG, + SP_NEW_PRBS7 | SP_M_VID_DEBUG); + if (err) + return err; + + err = regmap_write(anx6345->map[I2C_IDX_DPTX], + SP_DP_ANALOG_POWER_DOWN_REG, 0); + if (err) + return err; + + /* Force HPD */ + err = anx6345_set_bits(anx6345->map[I2C_IDX_DPTX], + SP_DP_SYSTEM_CTRL_BASE + 3, + SP_HPD_FORCE | SP_HPD_CTRL); + if (err) + return err; + + for (i = 0; i < 4; i++) { + /* 4 lanes */ + err = regmap_write(anx6345->map[I2C_IDX_DPTX], + SP_DP_LANE0_LT_CTRL_REG + i, 0); + if (err) + return err; + } + + /* Reset AUX */ + err = anx6345_set_bits(anx6345->map[I2C_IDX_TXCOM], + SP_RESET_CTRL2_REG, SP_AUX_RST); + if (err) + return err; + + err = anx6345_clear_bits(anx6345->map[I2C_IDX_TXCOM], + SP_RESET_CTRL2_REG, SP_AUX_RST); + if (err) + return err; + + err = anx6345_dp_link_training(anx6345); + if (err) + return err; + + return 0; +} + +static void anx6345_poweron(struct anx6345 *anx6345) +{ + struct anx6345_platform_data *pdata = &anx6345->pdata; + int err; + + if (WARN_ON(anx6345->powered)) + return; + + if (pdata->dvdd12) { + err = regulator_enable(pdata->dvdd12); + if (err) { + DRM_ERROR("Failed to enable DVDD12 regulator: %d\n", + err); + return; + } + + usleep_range(1000, 2000); + } + + if (pdata->dvdd25) { + err = regulator_enable(pdata->dvdd25); + if (err) { + DRM_ERROR("Failed to enable DVDD25 regulator: %d\n", + err); + return; + } + + usleep_range(5000, 10000); + } + + if (anx6345->panel) + drm_panel_prepare(anx6345->panel); + + gpiod_set_value_cansleep(pdata->gpiod_reset, 0); + usleep_range(1000, 2000); + + gpiod_set_value_cansleep(pdata->gpiod_reset, 1); + + /* Power on registers module */ + anx6345_set_bits(anx6345->map[I2C_IDX_TXCOM], SP_POWERDOWN_CTRL_REG, + SP_HDCP_PD | SP_AUDIO_PD | SP_VIDEO_PD | SP_LINK_PD); + anx6345_clear_bits(anx6345->map[I2C_IDX_TXCOM], SP_POWERDOWN_CTRL_REG, + SP_REGISTER_PD | SP_TOTAL_PD); + + anx6345->powered = true; +} + +static void anx6345_poweroff(struct anx6345 *anx6345) +{ + struct anx6345_platform_data *pdata = &anx6345->pdata; + int err; + + if (WARN_ON(!anx6345->powered)) + return; + + gpiod_set_value_cansleep(pdata->gpiod_reset, 1); + usleep_range(1000, 2000); + + if (anx6345->panel) + drm_panel_unprepare(anx6345->panel); + + if (pdata->dvdd25) { + err = regulator_disable(pdata->dvdd25); + if (err) { + DRM_ERROR("Failed to disable DVDD25 regulator: %d\n", + err); + return; + } + + usleep_range(5000, 10000); + } + + if (pdata->dvdd12) { + err = regulator_disable(pdata->dvdd12); + if (err) { + DRM_ERROR("Failed to disable DVDD12 regulator: %d\n", + err); + return; + } + + usleep_range(1000, 2000); + } + + anx6345->powered = false; +} + +static int anx6345_start(struct anx6345 *anx6345) +{ + int err; + + if (!anx6345->powered) + anx6345_poweron(anx6345); + + /* Power on needed modules */ + err = anx6345_clear_bits(anx6345->map[I2C_IDX_TXCOM], + SP_POWERDOWN_CTRL_REG, + SP_VIDEO_PD | SP_LINK_PD); + + err = anx6345_tx_initialization(anx6345); + if (err) { + DRM_ERROR("Failed transmitter initialization: %d\n", err); + goto err_poweroff; + } + + /* + * This delay seems to help keep the hardware in a good state. Without + * it, there are times where it fails silently. + */ + usleep_range(10000, 15000); + + return 0; + +err_poweroff: + DRM_ERROR("Failed DisplayPort transmitter initialization: %d\n", err); + anx6345_poweroff(anx6345); + + return err; +} + +static int anx6345_init_pdata(struct anx6345 *anx6345) +{ + struct anx6345_platform_data *pdata = &anx6345->pdata; + struct device *dev = &anx6345->client->dev; + + /* 1.2V digital core power regulator */ + pdata->dvdd12 = devm_regulator_get(dev, "dvdd12"); + if (IS_ERR(pdata->dvdd12)) { + DRM_ERROR("DVDD12 regulator not found\n"); + return PTR_ERR(pdata->dvdd12); + } + + /* 2.5V digital core power regulator */ + pdata->dvdd25 = devm_regulator_get(dev, "dvdd25"); + if (IS_ERR(pdata->dvdd25)) { + DRM_ERROR("DVDD25 regulator not found\n"); + return PTR_ERR(pdata->dvdd25); + } + + /* GPIO for chip reset */ + pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + + return PTR_ERR_OR_ZERO(pdata->gpiod_reset); +} + +static int anx6345_config_dp_output(struct anx6345 *anx6345) +{ + int err; + + err = anx6345_clear_bits(anx6345->map[I2C_IDX_TXCOM], SP_VID_CTRL1_REG, + SP_VIDEO_MUTE); + if (err) + return err; + + /* Enable DP output */ + err = anx6345_set_bits(anx6345->map[I2C_IDX_TXCOM], SP_VID_CTRL1_REG, + SP_VIDEO_EN); + if (err) + return err; + + /* Force stream valid */ + err = anx6345_set_bits(anx6345->map[I2C_IDX_DPTX], + SP_DP_SYSTEM_CTRL_BASE + 3, + SP_STRM_FORCE | SP_STRM_CTRL); + if (err) + return err; + + return 0; +} + +static int anx6345_get_downstream_info(struct anx6345 *anx6345) +{ + u8 value; + int err; + + err = drm_dp_dpcd_readb(&anx6345->aux, DP_SINK_COUNT, &value); + if (err < 0) { + DRM_ERROR("Get sink count failed %d\n", err); + return err; + } + + if (!DP_GET_SINK_COUNT(value)) { + DRM_ERROR("Downstream disconnected\n"); + return -EIO; + } + + return 0; +} + +static int anx6345_get_modes(struct drm_connector *connector) +{ + struct anx6345 *anx6345 = connector_to_anx6345(connector); + int err, num_modes = 0; + + if (WARN_ON(!anx6345->powered)) + return 0; + + mutex_lock(&anx6345->lock); + + if (!anx6345->edid) { + err = anx6345_get_downstream_info(anx6345); + if (err) { + DRM_ERROR("Failed to get downstream info: %d\n", err); + goto unlock; + } + + anx6345->edid = drm_get_edid(connector, &anx6345->aux.ddc); + if (!anx6345->edid) + DRM_ERROR("Failed to read EDID from panel\n"); + + err = drm_connector_update_edid_property(connector, + anx6345->edid); + if (err) { + DRM_ERROR("Failed to update EDID property: %d\n", err); + goto unlock; + } + } + + num_modes += drm_add_edid_modes(connector, anx6345->edid); + +unlock: + mutex_unlock(&anx6345->lock); + + if (!num_modes && anx6345->panel) + num_modes += drm_panel_get_modes(anx6345->panel); + + return num_modes; +} + +static const struct drm_connector_helper_funcs anx6345_connector_helper_funcs = { + .get_modes = anx6345_get_modes, +}; + +static enum drm_connector_status anx6345_detect(struct drm_connector *connector, + bool force) +{ + return connector_status_connected; +} + +static void +anx6345_connector_destroy(struct drm_connector *connector) +{ + struct anx6345 *anx6345 = connector_to_anx6345(connector); + + if (anx6345->panel) + drm_panel_detach(anx6345->panel); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs anx6345_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = anx6345_detect, + .destroy = anx6345_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int anx6345_bridge_attach(struct drm_bridge *bridge) +{ + struct anx6345 *anx6345 = bridge_to_anx6345(bridge); + int err; + + if (!bridge->encoder) { + DRM_ERROR("Parent encoder object not found"); + return -ENODEV; + } + + /* Register aux channel */ + anx6345->aux.name = "DP-AUX"; + anx6345->aux.dev = &anx6345->client->dev; + anx6345->aux.transfer = anx6345_aux_transfer; + + err = drm_dp_aux_register(&anx6345->aux); + if (err < 0) { + DRM_ERROR("Failed to register aux channel: %d\n", err); + return err; + } + + err = drm_connector_init(bridge->dev, &anx6345->connector, + &anx6345_connector_funcs, + DRM_MODE_CONNECTOR_eDP); + if (err) { + DRM_ERROR("Failed to initialize connector: %d\n", err); + return err; + } + + drm_connector_helper_add(&anx6345->connector, + &anx6345_connector_helper_funcs); + + err = drm_connector_register(&anx6345->connector); + if (err) { + DRM_ERROR("Failed to register connector: %d\n", err); + return err; + } + + anx6345->connector.polled = DRM_CONNECTOR_POLL_HPD; + + err = drm_connector_attach_encoder(&anx6345->connector, + bridge->encoder); + if (err) { + DRM_ERROR("Failed to link up connector to encoder: %d\n", err); + return err; + } + + if (anx6345->panel) { + err = drm_panel_attach(anx6345->panel, &anx6345->connector); + if (err) { + DRM_ERROR("Failed to attach panel: %d\n", err); + return err; + } + } + + return 0; +} + +static bool anx6345_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return false; + + /* Max 1200p at 5.4 Ghz, one lane */ + if (mode->clock > 154000) + return false; + + return true; +} + +static void anx6345_bridge_disable(struct drm_bridge *bridge) +{ + struct anx6345 *anx6345 = bridge_to_anx6345(bridge); + + /* Power off all modules except configuration registers access */ + anx6345_set_bits(anx6345->map[I2C_IDX_TXCOM], SP_POWERDOWN_CTRL_REG, + SP_HDCP_PD | SP_AUDIO_PD | SP_VIDEO_PD | SP_LINK_PD); + if (anx6345->panel) + drm_panel_disable(anx6345->panel); +} + +static void anx6345_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct anx6345 *anx6345 = bridge_to_anx6345(bridge); + + if (WARN_ON(!anx6345->powered)) + return; +} + +static void anx6345_bridge_enable(struct drm_bridge *bridge) +{ + struct anx6345 *anx6345 = bridge_to_anx6345(bridge); + int err; + + if (anx6345->panel) + drm_panel_enable(anx6345->panel); + + err = anx6345_start(anx6345); + if (err) { + DRM_ERROR("Failed to initialize: %d\n", err); + return; + } + + err = anx6345_config_dp_output(anx6345); + if (err) + DRM_ERROR("Failed to enable DP output: %d\n", err); +} + +static const struct drm_bridge_funcs anx6345_bridge_funcs = { + .attach = anx6345_bridge_attach, + .mode_fixup = anx6345_bridge_mode_fixup, + .disable = anx6345_bridge_disable, + .mode_set = anx6345_bridge_mode_set, + .enable = anx6345_bridge_enable, +}; + +static void unregister_i2c_dummy_clients(struct anx6345 *anx6345) +{ + unsigned int i; + + for (i = 1; i < ARRAY_SIZE(anx6345->i2c_clients); i++) + if (anx6345->i2c_clients[i] && + anx6345->i2c_clients[i]->addr != anx6345->client->addr) + i2c_unregister_device(anx6345->i2c_clients[i]); +} + +static const struct regmap_config anx6345_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, +}; + +static const u16 anx6345_chipid_list[] = { + 0x6345, +}; + +static int anx6345_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct anx6345 *anx6345; + struct anx6345_platform_data *pdata; + unsigned int i, idl, idh, version; + bool found = false; + int err; + + anx6345 = devm_kzalloc(&client->dev, sizeof(*anx6345), GFP_KERNEL); + if (!anx6345) + return -ENOMEM; + + pdata = &anx6345->pdata; + + mutex_init(&anx6345->lock); + + anx6345->bridge.of_node = client->dev.of_node; + + anx6345->client = client; + i2c_set_clientdata(client, anx6345); + + err = drm_of_find_panel_or_bridge(client->dev.of_node, 1, 0, + &anx6345->panel, NULL); + if (err == -EPROBE_DEFER) + return err; + + if (err) + DRM_DEBUG("No panel found\n"); + + err = anx6345_init_pdata(anx6345); + if (err) { + DRM_ERROR("Failed to initialize pdata: %d\n", err); + return err; + } + + /* Map slave addresses of ANX6345 */ + for (i = 0; i < I2C_NUM_ADDRESSES; i++) { + if (anx6345_i2c_addresses[i] >> 1 != client->addr) + anx6345->i2c_clients[i] = i2c_new_dummy(client->adapter, + anx6345_i2c_addresses[i] >> 1); + else + anx6345->i2c_clients[i] = client; + + if (!anx6345->i2c_clients[i]) { + err = -ENOMEM; + DRM_ERROR("Failed to reserve I2C bus %02x\n", + anx6345_i2c_addresses[i]); + goto err_unregister_i2c; + } + + anx6345->map[i] = devm_regmap_init_i2c(anx6345->i2c_clients[i], + &anx6345_regmap_config); + if (IS_ERR(anx6345->map[i])) { + err = PTR_ERR(anx6345->map[i]); + DRM_ERROR("Failed regmap initialization %02x\n", + anx6345_i2c_addresses[i]); + goto err_unregister_i2c; + } + } + + /* Look for supported chip ID */ + anx6345_poweron(anx6345); + + err = regmap_read(anx6345->map[I2C_IDX_TXCOM], SP_DEVICE_IDL_REG, + &idl); + if (err) + goto err_poweroff; + + err = regmap_read(anx6345->map[I2C_IDX_TXCOM], SP_DEVICE_IDH_REG, + &idh); + if (err) + goto err_poweroff; + + anx6345->chipid = (u8)idl | ((u8)idh << 8); + + err = regmap_read(anx6345->map[I2C_IDX_TXCOM], SP_DEVICE_VERSION_REG, + &version); + if (err) + goto err_poweroff; + + for (i = 0; i < ARRAY_SIZE(anx6345_chipid_list); i++) { + if (anx6345->chipid == anx6345_chipid_list[i]) { + DRM_INFO("Found ANX%x (ver. %d) eDP Transmitter\n", + anx6345->chipid, version); + found = true; + break; + } + } + + if (!found) { + DRM_ERROR("ANX%x (ver. %d) not supported by this driver\n", + anx6345->chipid, version); + err = -ENODEV; + goto err_poweroff; + } + + anx6345->bridge.funcs = &anx6345_bridge_funcs; + + drm_bridge_add(&anx6345->bridge); + + return 0; + +err_poweroff: + anx6345_poweroff(anx6345); + +err_unregister_i2c: + unregister_i2c_dummy_clients(anx6345); + return err; +} + +static int anx6345_i2c_remove(struct i2c_client *client) +{ + struct anx6345 *anx6345 = i2c_get_clientdata(client); + + drm_bridge_remove(&anx6345->bridge); + + unregister_i2c_dummy_clients(anx6345); + + kfree(anx6345->edid); + + return 0; +} + +static const struct i2c_device_id anx6345_id[] = { + { "anx6345", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, anx6345_id); + +static const struct of_device_id anx6345_match_table[] = { + { .compatible = "analogix,anx6345", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, anx6345_match_table); + +static struct i2c_driver anx6345_driver = { + .driver = { + .name = "anx6345", + .of_match_table = of_match_ptr(anx6345_match_table), + }, + .probe = anx6345_i2c_probe, + .remove = anx6345_i2c_remove, + .id_table = anx6345_id, +}; +module_i2c_driver(anx6345_driver); + +MODULE_DESCRIPTION("ANX6345 eDP Transmitter driver"); +MODULE_AUTHOR("Icenowy Zheng icenowy@aosc.io"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c index 9cb30962032e..53b0e73d6a24 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c @@ -117,7 +117,7 @@ ssize_t anx_aux_transfer(struct regmap *map_dptx, struct drm_dp_aux_msg *msg) else /* For non-zero-sized set the length field. */ ctrl1 |= (msg->size - 1) << SP_AUX_LENGTH_SHIFT;
- if ((msg->request & DP_AUX_I2C_READ) == 0) { + if ((msg->size > 0) && ((msg->request & DP_AUX_I2C_READ) == 0)) { /* When WRITE | MOT write values to data buffer */ err = regmap_bulk_write(map_dptx, SP_DP_BUF_DATA0_REG, buffer, diff --git a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h index c2ca854613a0..b29a0b3bc23c 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h +++ b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h @@ -75,7 +75,11 @@ #define SP_CHA_STA BIT(2) /* Bits for DP System Control Register 3 */ #define SP_HPD_STATUS BIT(6) +#define SP_HPD_FORCE BIT(5) +#define SP_HPD_CTRL BIT(4) #define SP_STRM_VALID BIT(2) +#define SP_STRM_FORCE BIT(1) +#define SP_STRM_CTRL BIT(0) /* Bits for DP System Control Register 4 */ #define SP_ENHANCED_MODE BIT(3)
@@ -120,6 +124,9 @@ #define SP_LINK_BW_SET_MASK 0x1f #define SP_INITIAL_SLIM_M_AUD_SEL BIT(5)
+/* DP Lane Count Setting Register */ +#define SP_DP_LANE_COUNT_SET_REG 0xa1 + /* DP Training Pattern Set Register */ #define SP_DP_TRAINING_PATTERN_SET_REG 0xa2
@@ -133,6 +140,7 @@
/* DP Link Training Control Register */ #define SP_DP_LT_CTRL_REG 0xa8 +#define SP_DP_LT_INPROGRESS 0x80 #define SP_LT_ERROR_TYPE_MASK 0x70 # define SP_LT_NO_ERROR 0x00 # define SP_LT_AUX_WRITE_ERROR 0x01 diff --git a/drivers/gpu/drm/bridge/analogix/analogix-i2c-txcommon.h b/drivers/gpu/drm/bridge/analogix/analogix-i2c-txcommon.h index 7d683573e970..480c98a225b1 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-i2c-txcommon.h +++ b/drivers/gpu/drm/bridge/analogix/analogix-i2c-txcommon.h @@ -183,6 +183,9 @@ #define SP_VBIT BIT(1) #define SP_AUDIO_LAYOUT BIT(0)
+/* Analog Debug Register 1 */ +#define SP_ANALOG_DEBUG1_REG 0xdc + /* Analog Debug Register 2 */ #define SP_ANALOG_DEBUG2_REG 0xdd #define SP_FORCE_SW_OFF_BYPASS 0x20
On Thu, Feb 14, 2019 at 09:09:51PM -0800, Vasily Khoruzhick wrote:
From: Icenowy Zheng icenowy@aosc.io
The ANX6345 is an ultra-low power DisplayPower/eDP transmitter designed for portable devices. This driver adds initial support for RGB to eDP mode, without HPD and interrupts.
This is a configuration usually seen in eDP applications.
Signed-off-by: Icenowy Zheng icenowy@aosc.io Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
drivers/gpu/drm/bridge/analogix/Kconfig | 11 + drivers/gpu/drm/bridge/analogix/Makefile | 1 + .../drm/bridge/analogix/analogix-anx6345.c | 845 ++++++++++++++++++ .../drm/bridge/analogix/analogix-i2c-dptx.c | 2 +- .../drm/bridge/analogix/analogix-i2c-dptx.h | 8 + .../bridge/analogix/analogix-i2c-txcommon.h | 3 + 6 files changed, 869 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
[ ... ]
+static int anx6345_start(struct anx6345 *anx6345) +{
- int err;
- if (!anx6345->powered)
anx6345_poweron(anx6345);
- /* Power on needed modules */
- err = anx6345_clear_bits(anx6345->map[I2C_IDX_TXCOM],
SP_POWERDOWN_CTRL_REG,
SP_VIDEO_PD | SP_LINK_PD);
- err = anx6345_tx_initialization(anx6345);
- if (err) {
DRM_ERROR("Failed transmitter initialization: %d\n", err);
goto err_poweroff;
You can move the whole err_poweroff section from below here and drop the goto.
- }
- /*
* This delay seems to help keep the hardware in a good state. Without
* it, there are times where it fails silently.
*/
- usleep_range(10000, 15000);
- return 0;
+err_poweroff:
- DRM_ERROR("Failed DisplayPort transmitter initialization: %d\n", err);
- anx6345_poweroff(anx6345);
- return err;
+}
[ ... ]
On Fri, Feb 15, 2019 at 12:23 AM Priit Laes plaes@plaes.org wrote:
err = anx6345_tx_initialization(anx6345);
if (err) {
DRM_ERROR("Failed transmitter initialization: %d\n", err);
goto err_poweroff;
You can move the whole err_poweroff section from below here and drop the goto.
Thanks, will do.
On 15.02.2019 06:09, Vasily Khoruzhick wrote:
From: Icenowy Zheng icenowy@aosc.io
The ANX6345 is an ultra-low power DisplayPower/eDP transmitter designed for portable devices. This driver adds initial support for RGB to eDP mode, without HPD and interrupts.
This is a configuration usually seen in eDP applications.
Signed-off-by: Icenowy Zheng icenowy@aosc.io Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
drivers/gpu/drm/bridge/analogix/Kconfig | 11 + drivers/gpu/drm/bridge/analogix/Makefile | 1 + .../drm/bridge/analogix/analogix-anx6345.c | 845 ++++++++++++++++++ .../drm/bridge/analogix/analogix-i2c-dptx.c | 2 +- .../drm/bridge/analogix/analogix-i2c-dptx.h | 8 + .../bridge/analogix/analogix-i2c-txcommon.h | 3 + 6 files changed, 869 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
diff --git a/drivers/gpu/drm/bridge/analogix/Kconfig b/drivers/gpu/drm/bridge/analogix/Kconfig index ed2d05c12546..3c6ec535d361 100644 --- a/drivers/gpu/drm/bridge/analogix/Kconfig +++ b/drivers/gpu/drm/bridge/analogix/Kconfig @@ -1,3 +1,14 @@ +config DRM_ANALOGIX_ANX6345
- tristate "Analogix ANX6345 bridge"
- select DRM_ANALOGIX_DP_I2C
- select DRM_KMS_HELPER
- select REGMAP_I2C
- help
ANX6345 is an ultra-low Full-HD DisplayPort/eDP
transmitter designed for portable devices. The
ANX6345 transforms the LVTTL RGB output of an
application processor to eDP or DisplayPort.
config DRM_ANALOGIX_ANX78XX tristate "Analogix ANX78XX bridge" select DRM_ANALOGIX_DP_I2C diff --git a/drivers/gpu/drm/bridge/analogix/Makefile b/drivers/gpu/drm/bridge/analogix/Makefile index 2d523b67487d..12fed7b04e1e 100644 --- a/drivers/gpu/drm/bridge/analogix/Makefile +++ b/drivers/gpu/drm/bridge/analogix/Makefile @@ -1,5 +1,6 @@ analogix_dp-objs := analogix_dp_core.o analogix_dp_reg.o analogix_dp_i2c-objs := analogix-i2c-dptx.o +obj-$(CONFIG_DRM_ANALOGIX_ANX6345) += analogix-anx6345.o obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix_dp.o obj-$(CONFIG_DRM_ANALOGIX_DP_I2C) += analogix_dp_i2c.o diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c new file mode 100644 index 000000000000..6098e245e074 --- /dev/null +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c @@ -0,0 +1,845 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(c) Icenowy Zheng icenowy@aosc.io
- Based on analogix-anx6345.c, which is:
- Copyright(c) 2016, Analogix Semiconductor.
- */
+#include <linux/delay.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_gpio.h>
Do you need this header?
+#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <linux/types.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h>
+#include <drm/drmP.h>
drmP.h is/should be deprecated.
+#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_dp_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h>
+#include "analogix-i2c-dptx.h" +#include "analogix-i2c-txcommon.h"
+#define I2C_NUM_ADDRESSES 2 +#define I2C_IDX_DPTX 0 +#define I2C_IDX_TXCOM 1
+#define XTAL_CLK 270 /* 27M */
+#define POLL_DELAY 50000 /* us */ +#define POLL_TIMEOUT 5000000 /* us */
+static const u8 anx6345_i2c_addresses[] = {
- [I2C_IDX_DPTX] = ANALOGIX_I2C_DPTX,
- [I2C_IDX_TXCOM] = ANALOGIX_I2C_TXCOMMON,
+};
+struct anx6345_platform_data {
- struct regulator *dvdd12;
- struct regulator *dvdd25;
- struct gpio_desc *gpiod_reset;
+};
Why do you need this struct, why just do not embed it's fields directly into struct anx6345 ?
+struct anx6345 {
- struct drm_dp_aux aux;
- struct drm_bridge bridge;
- struct i2c_client *client;
- struct edid *edid;
- struct drm_connector connector;
- struct drm_dp_link link;
- struct drm_panel *panel;
- struct anx6345_platform_data pdata;
- struct mutex lock;
- /*
* I2C Slave addresses of ANX6345 are mapped as DPTX and SYS
*/
- struct i2c_client *i2c_clients[I2C_NUM_ADDRESSES];
- struct regmap *map[I2C_NUM_ADDRESSES];
- u16 chipid;
- u8 dpcd[DP_RECEIVER_CAP_SIZE];
- bool powered;
+};
+static inline struct anx6345 *connector_to_anx6345(struct drm_connector *c) +{
- return container_of(c, struct anx6345, connector);
+}
+static inline struct anx6345 *bridge_to_anx6345(struct drm_bridge *bridge) +{
- return container_of(bridge, struct anx6345, bridge);
+}
+static int anx6345_set_bits(struct regmap *map, u8 reg, u8 mask) +{
- return regmap_update_bits(map, reg, mask, mask);
+}
+static int anx6345_clear_bits(struct regmap *map, u8 reg, u8 mask) +{
- return regmap_update_bits(map, reg, mask, 0);
+}
+static ssize_t anx6345_aux_transfer(struct drm_dp_aux *aux,
struct drm_dp_aux_msg *msg)
+{
- struct anx6345 *anx6345 = container_of(aux, struct anx6345, aux);
- return anx_aux_transfer(anx6345->map[I2C_IDX_DPTX], msg);
+}
+static int anx6345_dp_link_training(struct anx6345 *anx6345) +{
- unsigned int value;
- u8 dp_bw;
- int err;
- err = anx6345_clear_bits(anx6345->map[I2C_IDX_TXCOM],
SP_POWERDOWN_CTRL_REG,
SP_TOTAL_PD);
- if (err)
return err;
- err = drm_dp_dpcd_readb(&anx6345->aux, DP_MAX_LINK_RATE, &dp_bw);
- if (err < 0)
return err;
- switch (dp_bw) {
- case DP_LINK_BW_1_62:
- case DP_LINK_BW_2_7:
break;
- default:
DRM_DEBUG_KMS("DP bandwidth (%#02x) not supported\n", dp_bw);
return -EINVAL;
- }
- err = anx6345_set_bits(anx6345->map[I2C_IDX_TXCOM], SP_VID_CTRL1_REG,
SP_VIDEO_MUTE);
- if (err)
return err;
- err = anx6345_clear_bits(anx6345->map[I2C_IDX_TXCOM],
SP_VID_CTRL1_REG, SP_VIDEO_EN);
- if (err)
return err;
- /* Get DPCD info */
- err = drm_dp_dpcd_read(&anx6345->aux, DP_DPCD_REV,
&anx6345->dpcd, DP_RECEIVER_CAP_SIZE);
- if (err < 0) {
DRM_ERROR("Failed to read DPCD: %d\n", err);
return err;
- }
- /* Clear channel x SERDES power down */
- err = anx6345_clear_bits(anx6345->map[I2C_IDX_DPTX],
SP_DP_ANALOG_POWER_DOWN_REG, SP_CH0_PD);
- if (err)
return err;
- /* Check link capabilities */
- err = drm_dp_link_probe(&anx6345->aux, &anx6345->link);
- if (err < 0) {
DRM_ERROR("Failed to probe link capabilities: %d\n", err);
return err;
- }
- /* Power up the sink */
- err = drm_dp_link_power_up(&anx6345->aux, &anx6345->link);
- if (err < 0) {
DRM_ERROR("Failed to power up DisplayPort link: %d\n", err);
return err;
- }
- /* Possibly enable downspread on the sink */
- err = regmap_write(anx6345->map[I2C_IDX_DPTX],
SP_DP_DOWNSPREAD_CTRL1_REG, 0);
- if (err)
return err;
- if (anx6345->dpcd[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5) {
DRM_DEBUG("Enable downspread on the sink\n");
/* 4000PPM */
err = regmap_write(anx6345->map[I2C_IDX_DPTX],
SP_DP_DOWNSPREAD_CTRL1_REG, 8);
if (err)
return err;
err = drm_dp_dpcd_writeb(&anx6345->aux, DP_DOWNSPREAD_CTRL,
DP_SPREAD_AMP_0_5);
if (err < 0)
return err;
- } else {
err = drm_dp_dpcd_writeb(&anx6345->aux, DP_DOWNSPREAD_CTRL, 0);
if (err < 0)
return err;
- }
- /* Set the lane count and the link rate on the sink */
- if (drm_dp_enhanced_frame_cap(anx6345->dpcd))
err = anx6345_set_bits(anx6345->map[I2C_IDX_DPTX],
SP_DP_SYSTEM_CTRL_BASE + 4,
SP_ENHANCED_MODE);
- else
err = anx6345_clear_bits(anx6345->map[I2C_IDX_DPTX],
SP_DP_SYSTEM_CTRL_BASE + 4,
SP_ENHANCED_MODE);
- if (err)
return err;
- value = drm_dp_link_rate_to_bw_code(anx6345->link.rate);
- err = regmap_write(anx6345->map[I2C_IDX_DPTX],
SP_DP_MAIN_LINK_BW_SET_REG, value);
- if (err)
return err;
- err = regmap_write(anx6345->map[I2C_IDX_DPTX],
SP_DP_LANE_COUNT_SET_REG, anx6345->link.num_lanes);
- if (err)
return err;
- err = drm_dp_link_configure(&anx6345->aux, &anx6345->link);
- if (err < 0) {
DRM_ERROR("Failed to configure DisplayPort link: %d\n", err);
return err;
- }
- /* Start training on the source */
- err = regmap_write(anx6345->map[I2C_IDX_DPTX], SP_DP_LT_CTRL_REG,
SP_LT_EN);
- if (err)
return err;
- err = regmap_read_poll_timeout(anx6345->map[I2C_IDX_DPTX],
SP_DP_LT_CTRL_REG,
value, !(value & SP_DP_LT_INPROGRESS),
POLL_DELAY, POLL_TIMEOUT);
- if (err)
return err;
- return 0;
+}
+static int anx6345_tx_initialization(struct anx6345 *anx6345) +{
- int err, i;
- /* FIXME: hardcode color depth now */
- err = regmap_write(anx6345->map[I2C_IDX_TXCOM], SP_VID_CTRL2_REG,
SP_IN_BPC_6BIT << SP_IN_BPC_SHIFT);
- if (err)
return err;
- err = regmap_write(anx6345->map[I2C_IDX_DPTX], SP_DP_PLL_CTRL_REG, 0);
- if (err)
return err;
- err = regmap_write(anx6345->map[I2C_IDX_TXCOM],
SP_ANALOG_DEBUG1_REG, 0);
- if (err)
return err;
- err = regmap_write(anx6345->map[I2C_IDX_DPTX],
SP_DP_LINK_DEBUG_CTRL_REG,
SP_NEW_PRBS7 | SP_M_VID_DEBUG);
- if (err)
return err;
- err = regmap_write(anx6345->map[I2C_IDX_DPTX],
SP_DP_ANALOG_POWER_DOWN_REG, 0);
- if (err)
return err;
- /* Force HPD */
- err = anx6345_set_bits(anx6345->map[I2C_IDX_DPTX],
SP_DP_SYSTEM_CTRL_BASE + 3,
SP_HPD_FORCE | SP_HPD_CTRL);
- if (err)
return err;
- for (i = 0; i < 4; i++) {
/* 4 lanes */
err = regmap_write(anx6345->map[I2C_IDX_DPTX],
SP_DP_LANE0_LT_CTRL_REG + i, 0);
if (err)
return err;
- }
- /* Reset AUX */
- err = anx6345_set_bits(anx6345->map[I2C_IDX_TXCOM],
SP_RESET_CTRL2_REG, SP_AUX_RST);
- if (err)
return err;
- err = anx6345_clear_bits(anx6345->map[I2C_IDX_TXCOM],
SP_RESET_CTRL2_REG, SP_AUX_RST);
- if (err)
return err;
- err = anx6345_dp_link_training(anx6345);
- if (err)
return err;
- return 0;
+}
+static void anx6345_poweron(struct anx6345 *anx6345) +{
- struct anx6345_platform_data *pdata = &anx6345->pdata;
- int err;
- if (WARN_ON(anx6345->powered))
return;
It should not happen, you can remove this warn.
- if (pdata->dvdd12) {
If regulators are required this will be never null.
err = regulator_enable(pdata->dvdd12);
if (err) {
DRM_ERROR("Failed to enable DVDD12 regulator: %d\n",
err);
return;
}
usleep_range(1000, 2000);
- }
- if (pdata->dvdd25) {
ditto
err = regulator_enable(pdata->dvdd25);
if (err) {
DRM_ERROR("Failed to enable DVDD25 regulator: %d\n",
err);
return;
}
usleep_range(5000, 10000);
- }
- if (anx6345->panel)
drm_panel_prepare(anx6345->panel);
again, here and below: panel is never null, check can be removed.
- gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
- usleep_range(1000, 2000);
- gpiod_set_value_cansleep(pdata->gpiod_reset, 1);
Start/stop sequence seems odd regarding reset gpio:
1. In probe reset is set to low, in poweroff to high - incosistent.
2. If in case of disabled device reset should be 0, there is no point to set it again to 0 three lines above.
3. I suspect in dts reset gpio should be declared as active_low, and the logic in the driver should be reverted, in power off it should be set to high, in power on it should be lowered (logically).
- /* Power on registers module */
- anx6345_set_bits(anx6345->map[I2C_IDX_TXCOM], SP_POWERDOWN_CTRL_REG,
SP_HDCP_PD | SP_AUDIO_PD | SP_VIDEO_PD | SP_LINK_PD);
- anx6345_clear_bits(anx6345->map[I2C_IDX_TXCOM], SP_POWERDOWN_CTRL_REG,
SP_REGISTER_PD | SP_TOTAL_PD);
- anx6345->powered = true;
+}
+static void anx6345_poweroff(struct anx6345 *anx6345) +{
- struct anx6345_platform_data *pdata = &anx6345->pdata;
- int err;
- if (WARN_ON(!anx6345->powered))
return;
- gpiod_set_value_cansleep(pdata->gpiod_reset, 1);
- usleep_range(1000, 2000);
- if (anx6345->panel)
drm_panel_unprepare(anx6345->panel);
- if (pdata->dvdd25) {
err = regulator_disable(pdata->dvdd25);
if (err) {
DRM_ERROR("Failed to disable DVDD25 regulator: %d\n",
err);
return;
}
usleep_range(5000, 10000);
- }
- if (pdata->dvdd12) {
err = regulator_disable(pdata->dvdd12);
if (err) {
DRM_ERROR("Failed to disable DVDD12 regulator: %d\n",
err);
return;
}
usleep_range(1000, 2000);
- }
- anx6345->powered = false;
+}
+static int anx6345_start(struct anx6345 *anx6345) +{
- int err;
- if (!anx6345->powered)
anx6345_poweron(anx6345);
- /* Power on needed modules */
- err = anx6345_clear_bits(anx6345->map[I2C_IDX_TXCOM],
SP_POWERDOWN_CTRL_REG,
SP_VIDEO_PD | SP_LINK_PD);
- err = anx6345_tx_initialization(anx6345);
- if (err) {
DRM_ERROR("Failed transmitter initialization: %d\n", err);
goto err_poweroff;
- }
- /*
* This delay seems to help keep the hardware in a good state. Without
* it, there are times where it fails silently.
*/
- usleep_range(10000, 15000);
- return 0;
+err_poweroff:
- DRM_ERROR("Failed DisplayPort transmitter initialization: %d\n", err);
redundant message
- anx6345_poweroff(anx6345);
- return err;
+}
+static int anx6345_init_pdata(struct anx6345 *anx6345) +{
- struct anx6345_platform_data *pdata = &anx6345->pdata;
- struct device *dev = &anx6345->client->dev;
- /* 1.2V digital core power regulator */
- pdata->dvdd12 = devm_regulator_get(dev, "dvdd12");
- if (IS_ERR(pdata->dvdd12)) {
DRM_ERROR("DVDD12 regulator not found\n");
return PTR_ERR(pdata->dvdd12);
- }
- /* 2.5V digital core power regulator */
- pdata->dvdd25 = devm_regulator_get(dev, "dvdd25");
- if (IS_ERR(pdata->dvdd25)) {
DRM_ERROR("DVDD25 regulator not found\n");
return PTR_ERR(pdata->dvdd25);
- }
- /* GPIO for chip reset */
- pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
- return PTR_ERR_OR_ZERO(pdata->gpiod_reset);
+}
+static int anx6345_config_dp_output(struct anx6345 *anx6345) +{
- int err;
- err = anx6345_clear_bits(anx6345->map[I2C_IDX_TXCOM], SP_VID_CTRL1_REG,
SP_VIDEO_MUTE);
- if (err)
return err;
- /* Enable DP output */
- err = anx6345_set_bits(anx6345->map[I2C_IDX_TXCOM], SP_VID_CTRL1_REG,
SP_VIDEO_EN);
- if (err)
return err;
- /* Force stream valid */
- err = anx6345_set_bits(anx6345->map[I2C_IDX_DPTX],
SP_DP_SYSTEM_CTRL_BASE + 3,
SP_STRM_FORCE | SP_STRM_CTRL);
- if (err)
return err;
- return 0;
+}
+static int anx6345_get_downstream_info(struct anx6345 *anx6345) +{
- u8 value;
- int err;
- err = drm_dp_dpcd_readb(&anx6345->aux, DP_SINK_COUNT, &value);
- if (err < 0) {
DRM_ERROR("Get sink count failed %d\n", err);
The rule of thumb I heard is that if you start message capitalized you should end with dot. Since I do not know if it is enforced in kernel I leave the decision up to you.
return err;
- }
- if (!DP_GET_SINK_COUNT(value)) {
DRM_ERROR("Downstream disconnected\n");
return -EIO;
- }
- return 0;
+}
+static int anx6345_get_modes(struct drm_connector *connector) +{
- struct anx6345 *anx6345 = connector_to_anx6345(connector);
- int err, num_modes = 0;
- if (WARN_ON(!anx6345->powered))
return 0;
- mutex_lock(&anx6345->lock);
- if (!anx6345->edid) {
err = anx6345_get_downstream_info(anx6345);
if (err) {
DRM_ERROR("Failed to get downstream info: %d\n", err);
goto unlock;
}
anx6345->edid = drm_get_edid(connector, &anx6345->aux.ddc);
if (!anx6345->edid)
DRM_ERROR("Failed to read EDID from panel\n");
err = drm_connector_update_edid_property(connector,
anx6345->edid);
if (err) {
DRM_ERROR("Failed to update EDID property: %d\n", err);
goto unlock;
}
- }
- num_modes += drm_add_edid_modes(connector, anx6345->edid);
+unlock:
- mutex_unlock(&anx6345->lock);
- if (!num_modes && anx6345->panel)
num_modes += drm_panel_get_modes(anx6345->panel);
- return num_modes;
+}
+static const struct drm_connector_helper_funcs anx6345_connector_helper_funcs = {
- .get_modes = anx6345_get_modes,
+};
+static enum drm_connector_status anx6345_detect(struct drm_connector *connector,
bool force)
+{
- return connector_status_connected;
+}
+static void +anx6345_connector_destroy(struct drm_connector *connector) +{
- struct anx6345 *anx6345 = connector_to_anx6345(connector);
- if (anx6345->panel)
drm_panel_detach(anx6345->panel);
- drm_connector_cleanup(connector);
+}
+static const struct drm_connector_funcs anx6345_connector_funcs = {
- .fill_modes = drm_helper_probe_single_connector_modes,
- .detect = anx6345_detect,
- .destroy = anx6345_connector_destroy,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+static int anx6345_bridge_attach(struct drm_bridge *bridge) +{
- struct anx6345 *anx6345 = bridge_to_anx6345(bridge);
- int err;
- if (!bridge->encoder) {
DRM_ERROR("Parent encoder object not found");
return -ENODEV;
- }
- /* Register aux channel */
- anx6345->aux.name = "DP-AUX";
- anx6345->aux.dev = &anx6345->client->dev;
- anx6345->aux.transfer = anx6345_aux_transfer;
- err = drm_dp_aux_register(&anx6345->aux);
- if (err < 0) {
DRM_ERROR("Failed to register aux channel: %d\n", err);
return err;
- }
- err = drm_connector_init(bridge->dev, &anx6345->connector,
&anx6345_connector_funcs,
DRM_MODE_CONNECTOR_eDP);
- if (err) {
DRM_ERROR("Failed to initialize connector: %d\n", err);
return err;
- }
- drm_connector_helper_add(&anx6345->connector,
&anx6345_connector_helper_funcs);
- err = drm_connector_register(&anx6345->connector);
- if (err) {
DRM_ERROR("Failed to register connector: %d\n", err);
return err;
- }
- anx6345->connector.polled = DRM_CONNECTOR_POLL_HPD;
- err = drm_connector_attach_encoder(&anx6345->connector,
bridge->encoder);
- if (err) {
DRM_ERROR("Failed to link up connector to encoder: %d\n", err);
return err;
- }
- if (anx6345->panel) {
err = drm_panel_attach(anx6345->panel, &anx6345->connector);
if (err) {
DRM_ERROR("Failed to attach panel: %d\n", err);
return err;
}
- }
- return 0;
+}
+static bool anx6345_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
+{
- if (mode->flags & DRM_MODE_FLAG_INTERLACE)
return false;
- /* Max 1200p at 5.4 Ghz, one lane */
- if (mode->clock > 154000)
return false;
These checks should be in mode_valid callback.
- return true;
+}
+static void anx6345_bridge_disable(struct drm_bridge *bridge) +{
- struct anx6345 *anx6345 = bridge_to_anx6345(bridge);
- /* Power off all modules except configuration registers access */
- anx6345_set_bits(anx6345->map[I2C_IDX_TXCOM], SP_POWERDOWN_CTRL_REG,
SP_HDCP_PD | SP_AUDIO_PD | SP_VIDEO_PD | SP_LINK_PD);
- if (anx6345->panel)
drm_panel_disable(anx6345->panel);
+}
+static void anx6345_bridge_mode_set(struct drm_bridge *bridge,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
+{
- struct anx6345 *anx6345 = bridge_to_anx6345(bridge);
- if (WARN_ON(!anx6345->powered))
return;
+}
+static void anx6345_bridge_enable(struct drm_bridge *bridge) +{
- struct anx6345 *anx6345 = bridge_to_anx6345(bridge);
- int err;
- if (anx6345->panel)
drm_panel_enable(anx6345->panel);
- err = anx6345_start(anx6345);
- if (err) {
DRM_ERROR("Failed to initialize: %d\n", err);
return;
- }
- err = anx6345_config_dp_output(anx6345);
- if (err)
DRM_ERROR("Failed to enable DP output: %d\n", err);
+}
+static const struct drm_bridge_funcs anx6345_bridge_funcs = {
- .attach = anx6345_bridge_attach,
- .mode_fixup = anx6345_bridge_mode_fixup,
- .disable = anx6345_bridge_disable,
- .mode_set = anx6345_bridge_mode_set,
- .enable = anx6345_bridge_enable,
+};
+static void unregister_i2c_dummy_clients(struct anx6345 *anx6345) +{
- unsigned int i;
- for (i = 1; i < ARRAY_SIZE(anx6345->i2c_clients); i++)
if (anx6345->i2c_clients[i] &&
anx6345->i2c_clients[i]->addr != anx6345->client->addr)
i2c_unregister_device(anx6345->i2c_clients[i]);
+}
+static const struct regmap_config anx6345_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = 0xff,
+};
+static const u16 anx6345_chipid_list[] = {
- 0x6345,
+};
+static int anx6345_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
+{
- struct anx6345 *anx6345;
- struct anx6345_platform_data *pdata;
- unsigned int i, idl, idh, version;
- bool found = false;
- int err;
- anx6345 = devm_kzalloc(&client->dev, sizeof(*anx6345), GFP_KERNEL);
- if (!anx6345)
return -ENOMEM;
- pdata = &anx6345->pdata;
- mutex_init(&anx6345->lock);
- anx6345->bridge.of_node = client->dev.of_node;
- anx6345->client = client;
- i2c_set_clientdata(client, anx6345);
- err = drm_of_find_panel_or_bridge(client->dev.of_node, 1, 0,
&anx6345->panel, NULL);
- if (err == -EPROBE_DEFER)
return err;
- if (err)
DRM_DEBUG("No panel found\n");
- err = anx6345_init_pdata(anx6345);
- if (err) {
DRM_ERROR("Failed to initialize pdata: %d\n", err);
return err;
- }
- /* Map slave addresses of ANX6345 */
- for (i = 0; i < I2C_NUM_ADDRESSES; i++) {
if (anx6345_i2c_addresses[i] >> 1 != client->addr)
anx6345->i2c_clients[i] = i2c_new_dummy(client->adapter,
anx6345_i2c_addresses[i] >> 1);
else
anx6345->i2c_clients[i] = client;
I see this contredanse is copy/pasted from anx78*, but it looks quite complicated. As I understand there are two i2c addresses, why we cannot assume one address is for control interfaces and another
is dummy? It would simplify the code here and in other places.
if (!anx6345->i2c_clients[i]) {
err = -ENOMEM;
DRM_ERROR("Failed to reserve I2C bus %02x\n",
anx6345_i2c_addresses[i]);
goto err_unregister_i2c;
}
anx6345->map[i] = devm_regmap_init_i2c(anx6345->i2c_clients[i],
&anx6345_regmap_config);
if (IS_ERR(anx6345->map[i])) {
err = PTR_ERR(anx6345->map[i]);
DRM_ERROR("Failed regmap initialization %02x\n",
anx6345_i2c_addresses[i]);
goto err_unregister_i2c;
}
- }
- /* Look for supported chip ID */
- anx6345_poweron(anx6345);
- err = regmap_read(anx6345->map[I2C_IDX_TXCOM], SP_DEVICE_IDL_REG,
&idl);
- if (err)
goto err_poweroff;
- err = regmap_read(anx6345->map[I2C_IDX_TXCOM], SP_DEVICE_IDH_REG,
&idh);
- if (err)
goto err_poweroff;
- anx6345->chipid = (u8)idl | ((u8)idh << 8);
- err = regmap_read(anx6345->map[I2C_IDX_TXCOM], SP_DEVICE_VERSION_REG,
&version);
- if (err)
goto err_poweroff;
- for (i = 0; i < ARRAY_SIZE(anx6345_chipid_list); i++) {
if (anx6345->chipid == anx6345_chipid_list[i]) {
DRM_INFO("Found ANX%x (ver. %d) eDP Transmitter\n",
anx6345->chipid, version);
found = true;
break;
}
- }
- if (!found) {
DRM_ERROR("ANX%x (ver. %d) not supported by this driver\n",
anx6345->chipid, version);
err = -ENODEV;
goto err_poweroff;
- }
As I see chip becomes powered forever, is it OK? Usually it should be powered only when pipeline starts, and powered-off after pipeline stops.
Regards
Andrzej
- anx6345->bridge.funcs = &anx6345_bridge_funcs;
- drm_bridge_add(&anx6345->bridge);
- return 0;
+err_poweroff:
- anx6345_poweroff(anx6345);
+err_unregister_i2c:
- unregister_i2c_dummy_clients(anx6345);
- return err;
+}
+static int anx6345_i2c_remove(struct i2c_client *client) +{
- struct anx6345 *anx6345 = i2c_get_clientdata(client);
- drm_bridge_remove(&anx6345->bridge);
- unregister_i2c_dummy_clients(anx6345);
- kfree(anx6345->edid);
- return 0;
+}
+static const struct i2c_device_id anx6345_id[] = {
- { "anx6345", 0 },
- { /* sentinel */ }
+}; +MODULE_DEVICE_TABLE(i2c, anx6345_id);
+static const struct of_device_id anx6345_match_table[] = {
- { .compatible = "analogix,anx6345", },
- { /* sentinel */ },
+}; +MODULE_DEVICE_TABLE(of, anx6345_match_table);
+static struct i2c_driver anx6345_driver = {
- .driver = {
.name = "anx6345",
.of_match_table = of_match_ptr(anx6345_match_table),
},
- .probe = anx6345_i2c_probe,
- .remove = anx6345_i2c_remove,
- .id_table = anx6345_id,
+}; +module_i2c_driver(anx6345_driver);
+MODULE_DESCRIPTION("ANX6345 eDP Transmitter driver"); +MODULE_AUTHOR("Icenowy Zheng icenowy@aosc.io"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c index 9cb30962032e..53b0e73d6a24 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c @@ -117,7 +117,7 @@ ssize_t anx_aux_transfer(struct regmap *map_dptx, struct drm_dp_aux_msg *msg) else /* For non-zero-sized set the length field. */ ctrl1 |= (msg->size - 1) << SP_AUX_LENGTH_SHIFT;
- if ((msg->request & DP_AUX_I2C_READ) == 0) {
- if ((msg->size > 0) && ((msg->request & DP_AUX_I2C_READ) == 0)) { /* When WRITE | MOT write values to data buffer */ err = regmap_bulk_write(map_dptx, SP_DP_BUF_DATA0_REG, buffer,
diff --git a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h index c2ca854613a0..b29a0b3bc23c 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h +++ b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.h @@ -75,7 +75,11 @@ #define SP_CHA_STA BIT(2) /* Bits for DP System Control Register 3 */ #define SP_HPD_STATUS BIT(6) +#define SP_HPD_FORCE BIT(5) +#define SP_HPD_CTRL BIT(4) #define SP_STRM_VALID BIT(2) +#define SP_STRM_FORCE BIT(1) +#define SP_STRM_CTRL BIT(0) /* Bits for DP System Control Register 4 */ #define SP_ENHANCED_MODE BIT(3)
@@ -120,6 +124,9 @@ #define SP_LINK_BW_SET_MASK 0x1f #define SP_INITIAL_SLIM_M_AUD_SEL BIT(5)
+/* DP Lane Count Setting Register */ +#define SP_DP_LANE_COUNT_SET_REG 0xa1
/* DP Training Pattern Set Register */ #define SP_DP_TRAINING_PATTERN_SET_REG 0xa2
@@ -133,6 +140,7 @@
/* DP Link Training Control Register */ #define SP_DP_LT_CTRL_REG 0xa8 +#define SP_DP_LT_INPROGRESS 0x80 #define SP_LT_ERROR_TYPE_MASK 0x70 # define SP_LT_NO_ERROR 0x00 # define SP_LT_AUX_WRITE_ERROR 0x01 diff --git a/drivers/gpu/drm/bridge/analogix/analogix-i2c-txcommon.h b/drivers/gpu/drm/bridge/analogix/analogix-i2c-txcommon.h index 7d683573e970..480c98a225b1 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-i2c-txcommon.h +++ b/drivers/gpu/drm/bridge/analogix/analogix-i2c-txcommon.h @@ -183,6 +183,9 @@ #define SP_VBIT BIT(1) #define SP_AUDIO_LAYOUT BIT(0)
+/* Analog Debug Register 1 */ +#define SP_ANALOG_DEBUG1_REG 0xdc
/* Analog Debug Register 2 */ #define SP_ANALOG_DEBUG2_REG 0xdd #define SP_FORCE_SW_OFF_BYPASS 0x20
On Fri, Feb 15, 2019 at 1:13 AM Andrzej Hajda a.hajda@samsung.com wrote:
Hi Andrzej,
Thanks for review!
+#include <linux/of_gpio.h>
Do you need this header?
I'll drop it.
+#include <drm/drmP.h>
drmP.h is/should be deprecated.
Same here
+struct anx6345_platform_data {
struct regulator *dvdd12;
struct regulator *dvdd25;
struct gpio_desc *gpiod_reset;
+};
Why do you need this struct, why just do not embed it's fields directly into struct anx6345 ?
OK, I'll embed it into struct anx6345
if (WARN_ON(anx6345->powered))
return;
It should not happen, you can remove this warn.
OK
if (pdata->dvdd12) {
If regulators are required this will be never null.
Right, and regulator subsystem will return dummy regulator if it's missing in dts. I'll remove redundant checks.
if (pdata->dvdd25) {
ditto
OK
if (anx6345->panel)
drm_panel_prepare(anx6345->panel);
again, here and below: panel is never null, check can be removed.
That's not true, panel is optional. It can be DP connector, not a panel.
gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
usleep_range(1000, 2000);
gpiod_set_value_cansleep(pdata->gpiod_reset, 1);
Start/stop sequence seems odd regarding reset gpio:
In probe reset is set to low, in poweroff to high - incosistent.
If in case of disabled device reset should be 0, there is no point to
set it again to 0 three lines above.
- I suspect in dts reset gpio should be declared as active_low, and the
logic in the driver should be reverted, in power off it should be set to high, in power on it should be lowered (logically).
OK, I'll look into it.
+err_poweroff:
DRM_ERROR("Failed DisplayPort transmitter initialization: %d\n", err);
redundant message
OK, will drop.
DRM_ERROR("Get sink count failed %d\n", err);
The rule of thumb I heard is that if you start message capitalized you should end with dot. Since I do not know if it is enforced in kernel I leave the decision up to you.
I grepped DRM_ERROR in driver/gpu/drm and they do exactly the same as here. So I'll just keep it as is for consistency.
+static bool anx6345_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
+{
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
return false;
/* Max 1200p at 5.4 Ghz, one lane */
if (mode->clock > 154000)
return false;
These checks should be in mode_valid callback.
OK
/* Map slave addresses of ANX6345 */
for (i = 0; i < I2C_NUM_ADDRESSES; i++) {
if (anx6345_i2c_addresses[i] >> 1 != client->addr)
anx6345->i2c_clients[i] = i2c_new_dummy(client->adapter,
anx6345_i2c_addresses[i] >> 1);
else
anx6345->i2c_clients[i] = client;
I see this contredanse is copy/pasted from anx78*, but it looks quite complicated. As I understand there are two i2c addresses, why we cannot assume one address is for control interfaces and another is dummy? It would simplify the code here and in other places.
Sorry, I don't get you, could you elaborate? Note that anx6345 uses both addresses, i2c_new_dummy() just registers new i2c device bound to a dummy driver and it's supposed to be used for devices that consume more than one i2c address.
if (!found) {
DRM_ERROR("ANX%x (ver. %d) not supported by this driver\n",
anx6345->chipid, version);
err = -ENODEV;
goto err_poweroff;
}
As I see chip becomes powered forever, is it OK? Usually it should be powered only when pipeline starts, and powered-off after pipeline stops.
I'll look into how hard it would be to implement but personally I think it's OK for now. We can add more sophisticated power management once this driver is merged.
On 15.02.2019 20:36, Vasily Khoruzhick wrote:
On Fri, Feb 15, 2019 at 1:13 AM Andrzej Hajda a.hajda@samsung.com wrote:
Hi Andrzej,
Thanks for review!
+#include <linux/of_gpio.h>
Do you need this header?
I'll drop it.
+#include <drm/drmP.h>
drmP.h is/should be deprecated.
Same here
+struct anx6345_platform_data {
struct regulator *dvdd12;
struct regulator *dvdd25;
struct gpio_desc *gpiod_reset;
+};
Why do you need this struct, why just do not embed it's fields directly into struct anx6345 ?
OK, I'll embed it into struct anx6345
if (WARN_ON(anx6345->powered))
return;
It should not happen, you can remove this warn.
OK
if (pdata->dvdd12) {
If regulators are required this will be never null.
Right, and regulator subsystem will return dummy regulator if it's missing in dts. I'll remove redundant checks.
if (pdata->dvdd25) {
ditto
OK
if (anx6345->panel)
drm_panel_prepare(anx6345->panel);
again, here and below: panel is never null, check can be removed.
That's not true, panel is optional. It can be DP connector, not a panel.
gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
usleep_range(1000, 2000);
gpiod_set_value_cansleep(pdata->gpiod_reset, 1);
Start/stop sequence seems odd regarding reset gpio:
In probe reset is set to low, in poweroff to high - incosistent.
If in case of disabled device reset should be 0, there is no point to
set it again to 0 three lines above.
- I suspect in dts reset gpio should be declared as active_low, and the
logic in the driver should be reverted, in power off it should be set to high, in power on it should be lowered (logically).
OK, I'll look into it.
+err_poweroff:
DRM_ERROR("Failed DisplayPort transmitter initialization: %d\n", err);
redundant message
OK, will drop.
DRM_ERROR("Get sink count failed %d\n", err);
The rule of thumb I heard is that if you start message capitalized you should end with dot. Since I do not know if it is enforced in kernel I leave the decision up to you.
I grepped DRM_ERROR in driver/gpu/drm and they do exactly the same as here. So I'll just keep it as is for consistency.
+static bool anx6345_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
+{
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
return false;
/* Max 1200p at 5.4 Ghz, one lane */
if (mode->clock > 154000)
return false;
These checks should be in mode_valid callback.
OK
/* Map slave addresses of ANX6345 */
for (i = 0; i < I2C_NUM_ADDRESSES; i++) {
if (anx6345_i2c_addresses[i] >> 1 != client->addr)
anx6345->i2c_clients[i] = i2c_new_dummy(client->adapter,
anx6345_i2c_addresses[i] >> 1);
else
anx6345->i2c_clients[i] = client;
I see this contredanse is copy/pasted from anx78*, but it looks quite complicated. As I understand there are two i2c addresses, why we cannot assume one address is for control interfaces and another is dummy? It would simplify the code here and in other places.
Sorry, I don't get you, could you elaborate? Note that anx6345 uses both addresses, i2c_new_dummy() just registers new i2c device bound to a dummy driver and it's supposed to be used for devices that consume more than one i2c address.
My idea was to assume that ANALOGIX_I2C_DPTX is the main address, ie. address which should be set in dts in device node reg property.
Other addresses should be registered as dummy devices during probe - simple loop without conditionals, without redundant fields in anx6345 context - i2c_clients, client.
I do not insist on this change but I suggest as it should simplify the code.
And moreover, you can consider removing direction bit from i2c addresses, it could be also confusing and against i2c kernel api.
if (!found) {
DRM_ERROR("ANX%x (ver. %d) not supported by this driver\n",
anx6345->chipid, version);
err = -ENODEV;
goto err_poweroff;
}
As I see chip becomes powered forever, is it OK? Usually it should be powered only when pipeline starts, and powered-off after pipeline stops.
I'll look into how hard it would be to implement but personally I think it's OK for now. We can add more sophisticated power management once this driver is merged.
But the rule is every resource allocated/set during lifetime of the driver should be dropped on driver removal, so please do it at least in remove callback.
Regards
Andrzej
Clock rate check that was added in commit bb43d40d7c83 ("drm/sun4i: rgb: Validate the clock rate") prevents some panel and bridges from working with sun4i driver.
Unfortunately, dotclock frequency for some modes are not achievable on sunxi hardware, and there's a slight deviation in rate returned by clk_round_rate(), so they fail this check.
Experiments show that panels and bridges work fine with this slight deviation, e.g. Pinebook that uses ANX6345 bridge with 768p eDP panel requests 73 MHz, gets 72.296MHz instead (0.96% difference) and works just fine.
This patch adds DT property to disable strict clock rate check
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com --- .../devicetree/bindings/display/sunxi/sun4i-drm.txt | 2 ++ drivers/gpu/drm/sun4i/sun4i_rgb.c | 5 +++++ drivers/gpu/drm/sun4i/sun4i_tcon.c | 3 +++ drivers/gpu/drm/sun4i/sun4i_tcon.h | 1 + 4 files changed, 11 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index f426bdb42f18..18c8b053a28d 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -63,6 +63,8 @@ Required properties: Documentation/devicetree/bindings/media/video-interfaces.txt. The first port should be the input endpoint. The second should be the output, usually to an HDMI connector. + - no-strict-clock-check: don't reject timings if exact dot clock can't be + reached.
DWC HDMI TX Encoder ------------------- diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index f4a22689eb54..b392d4b1ebd3 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -94,7 +94,12 @@ static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
tcon->dclk_min_div = 6; tcon->dclk_max_div = 127; + + if (tcon->no_strict_clock_check) + return MODE_OK; + rounded_rate = clk_round_rate(tcon->dclk, rate); + if (rounded_rate < rate) return MODE_CLOCK_LOW;
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index cf45d0f940f9..f5f7f479332b 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -1108,6 +1108,9 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, tcon->id = engine->id; tcon->quirks = of_device_get_match_data(dev);
+ tcon->no_strict_clock_check = of_property_read_bool(dev->of_node, + "no-strict-clock-check"); + tcon->lcd_rst = devm_reset_control_get(dev, "lcd"); if (IS_ERR(tcon->lcd_rst)) { dev_err(dev, "Couldn't get our reset line\n"); diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h index b5214d71610f..ab510b80004a 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.h +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h @@ -258,6 +258,7 @@ struct sun4i_tcon { struct reset_control *lvds_rst;
struct drm_panel *panel; + bool no_strict_clock_check;
/* Platform adjustments */ const struct sun4i_tcon_quirks *quirks;
On Thu, Feb 14, 2019 at 09:09:52PM -0800, Vasily Khoruzhick wrote:
Clock rate check that was added in commit bb43d40d7c83 ("drm/sun4i: rgb: Validate the clock rate") prevents some panel and bridges from working with sun4i driver.
Sounds lile a regression that should be reverted. The fix is not a backwards compatible change either.
Unfortunately, dotclock frequency for some modes are not achievable on sunxi hardware, and there's a slight deviation in rate returned by clk_round_rate(), so they fail this check.
Experiments show that panels and bridges work fine with this slight deviation, e.g. Pinebook that uses ANX6345 bridge with 768p eDP panel requests 73 MHz, gets 72.296MHz instead (0.96% difference) and works just fine.
This patch adds DT property to disable strict clock rate check
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
.../devicetree/bindings/display/sunxi/sun4i-drm.txt | 2 ++ drivers/gpu/drm/sun4i/sun4i_rgb.c | 5 +++++ drivers/gpu/drm/sun4i/sun4i_tcon.c | 3 +++ drivers/gpu/drm/sun4i/sun4i_tcon.h | 1 + 4 files changed, 11 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index f426bdb42f18..18c8b053a28d 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -63,6 +63,8 @@ Required properties: Documentation/devicetree/bindings/media/video-interfaces.txt. The first port should be the input endpoint. The second should be the output, usually to an HDMI connector.
- no-strict-clock-check: don't reject timings if exact dot clock can't be
- reached.
This should be the default IMO. Most panels are a single timing, so if we reject it the fallback no display?
I thought we had some mechanism already to allow some range of frequencies. I think the chromeos guys needed something IIRC.
Rob
On Mon, Feb 18, 2019 at 10:26 AM Rob Herring robh@kernel.org wrote:
On Thu, Feb 14, 2019 at 09:09:52PM -0800, Vasily Khoruzhick wrote:
Clock rate check that was added in commit bb43d40d7c83 ("drm/sun4i: rgb: Validate the clock rate") prevents some panel and bridges from working with sun4i driver.
Sounds lile a regression that should be reverted. The fix is not a backwards compatible change either.
anx6345 driver isn't mainlined yet and I'm not sure if this change breaks any mainlined boards. So likely there's not enough justification to revert it.
Unfortunately, dotclock frequency for some modes are not achievable on sunxi hardware, and there's a slight deviation in rate returned by clk_round_rate(), so they fail this check.
Experiments show that panels and bridges work fine with this slight deviation, e.g. Pinebook that uses ANX6345 bridge with 768p eDP panel requests 73 MHz, gets 72.296MHz instead (0.96% difference) and works just fine.
This patch adds DT property to disable strict clock rate check
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
.../devicetree/bindings/display/sunxi/sun4i-drm.txt | 2 ++ drivers/gpu/drm/sun4i/sun4i_rgb.c | 5 +++++ drivers/gpu/drm/sun4i/sun4i_tcon.c | 3 +++ drivers/gpu/drm/sun4i/sun4i_tcon.h | 1 + 4 files changed, 11 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index f426bdb42f18..18c8b053a28d 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -63,6 +63,8 @@ Required properties: Documentation/devicetree/bindings/media/video-interfaces.txt. The first port should be the input endpoint. The second should be the output, usually to an HDMI connector.
- no-strict-clock-check: don't reject timings if exact dot clock can't be
- reached.
This should be the default IMO. Most panels are a single timing, so if we reject it the fallback no display?
As far as I remember the change was introduced to reject some modes for which dotclock can't be reached when driver is used with VGA bridge. So if we make it default it'll break boards with VGA bridge and old DT.
I thought we had some mechanism already to allow some range of frequencies. I think the chromeos guys needed something IIRC.
You can specify frequency range for panels, but there's nothing for bridges. In my case EDID doesn't specify clock tolerance.
Rob
On Mon, Feb 18, 2019 at 11:33:05AM -0800, Vasily Khoruzhick wrote:
On Mon, Feb 18, 2019 at 10:26 AM Rob Herring robh@kernel.org wrote:
On Thu, Feb 14, 2019 at 09:09:52PM -0800, Vasily Khoruzhick wrote:
Clock rate check that was added in commit bb43d40d7c83 ("drm/sun4i: rgb: Validate the clock rate") prevents some panel and bridges from working with sun4i driver.
Sounds lile a regression that should be reverted. The fix is not a backwards compatible change either.
anx6345 driver isn't mainlined yet and I'm not sure if this change breaks any mainlined boards. So likely there's not enough justification to revert it.
Unfortunately, dotclock frequency for some modes are not achievable on sunxi hardware, and there's a slight deviation in rate returned by clk_round_rate(), so they fail this check.
Experiments show that panels and bridges work fine with this slight deviation, e.g. Pinebook that uses ANX6345 bridge with 768p eDP panel requests 73 MHz, gets 72.296MHz instead (0.96% difference) and works just fine.
This patch adds DT property to disable strict clock rate check
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
.../devicetree/bindings/display/sunxi/sun4i-drm.txt | 2 ++ drivers/gpu/drm/sun4i/sun4i_rgb.c | 5 +++++ drivers/gpu/drm/sun4i/sun4i_tcon.c | 3 +++ drivers/gpu/drm/sun4i/sun4i_tcon.h | 1 + 4 files changed, 11 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index f426bdb42f18..18c8b053a28d 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -63,6 +63,8 @@ Required properties: Documentation/devicetree/bindings/media/video-interfaces.txt. The first port should be the input endpoint. The second should be the output, usually to an HDMI connector.
- no-strict-clock-check: don't reject timings if exact dot clock can't be
- reached.
This should be the default IMO. Most panels are a single timing, so if we reject it the fallback no display?
As far as I remember the change was introduced to reject some modes for which dotclock can't be reached when driver is used with VGA bridge. So if we make it default it'll break boards with VGA bridge and old DT.
I thought we had some mechanism already to allow some range of frequencies. I think the chromeos guys needed something IIRC.
You can specify frequency range for panels, but there's nothing for bridges. In my case EDID doesn't specify clock tolerance.
I gave it some more though, and came up with the following patch. The basic idea is to leave the boundary check for the bridges that will have EDID and we need to filter out the modes that have no chance of being supported. The tolerancy used is the one defined in VESA specs, but I added a module parameter if you wanted to tune that.
And finally, since most of our panels are single timings without any tolerancy, we just try our best in this case and that's it, while leaving the door open to support display_timings and being able to do more once we have an idea of what the tolerancies are.
If that works for you, I'll submit it.
Maxime
--- >8 --- diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index f4a22689eb54..0460146aab75 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -43,6 +43,25 @@ drm_encoder_to_sun4i_rgb(struct drm_encoder *encoder) encoder); }
+static inline struct drm_connector * +sun4i_rgb_get_connector_from_encoder(const struct drm_encoder *encoder) +{ + struct drm_connector *connector = NULL, *tmp; + struct drm_connector_list_iter iter; + + drm_connector_list_iter_begin(encoder->dev, &iter); + drm_for_each_connector_iter(tmp, &iter) + if (tmp->encoder == encoder) { + connector = tmp; + goto out; + } + +out: + drm_connector_list_iter_end(&iter); + + return connector; +} + static int sun4i_rgb_get_modes(struct drm_connector *connector) { struct sun4i_rgb *rgb = @@ -52,11 +71,22 @@ static int sun4i_rgb_get_modes(struct drm_connector *connector) return drm_panel_get_modes(tcon->panel); }
+/* + * VESA DMT defines a tolerancy of 0.5% on the pixel clock, while the + * CVT spec reuses that tolerancy in its examples, so it looks to be a + * good default tolerancy for the EDID-based modes. + */ +static unsigned int clock_tolerancy = 5; +module_param(clock_tolerancy, uint, 0644); +MODULE_PARM_DESC(clock_tolerancy, + "Tolerancy of the pixel clock in per mille"); + static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc, const struct drm_display_mode *mode) { struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(crtc); struct sun4i_tcon *tcon = rgb->tcon; + struct drm_connector *connector = sun4i_rgb_get_connector_from_encoder(crtc); u32 hsync = mode->hsync_end - mode->hsync_start; u32 vsync = mode->vsync_end - mode->vsync_start; unsigned long rate = mode->clock * 1000; @@ -92,15 +122,27 @@ static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
DRM_DEBUG_DRIVER("Vertical parameters OK\n");
+ /* + * TODO: We should use the struct display_timing if available + * and / or trying to stretch the timings within that + * tolerancy to take care of panels that we wouldn't be able + * to have a exact match for. + */ + if (connector->connector_type == DRM_MODE_CONNECTOR_Unknown) { + DRM_DEBUG_DRIVER("RGB panel used, skipping clock rate checks"); + goto out; + } + tcon->dclk_min_div = 6; tcon->dclk_max_div = 127; rounded_rate = clk_round_rate(tcon->dclk, rate); - if (rounded_rate < rate) + if (rounded_rate < (rate * (1000 - clock_tolerancy) / 1000)) return MODE_CLOCK_LOW;
- if (rounded_rate > rate) + if (rounded_rate > (rate * (1000 + clock_tolerancy) / 1000)) return MODE_CLOCK_HIGH;
+out: DRM_DEBUG_DRIVER("Clock rate OK\n");
return MODE_OK; --- >8 ---
On Tue, Feb 19, 2019 at 12:56 AM Maxime Ripard maxime.ripard@bootlin.com wrote:
On Mon, Feb 18, 2019 at 11:33:05AM -0800, Vasily Khoruzhick wrote:
On Mon, Feb 18, 2019 at 10:26 AM Rob Herring robh@kernel.org wrote:
On Thu, Feb 14, 2019 at 09:09:52PM -0800, Vasily Khoruzhick wrote:
Clock rate check that was added in commit bb43d40d7c83 ("drm/sun4i: rgb: Validate the clock rate") prevents some panel and bridges from working with sun4i driver.
Sounds lile a regression that should be reverted. The fix is not a backwards compatible change either.
anx6345 driver isn't mainlined yet and I'm not sure if this change breaks any mainlined boards. So likely there's not enough justification to revert it.
Unfortunately, dotclock frequency for some modes are not achievable on sunxi hardware, and there's a slight deviation in rate returned by clk_round_rate(), so they fail this check.
Experiments show that panels and bridges work fine with this slight deviation, e.g. Pinebook that uses ANX6345 bridge with 768p eDP panel requests 73 MHz, gets 72.296MHz instead (0.96% difference) and works just fine.
This patch adds DT property to disable strict clock rate check
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
.../devicetree/bindings/display/sunxi/sun4i-drm.txt | 2 ++ drivers/gpu/drm/sun4i/sun4i_rgb.c | 5 +++++ drivers/gpu/drm/sun4i/sun4i_tcon.c | 3 +++ drivers/gpu/drm/sun4i/sun4i_tcon.h | 1 + 4 files changed, 11 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index f426bdb42f18..18c8b053a28d 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -63,6 +63,8 @@ Required properties: Documentation/devicetree/bindings/media/video-interfaces.txt. The first port should be the input endpoint. The second should be the output, usually to an HDMI connector.
- no-strict-clock-check: don't reject timings if exact dot clock can't be
- reached.
This should be the default IMO. Most panels are a single timing, so if we reject it the fallback no display?
As far as I remember the change was introduced to reject some modes for which dotclock can't be reached when driver is used with VGA bridge. So if we make it default it'll break boards with VGA bridge and old DT.
I thought we had some mechanism already to allow some range of frequencies. I think the chromeos guys needed something IIRC.
You can specify frequency range for panels, but there's nothing for bridges. In my case EDID doesn't specify clock tolerance.
I gave it some more though, and came up with the following patch. The basic idea is to leave the boundary check for the bridges that will have EDID and we need to filter out the modes that have no chance of being supported. The tolerancy used is the one defined in VESA specs, but I added a module parameter if you wanted to tune that.
And finally, since most of our panels are single timings without any tolerancy, we just try our best in this case and that's it, while leaving the door open to support display_timings and being able to do more once we have an idea of what the tolerancies are.
If that works for you, I'll submit it.
Maxime, thanks for your patch but it doesn't work for me. Pinebook needs 1% tolerance. Having it as a module parameter means that no distro will be able to boot on Pinebook out of the box.
Maxime
--- >8 --- diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index f4a22689eb54..0460146aab75 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -43,6 +43,25 @@ drm_encoder_to_sun4i_rgb(struct drm_encoder *encoder) encoder); }
+static inline struct drm_connector * +sun4i_rgb_get_connector_from_encoder(const struct drm_encoder *encoder) +{
struct drm_connector *connector = NULL, *tmp;
struct drm_connector_list_iter iter;
drm_connector_list_iter_begin(encoder->dev, &iter);
drm_for_each_connector_iter(tmp, &iter)
if (tmp->encoder == encoder) {
connector = tmp;
goto out;
}
+out:
drm_connector_list_iter_end(&iter);
return connector;
+}
static int sun4i_rgb_get_modes(struct drm_connector *connector) { struct sun4i_rgb *rgb = @@ -52,11 +71,22 @@ static int sun4i_rgb_get_modes(struct drm_connector *connector) return drm_panel_get_modes(tcon->panel); }
+/*
- VESA DMT defines a tolerancy of 0.5% on the pixel clock, while the
- CVT spec reuses that tolerancy in its examples, so it looks to be a
- good default tolerancy for the EDID-based modes.
- */
+static unsigned int clock_tolerancy = 5; +module_param(clock_tolerancy, uint, 0644); +MODULE_PARM_DESC(clock_tolerancy,
"Tolerancy of the pixel clock in per mille");
static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc, const struct drm_display_mode *mode) { struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(crtc); struct sun4i_tcon *tcon = rgb->tcon;
struct drm_connector *connector = sun4i_rgb_get_connector_from_encoder(crtc); u32 hsync = mode->hsync_end - mode->hsync_start; u32 vsync = mode->vsync_end - mode->vsync_start; unsigned long rate = mode->clock * 1000;
@@ -92,15 +122,27 @@ static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
DRM_DEBUG_DRIVER("Vertical parameters OK\n");
/*
* TODO: We should use the struct display_timing if available
* and / or trying to stretch the timings within that
* tolerancy to take care of panels that we wouldn't be able
* to have a exact match for.
*/
if (connector->connector_type == DRM_MODE_CONNECTOR_Unknown) {
DRM_DEBUG_DRIVER("RGB panel used, skipping clock rate checks");
goto out;
}
tcon->dclk_min_div = 6; tcon->dclk_max_div = 127; rounded_rate = clk_round_rate(tcon->dclk, rate);
if (rounded_rate < rate)
if (rounded_rate < (rate * (1000 - clock_tolerancy) / 1000)) return MODE_CLOCK_LOW;
if (rounded_rate > rate)
if (rounded_rate > (rate * (1000 + clock_tolerancy) / 1000)) return MODE_CLOCK_HIGH;
+out: DRM_DEBUG_DRIVER("Clock rate OK\n");
return MODE_OK;
--- >8 ---
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
On Tue, Feb 19, 2019 at 07:44:56AM -0800, Vasily Khoruzhick wrote:
On Tue, Feb 19, 2019 at 12:56 AM Maxime Ripard maxime.ripard@bootlin.com wrote:
On Mon, Feb 18, 2019 at 11:33:05AM -0800, Vasily Khoruzhick wrote:
On Mon, Feb 18, 2019 at 10:26 AM Rob Herring robh@kernel.org wrote:
On Thu, Feb 14, 2019 at 09:09:52PM -0800, Vasily Khoruzhick wrote:
Clock rate check that was added in commit bb43d40d7c83 ("drm/sun4i: rgb: Validate the clock rate") prevents some panel and bridges from working with sun4i driver.
Sounds lile a regression that should be reverted. The fix is not a backwards compatible change either.
anx6345 driver isn't mainlined yet and I'm not sure if this change breaks any mainlined boards. So likely there's not enough justification to revert it.
Unfortunately, dotclock frequency for some modes are not achievable on sunxi hardware, and there's a slight deviation in rate returned by clk_round_rate(), so they fail this check.
Experiments show that panels and bridges work fine with this slight deviation, e.g. Pinebook that uses ANX6345 bridge with 768p eDP panel requests 73 MHz, gets 72.296MHz instead (0.96% difference) and works just fine.
This patch adds DT property to disable strict clock rate check
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
.../devicetree/bindings/display/sunxi/sun4i-drm.txt | 2 ++ drivers/gpu/drm/sun4i/sun4i_rgb.c | 5 +++++ drivers/gpu/drm/sun4i/sun4i_tcon.c | 3 +++ drivers/gpu/drm/sun4i/sun4i_tcon.h | 1 + 4 files changed, 11 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index f426bdb42f18..18c8b053a28d 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -63,6 +63,8 @@ Required properties: Documentation/devicetree/bindings/media/video-interfaces.txt. The first port should be the input endpoint. The second should be the output, usually to an HDMI connector.
- no-strict-clock-check: don't reject timings if exact dot clock can't be
- reached.
This should be the default IMO. Most panels are a single timing, so if we reject it the fallback no display?
As far as I remember the change was introduced to reject some modes for which dotclock can't be reached when driver is used with VGA bridge. So if we make it default it'll break boards with VGA bridge and old DT.
I thought we had some mechanism already to allow some range of frequencies. I think the chromeos guys needed something IIRC.
You can specify frequency range for panels, but there's nothing for bridges. In my case EDID doesn't specify clock tolerance.
I gave it some more though, and came up with the following patch. The basic idea is to leave the boundary check for the bridges that will have EDID and we need to filter out the modes that have no chance of being supported. The tolerancy used is the one defined in VESA specs, but I added a module parameter if you wanted to tune that.
And finally, since most of our panels are single timings without any tolerancy, we just try our best in this case and that's it, while leaving the door open to support display_timings and being able to do more once we have an idea of what the tolerancies are.
If that works for you, I'll submit it.
Maxime, thanks for your patch but it doesn't work for me. Pinebook needs 1% tolerance. Having it as a module parameter means that no distro will be able to boot on Pinebook out of the box.
I don't really know what to tell you, the VESA spec defines everywhere that tolerance, and if we're not able to provide that, then we're not compliant and I don't want us to not be compliant just because one panel needed to be a bit more flexible, and especially since what could work on one panel might fail on another one.
If you want alternate solutions, then please answer to: http://lists.infradead.org/pipermail/linux-arm-kernel/2019-February/630441.h...
or provide the EDID blob.
Maxime
On Wed, Feb 20, 2019 at 2:33 AM Maxime Ripard maxime.ripard@bootlin.com wrote:
On Tue, Feb 19, 2019 at 07:44:56AM -0800, Vasily Khoruzhick wrote:
On Tue, Feb 19, 2019 at 12:56 AM Maxime Ripard
Maxime, thanks for your patch but it doesn't work for me. Pinebook needs 1% tolerance. Having it as a module parameter means that no distro will be able to boot on Pinebook out of the box.
I don't really know what to tell you, the VESA spec defines everywhere that tolerance, and if we're not able to provide that, then we're not compliant and I don't want us to not be compliant just because one panel needed to be a bit more flexible, and especially since what could work on one panel might fail on another one.
If you want alternate solutions, then please answer to: http://lists.infradead.org/pipermail/linux-arm-kernel/2019-February/630441.h...
or provide the EDID blob.
Uh, I spoke to early, my bad. I made a mistake when I calculated rate deviation for 768p last time - I took 73MHz as dotclock, but actual requested rate 72.3 MHz and it gets 72.296296 MHz, so it's within 0.5% tolerance. 1080p pinebook needs 138.5 MHz and gets 138.461538 MHz - also within 0.5%.
However your patch has an issue, I'll respond to your previous email.
Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
On Tue, Feb 19, 2019 at 12:56 AM Maxime Ripard maxime.ripard@bootlin.com wrote:
On Mon, Feb 18, 2019 at 11:33:05AM -0800, Vasily Khoruzhick wrote:
On Mon, Feb 18, 2019 at 10:26 AM Rob Herring robh@kernel.org wrote:
On Thu, Feb 14, 2019 at 09:09:52PM -0800, Vasily Khoruzhick wrote:
Clock rate check that was added in commit bb43d40d7c83 ("drm/sun4i: rgb: Validate the clock rate") prevents some panel and bridges from working with sun4i driver.
Sounds lile a regression that should be reverted. The fix is not a backwards compatible change either.
anx6345 driver isn't mainlined yet and I'm not sure if this change breaks any mainlined boards. So likely there's not enough justification to revert it.
Unfortunately, dotclock frequency for some modes are not achievable on sunxi hardware, and there's a slight deviation in rate returned by clk_round_rate(), so they fail this check.
Experiments show that panels and bridges work fine with this slight deviation, e.g. Pinebook that uses ANX6345 bridge with 768p eDP panel requests 73 MHz, gets 72.296MHz instead (0.96% difference) and works just fine.
This patch adds DT property to disable strict clock rate check
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
.../devicetree/bindings/display/sunxi/sun4i-drm.txt | 2 ++ drivers/gpu/drm/sun4i/sun4i_rgb.c | 5 +++++ drivers/gpu/drm/sun4i/sun4i_tcon.c | 3 +++ drivers/gpu/drm/sun4i/sun4i_tcon.h | 1 + 4 files changed, 11 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index f426bdb42f18..18c8b053a28d 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -63,6 +63,8 @@ Required properties: Documentation/devicetree/bindings/media/video-interfaces.txt. The first port should be the input endpoint. The second should be the output, usually to an HDMI connector.
- no-strict-clock-check: don't reject timings if exact dot clock can't be
- reached.
This should be the default IMO. Most panels are a single timing, so if we reject it the fallback no display?
As far as I remember the change was introduced to reject some modes for which dotclock can't be reached when driver is used with VGA bridge. So if we make it default it'll break boards with VGA bridge and old DT.
I thought we had some mechanism already to allow some range of frequencies. I think the chromeos guys needed something IIRC.
You can specify frequency range for panels, but there's nothing for bridges. In my case EDID doesn't specify clock tolerance.
I gave it some more though, and came up with the following patch. The basic idea is to leave the boundary check for the bridges that will have EDID and we need to filter out the modes that have no chance of being supported. The tolerancy used is the one defined in VESA specs, but I added a module parameter if you wanted to tune that.
And finally, since most of our panels are single timings without any tolerancy, we just try our best in this case and that's it, while leaving the door open to support display_timings and being able to do more once we have an idea of what the tolerancies are.
If that works for you, I'll submit it.
Maxime
--- >8 --- diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index f4a22689eb54..0460146aab75 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -43,6 +43,25 @@ drm_encoder_to_sun4i_rgb(struct drm_encoder *encoder) encoder); }
+static inline struct drm_connector * +sun4i_rgb_get_connector_from_encoder(const struct drm_encoder *encoder) +{
struct drm_connector *connector = NULL, *tmp;
struct drm_connector_list_iter iter;
drm_connector_list_iter_begin(encoder->dev, &iter);
drm_for_each_connector_iter(tmp, &iter)
if (tmp->encoder == encoder) {
connector = tmp;
goto out;
}
+out:
drm_connector_list_iter_end(&iter);
return connector;
+}
static int sun4i_rgb_get_modes(struct drm_connector *connector) { struct sun4i_rgb *rgb = @@ -52,11 +71,22 @@ static int sun4i_rgb_get_modes(struct drm_connector *connector) return drm_panel_get_modes(tcon->panel); }
+/*
- VESA DMT defines a tolerancy of 0.5% on the pixel clock, while the
- CVT spec reuses that tolerancy in its examples, so it looks to be a
- good default tolerancy for the EDID-based modes.
- */
+static unsigned int clock_tolerancy = 5; +module_param(clock_tolerancy, uint, 0644); +MODULE_PARM_DESC(clock_tolerancy,
"Tolerancy of the pixel clock in per mille");
static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc, const struct drm_display_mode *mode) { struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(crtc); struct sun4i_tcon *tcon = rgb->tcon;
struct drm_connector *connector = sun4i_rgb_get_connector_from_encoder(crtc); u32 hsync = mode->hsync_end - mode->hsync_start; u32 vsync = mode->vsync_end - mode->vsync_start; unsigned long rate = mode->clock * 1000;
@@ -92,15 +122,27 @@ static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
DRM_DEBUG_DRIVER("Vertical parameters OK\n");
/*
* TODO: We should use the struct display_timing if available
* and / or trying to stretch the timings within that
* tolerancy to take care of panels that we wouldn't be able
* to have a exact match for.
*/
if (connector->connector_type == DRM_MODE_CONNECTOR_Unknown) {
I applied your patch onto 5.0-rc6 and connector is NULL for me on Pinebook. I guess you need to check whether it's NULL before dereferencing it.
DRM_DEBUG_DRIVER("RGB panel used, skipping clock rate checks");
goto out;
}
tcon->dclk_min_div = 6; tcon->dclk_max_div = 127; rounded_rate = clk_round_rate(tcon->dclk, rate);
if (rounded_rate < rate)
if (rounded_rate < (rate * (1000 - clock_tolerancy) / 1000)) return MODE_CLOCK_LOW;
if (rounded_rate > rate)
if (rounded_rate > (rate * (1000 + clock_tolerancy) / 1000)) return MODE_CLOCK_HIGH;
+out: DRM_DEBUG_DRIVER("Clock rate OK\n");
return MODE_OK;
--- >8 ---
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
From: Icenowy Zheng icenowy@aosc.io
Allwinner A64's TCON0 can output RGB666 LCD signal.
Add its pinmux.
Signed-off-by: Icenowy Zheng icenowy@aosc.io Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com --- arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index 2abb335145a6..a8bbee84e7da 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -559,6 +559,15 @@ function = "i2c1"; };
+ lcd_rgb666_pins: lcd-rgb666 { + pins = "PD0", "PD1", "PD2", "PD3", "PD4", + "PD5", "PD6", "PD7", "PD8", "PD9", + "PD10", "PD11", "PD12", "PD13", + "PD14", "PD15", "PD16", "PD17", + "PD18", "PD19", "PD20", "PD21"; + function = "lcd0"; + }; + mmc0_pins: mmc0-pins { pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
This commit adds support for the BOE HB140WX1-501 14" WXGA TFT LCD panel.
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com --- .../display/panel/boe,hb140wx1-501.txt | 7 +++++ drivers/gpu/drm/panel/panel-simple.c | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/boe,hb140wx1-501.txt
diff --git a/Documentation/devicetree/bindings/display/panel/boe,hb140wx1-501.txt b/Documentation/devicetree/bindings/display/panel/boe,hb140wx1-501.txt new file mode 100644 index 000000000000..fc6f085eb8d1 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/boe,hb140wx1-501.txt @@ -0,0 +1,7 @@ +BOE HB140WX1-501 14" WXGA (1366x768) TFT LCD panel + +Required properties: +- compatible: should be "boe,hb140wx1-501" + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 9c69e739a524..9a4c9dd02c6c 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -828,6 +828,29 @@ static const struct panel_desc bananapi_s070wv20_ct16 = { }, };
+static const struct drm_display_mode boe_hb140wx1_mode = { + .clock = 72300, + .hdisplay = 1366, + .hsync_start = 1366 + 48, + .hsync_end = 1366 + 48 + 32, + .htotal = 1366 + 48 + 32 + 80, + .vdisplay = 768, + .vsync_start = 768 + 3, + .vsync_end = 768 + 3 + 6, + .vtotal = 768 + 3 + 6 + 13, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC, +}; + +static const struct panel_desc boe_hb140wx1 = { + .modes = &boe_hb140wx1_mode, + .num_modes = 1, + .size = { + .width = 310, + .height = 170, + }, +}; + static const struct drm_display_mode boe_hv070wsa_mode = { .clock = 42105, .hdisplay = 1024, @@ -2541,6 +2564,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "bananapi,s070wv20-ct16", .data = &bananapi_s070wv20_ct16, + }, { + .compatible = "boe,hb140wx1-501", + .data = &boe_hb140wx1 }, { .compatible = "boe,hv070wsa-100", .data = &boe_hv070wsa
Hi Vasily.
On Thu, Feb 14, 2019 at 09:09:54PM -0800, Vasily Khoruzhick via dri-devel wrote:
This commit adds support for the BOE HB140WX1-501 14" WXGA TFT LCD panel.
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
.../display/panel/boe,hb140wx1-501.txt | 7 +++++ drivers/gpu/drm/panel/panel-simple.c | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/boe,hb140wx1-501.txt
diff --git a/Documentation/devicetree/bindings/display/panel/boe,hb140wx1-501.txt b/Documentation/devicetree/bindings/display/panel/boe,hb140wx1-501.txt new file mode 100644 index 000000000000..fc6f085eb8d1 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/boe,hb140wx1-501.txt @@ -0,0 +1,7 @@ +BOE HB140WX1-501 14" WXGA (1366x768) TFT LCD panel
+Required properties: +- compatible: should be "boe,hb140wx1-501"
+This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory.
boe must be added to vendor-prefixes.txt (It is not present in the file I checked, but I may have been added later)
It is usual practice to split the patch in a dt-bindings: patch and a driver patch. And for the dt-bindings patch follow the submission rules laid out in Documentation/devicetree/bindings/submitting-patches.txt
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 9c69e739a524..9a4c9dd02c6c 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -828,6 +828,29 @@ static const struct panel_desc bananapi_s070wv20_ct16 = { }, };
+static const struct drm_display_mode boe_hb140wx1_mode = {
- .clock = 72300,
- .hdisplay = 1366,
- .hsync_start = 1366 + 48,
- .hsync_end = 1366 + 48 + 32,
- .htotal = 1366 + 48 + 32 + 80,
- .vdisplay = 768,
- .vsync_start = 768 + 3,
- .vsync_end = 768 + 3 + 6,
- .vtotal = 768 + 3 + 6 + 13,
- .vrefresh = 60,
- .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC,
+1 for specifying .flags.
+};
+static const struct panel_desc boe_hb140wx1 = {
- .modes = &boe_hb140wx1_mode,
- .num_modes = 1,
- .size = {
.width = 310,
.height = 170,
- },
+};
No .bus_format and no .bus_flags specified. Are they not used by this panel or are you lucky and the default works?
static const struct drm_display_mode boe_hv070wsa_mode = { .clock = 42105, .hdisplay = 1024, @@ -2541,6 +2564,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "bananapi,s070wv20-ct16", .data = &bananapi_s070wv20_ct16,
- }, {
.compatible = "boe,hb140wx1-501",
.data = &boe_hb140wx1
It is common practice to end the line with a "," so one do not need to modify this line if an extra line is required.
With the comments addressed you can add my: Reviewed-by: Sam Ravnborg sam@ravnborg.org
On Sat, Feb 16, 2019 at 12:43 PM Sam Ravnborg sam@ravnborg.org wrote:
Hi Vasily.
Hi Sam,
On Thu, Feb 14, 2019 at 09:09:54PM -0800, Vasily Khoruzhick via dri-devel wrote:
This commit adds support for the BOE HB140WX1-501 14" WXGA TFT LCD panel.
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
.../display/panel/boe,hb140wx1-501.txt | 7 +++++ drivers/gpu/drm/panel/panel-simple.c | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/boe,hb140wx1-501.txt
diff --git a/Documentation/devicetree/bindings/display/panel/boe,hb140wx1-501.txt b/Documentation/devicetree/bindings/display/panel/boe,hb140wx1-501.txt new file mode 100644 index 000000000000..fc6f085eb8d1 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/boe,hb140wx1-501.txt @@ -0,0 +1,7 @@ +BOE HB140WX1-501 14" WXGA (1366x768) TFT LCD panel
+Required properties: +- compatible: should be "boe,hb140wx1-501"
+This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory.
boe must be added to vendor-prefixes.txt (It is not present in the file I checked, but I may have been added later)
It's been there for quite a while, it's added in 27d23b30a561b which was included in 4.5.
It is usual practice to split the patch in a dt-bindings: patch and a driver patch. And for the dt-bindings patch follow the submission rules laid out in Documentation/devicetree/bindings/submitting-patches.txt
I checked git log for drivers/gpu/drm/panel/panel-simple.c and looks like recent commits for this file added binding and timings in a single commit. I can split it into separate file if maintainers insist though.
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 9c69e739a524..9a4c9dd02c6c 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -828,6 +828,29 @@ static const struct panel_desc bananapi_s070wv20_ct16 = { }, };
+static const struct drm_display_mode boe_hb140wx1_mode = {
.clock = 72300,
.hdisplay = 1366,
.hsync_start = 1366 + 48,
.hsync_end = 1366 + 48 + 32,
.htotal = 1366 + 48 + 32 + 80,
.vdisplay = 768,
.vsync_start = 768 + 3,
.vsync_end = 768 + 3 + 6,
.vtotal = 768 + 3 + 6 + 13,
.vrefresh = 60,
.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC,
+1 for specifying .flags.
+};
+static const struct panel_desc boe_hb140wx1 = {
.modes = &boe_hb140wx1_mode,
.num_modes = 1,
.size = {
.width = 310,
.height = 170,
},
+};
No .bus_format and no .bus_flags specified. Are they not used by this panel or are you lucky and the default works?
This is eDP panel, it doesn't need bus_format or bus_flags.
static const struct drm_display_mode boe_hv070wsa_mode = { .clock = 42105, .hdisplay = 1024, @@ -2541,6 +2564,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "bananapi,s070wv20-ct16", .data = &bananapi_s070wv20_ct16,
}, {
.compatible = "boe,hb140wx1-501",
.data = &boe_hb140wx1
It is common practice to end the line with a "," so one do not need to modify this line if an extra line is required.
OK, will do.
With the comments addressed you can add my: Reviewed-by: Sam Ravnborg sam@ravnborg.org
Hi Vasily
boe must be added to vendor-prefixes.txt (It is not present in the file I checked, but I may have been added later)
It's been there for quite a while, it's added in 27d23b30a561b which was included in 4.5.
Yep, at second look it was there in the file I looked at too.
Sam
Add vendor prefix for Guangdong Neweast Optoelectronics CO. LTD
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 389508584f48..15cfc265b644 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -267,6 +267,7 @@ netron-dy Netron DY netxeon Shenzhen Netxeon Technology CO., LTD nexbox Nexbox nextthing Next Thing Co. +neweast Guangdong Neweast Optoelectronics CO., LTD newhaven Newhaven Display International ni National Instruments nintendo Nintendo
On Thu, 14 Feb 2019 21:09:55 -0800, Vasily Khoruzhick wrote:
Add vendor prefix for Guangdong Neweast Optoelectronics CO. LTD
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+)
Reviewed-by: Rob Herring robh@kernel.org
This commit adds support for the NewEast Optoelectronics CO., LTD WJFH116008A 11.6" 1920x1080 TFT LCD panel.
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com --- .../display/panel/neweast,wjfh116008a.txt | 7 ++++ drivers/gpu/drm/panel/panel-simple.c | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt
diff --git a/Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt b/Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt new file mode 100644 index 000000000000..d76579f9f55e --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt @@ -0,0 +1,7 @@ +NewEast Optoelectronics CO., LTD WJFH116008A 11.6" 1920x1080 TFT LCD panel + +Required properties: +- compatible: should be "neweast,wjfh116008a" + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 9a4c9dd02c6c..3edc77e49d8f 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1843,6 +1843,42 @@ static const struct panel_desc netron_dy_e231732 = { .bus_format = MEDIA_BUS_FMT_RGB666_1X18, };
+static const struct drm_display_mode neweast_wjfh116008a_modes[] = { +{ + .clock = 138500, + .hdisplay = 1920, + .hsync_start = 1920 + 48, + .hsync_end = 1920 + 48 + 32, + .htotal = 1920 + 48 + 32 + 80, + .vdisplay = 1080, + .vsync_start = 1080 + 3, + .vsync_end = 1080 + 3 + 5, + .vtotal = 1080 + 3 + 5 + 23, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, +}, { + .clock = 110920, + .hdisplay = 1920, + .hsync_start = 1920 + 48, + .hsync_end = 1920 + 48 + 32, + .htotal = 1920 + 48 + 32 + 80, + .vdisplay = 1080, + .vsync_start = 1080 + 3, + .vsync_end = 1080 + 3 + 5, + .vtotal = 1080 + 3 + 5 + 23, + .vrefresh = 48, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, +} }; + +static const struct panel_desc neweast_wjfh116008a = { + .modes = neweast_wjfh116008a_modes, + .num_modes = 2, + .size = { + .width = 260, + .height = 150, + }, +}; + static const struct drm_display_mode newhaven_nhd_43_480272ef_atxl_mode = { .clock = 9000, .hdisplay = 480, @@ -2690,6 +2726,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "netron-dy,e231732", .data = &netron_dy_e231732, + }, { + .compatible = "neweast,wjfh116008a", + .data = &neweast_wjfh116008a, }, { .compatible = "newhaven,nhd-4.3-480272ef-atxl", .data = &newhaven_nhd_43_480272ef_atxl,
On Thu, Feb 14, 2019 at 09:09:56PM -0800, Vasily Khoruzhick wrote:
This commit adds support for the NewEast Optoelectronics CO., LTD WJFH116008A 11.6" 1920x1080 TFT LCD panel.
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
.../display/panel/neweast,wjfh116008a.txt | 7 ++++ drivers/gpu/drm/panel/panel-simple.c | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt
diff --git a/Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt b/Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt new file mode 100644 index 000000000000..d76579f9f55e --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt @@ -0,0 +1,7 @@ +NewEast Optoelectronics CO., LTD WJFH116008A 11.6" 1920x1080 TFT LCD panel
+Required properties: +- compatible: should be "neweast,wjfh116008a"
+This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory.
We already established that this goes thru a standard eDP connector. We should describe that and everything associated with it.
Rob
On Mon, Feb 18, 2019 at 10:33 AM Rob Herring robh@kernel.org wrote:
On Thu, Feb 14, 2019 at 09:09:56PM -0800, Vasily Khoruzhick wrote:
This commit adds support for the NewEast Optoelectronics CO., LTD WJFH116008A 11.6" 1920x1080 TFT LCD panel.
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
.../display/panel/neweast,wjfh116008a.txt | 7 ++++ drivers/gpu/drm/panel/panel-simple.c | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt
diff --git a/Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt b/Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt new file mode 100644 index 000000000000..d76579f9f55e --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt @@ -0,0 +1,7 @@ +NewEast Optoelectronics CO., LTD WJFH116008A 11.6" 1920x1080 TFT LCD panel
+Required properties: +- compatible: should be "neweast,wjfh116008a"
+This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory.
We already established that this goes thru a standard eDP connector. We should describe that and everything associated with it.
I believe using eDP connector binding wouldn't help much in my case and it won't improve accuracy of hardware description while adding unnecessary code duplication (edp-connector will be pretty much simple-panel).
Since currently there're no standalone connector drivers, implementing one requires significant refactoring of the code that I'm not familiar.
Rob
On Mon, Feb 18, 2019 at 1:07 PM Vasily Khoruzhick anarsoul@gmail.com wrote:
On Mon, Feb 18, 2019 at 10:33 AM Rob Herring robh@kernel.org wrote:
On Thu, Feb 14, 2019 at 09:09:56PM -0800, Vasily Khoruzhick wrote:
This commit adds support for the NewEast Optoelectronics CO., LTD WJFH116008A 11.6" 1920x1080 TFT LCD panel.
Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com
.../display/panel/neweast,wjfh116008a.txt | 7 ++++ drivers/gpu/drm/panel/panel-simple.c | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt
diff --git a/Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt b/Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt new file mode 100644 index 000000000000..d76579f9f55e --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/neweast,wjfh116008a.txt @@ -0,0 +1,7 @@ +NewEast Optoelectronics CO., LTD WJFH116008A 11.6" 1920x1080 TFT LCD panel
+Required properties: +- compatible: should be "neweast,wjfh116008a"
+This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory.
We already established that this goes thru a standard eDP connector. We should describe that and everything associated with it.
I believe using eDP connector binding wouldn't help much in my case and it won't improve accuracy of hardware description while adding unnecessary code duplication (edp-connector will be pretty much simple-panel).
Since currently there're no standalone connector drivers, implementing one requires significant refactoring of the code that I'm not familiar.
I'm not talking about drivers. I'm talking about bindings. Those are not necessarily 1-1. There's no reason the simple panel driver can't have an 'edp-connector' entry.
Also, since you have EDID, you should be using that for timing data IMO, and the binding needs to have enough information to support that. It may if DP-aux comes from the bridge chip or it may not if you need to describe that connection. Again, this is independent from what Linux chooses to do. If Linux chooses to have its own timing information that's its choice. Another OS may choose to use EDID.
Rob
On Tue, Feb 19, 2019 at 6:54 AM Rob Herring robh@kernel.org wrote:
I believe using eDP connector binding wouldn't help much in my case and it won't improve accuracy of hardware description while adding unnecessary code duplication (edp-connector will be pretty much simple-panel).
Since currently there're no standalone connector drivers, implementing one requires significant refactoring of the code that I'm not familiar.
I'm not talking about drivers. I'm talking about bindings. Those are not necessarily 1-1. There's no reason the simple panel driver can't have an 'edp-connector' entry.
These aren't independent things. Bindings are useless without drivers.
Also how are you going to address mainlined platforms that have eDP and use panel bindings? E.g. rk3399 supports eDP and uses panel binding - see rk3399-gru-kevin.dts as example.
Also, since you have EDID, you should be using that for timing data IMO, and the binding needs to have enough information to support that. It may if DP-aux comes from the bridge chip or it may not if you need to describe that connection. Again, this is independent from what Linux chooses to do. If Linux chooses to have its own timing information that's its choice. Another OS may choose to use EDID.
I see nothing wrong here - anx6345 driver reads EDID (over AUX) and uses timing from it, if reading EDID failed it fallback to timings defined in panel driver.
Rob
On Tue, Feb 19, 2019 at 01:35:26PM -0800, Vasily Khoruzhick wrote:
On Tue, Feb 19, 2019 at 6:54 AM Rob Herring robh@kernel.org wrote:
I believe using eDP connector binding wouldn't help much in my case and it won't improve accuracy of hardware description while adding unnecessary code duplication (edp-connector will be pretty much simple-panel).
Since currently there're no standalone connector drivers, implementing one requires significant refactoring of the code that I'm not familiar.
I'm not talking about drivers. I'm talking about bindings. Those are not necessarily 1-1. There's no reason the simple panel driver can't have an 'edp-connector' entry.
These aren't independent things. Bindings are useless without drivers.
What I mean is bindings for panels and connectors are basically the same thing. The kernel handles them quite differently, but that is a kernel design decision independent of bindings. Another client (of DT) may do things differently or the kernel may do things differently in the future.
There is not any simple panel binding really. This originated I think from a 'simple-panel' compatible that was originally attempted. What we have is a collection of common properties for panels which panel bindings can use. And we have some common panel docs too. I've been meaning to clean-up and unify all this. What I'd like is a pool of common properties for any connector or panel to use. Then we can define what subset any panel or connector use. When there's something standard across devices like LVDS modes or eDP connectors, then we can further define a subset. It is easiest if these subsets are also encapsulated by a compatible string, but that's not required and some times undesirable. For the latter, the 'simple-panel' compatible is what I'm thinking of. It needs to have a well defined meaning such as following some spec.
Also how are you going to address mainlined platforms that have eDP and use panel bindings? E.g. rk3399 supports eDP and uses panel binding - see rk3399-gru-kevin.dts as example.
I'm not.
Supporting eDP and having a standard eDP connector are 2 different things. I'd guess chromebooks would do something standard, but I've got no idea. Assuming they do, there's nothing to do for existing bindings. They are already defined. But I imagine having a 'edp-connector' fallback would be helpful to the chromebook folks.
We can easily map a specific binding to a specific or generic driver. So if we ever have a generic eDP connector driver, then we could perhaps move rk3399-gru-kevin to use that. Kind of depends if it matches whatever set of properties is defined for eDP connectors.
Also, since you have EDID, you should be using that for timing data IMO, and the binding needs to have enough information to support that. It may if DP-aux comes from the bridge chip or it may not if you need to describe that connection. Again, this is independent from what Linux chooses to do. If Linux chooses to have its own timing information that's its choice. Another OS may choose to use EDID.
I see nothing wrong here - anx6345 driver reads EDID (over AUX) and uses timing from it, if reading EDID failed it fallback to timings defined in panel driver.
Okay, I didn't look at the driver part closely. So really, it's just having a fallback compatible and defining what that includes.
Rob
On Fri, Feb 22, 2019 at 10:37 AM Rob Herring robh@kernel.org wrote:
There is not any simple panel binding really. This originated I think from a 'simple-panel' compatible that was originally attempted. What we have is a collection of common properties for panels which panel bindings can use. And we have some common panel docs too. I've been meaning to clean-up and unify all this.
I'm afraid I'm not right person to clean it up. My understanding of what different kind of panels and connectors need is pretty limited. Moreover I'm doing pinebook hacking in my spare time which is few hours a week and major panel/connector bindings clean up is not something I want to invest my time in.
Since it seems to be a blocker I'll wait for someone to clean up panel/connector bindings and then respin the series. Meanwhile it'll live in my github.
Thanks for review and sharing your ideas.
Regards, Vasily
From: Icenowy Zheng icenowy@aosc.io
Pinebook has an ANX6345 bridge connected to the RGB666 LCD output and eDP panel input. The bridge is controlled via I2C that's connected to R_I2C bus.
There're pinebooks with 3 different panels in the wild, all 3 with different timings. Add all the compatibles to panel node and hope that we can get correct timings from EDID. If reading EDID fails it's up to firmware to fixup compatible string.
Enable all this hardware in device tree.
Signed-off-by: Icenowy Zheng icenowy@aosc.io Signed-off-by: Vasily Khoruzhick anarsoul@gmail.com --- .../dts/allwinner/sun50i-a64-pinebook.dts | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinebook.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinebook.dts index d22736a62481..0d9280bafc12 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinebook.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinebook.dts @@ -63,6 +63,19 @@ }; };
+ edp_panel: edp-panel { + compatible = "boe,hb140wx1-501", "innolux,n116bge", + "neweast,wjfh116008a", "simple-panel"; + backlight = <&backlight>; + power-supply = <®_dc1sw>; + + port { + panel_in_edp: endpoint { + remote-endpoint = <&anx6345_out_panel>; + }; + }; + }; + reg_vcc3v3: vcc3v3 { compatible = "regulator-fixed"; regulator-name = "vcc3v3"; @@ -102,6 +115,10 @@ status = "okay"; };
+&de { + status = "okay"; +}; + &ehci0 { phys = <&usbphy 0>; phy-names = "usb"; @@ -112,6 +129,10 @@ status = "okay"; };
+&mixer0 { + status = "okay"; +}; + &mmc0 { pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins>; @@ -163,6 +184,46 @@ status = "okay"; };
+&r_i2c { + pinctrl-names = "default"; + pinctrl-0 = <&r_i2c_pins_a>; + status = "okay"; + + anx6345: anx6345@38 { + compatible = "analogix,anx6345"; + reg = <0x38>; + reset-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* PD24 */ + dvdd25-supply = <®_dldo2>; + dvdd12-supply = <®_fldo1>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + anx6345_in: port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + anx6345_in_tcon0: endpoint@0 { + reg = <0>; + remote-endpoint = <&tcon0_out_anx6345>; + }; + }; + + anx6345_out: port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + anx6345_out_panel: endpoint@0 { + reg = <0>; + remote-endpoint = <&panel_in_edp>; + }; + }; + }; + }; +}; + &r_rsb { status = "okay";
@@ -334,6 +395,21 @@ "MIC2", "Internal Microphone Right"; };
+&tcon0 { + pinctrl-names = "default"; + pinctrl-0 = <&lcd_rgb666_pins>; + no-strict-clock-check; + + status = "okay"; +}; + +&tcon0_out { + tcon0_out_anx6345: endpoint@0 { + reg = <0>; + remote-endpoint = <&anx6345_in_tcon0>; + }; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_pb_pins>;
dri-devel@lists.freedesktop.org