diff options
Diffstat (limited to '')
39 files changed, 2076 insertions, 1863 deletions
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 57946d80b02d..8b2226f72b24 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -15,17 +15,6 @@ config DRM_PANEL_BRIDGE menu "Display Interface Bridges" depends on DRM && DRM_BRIDGE -config DRM_CDNS_DSI - tristate "Cadence DPI/DSI bridge" - select DRM_KMS_HELPER - select DRM_MIPI_DSI - select DRM_PANEL_BRIDGE - select GENERIC_PHY_MIPI_DPHY - depends on OF - help - Support Cadence DPI to DSI bridge. This is an internal - bridge and is meant to be directly embedded in a SoC. - config DRM_CHIPONE_ICN6211 tristate "Chipone ICN6211 MIPI-DSI/RGB Converter bridge" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 1884803c6860..52f6e8b4a821 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_CDNS_DSI) += cdns-dsi.o obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index e7a6e456ed0d..ddceafa7b637 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -1185,8 +1185,9 @@ static int adv7511_parse_dt(struct device_node *np, return 0; } -static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +static int adv7511_probe(struct i2c_client *i2c) { + const struct i2c_device_id *id = i2c_client_get_device_id(i2c); struct adv7511_link_config link_config; struct adv7511 *adv7511; struct device *dev = &i2c->dev; @@ -1392,7 +1393,7 @@ static struct i2c_driver adv7511_driver = { .of_match_table = adv7511_of_ids, }, .id_table = adv7511_i2c_ids, - .probe = adv7511_probe, + .probe_new = adv7511_probe, .remove = adv7511_remove, }; diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c index 660a54857929..3577c532abb4 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c @@ -22,7 +22,6 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> @@ -692,8 +691,7 @@ static bool anx6345_get_chip_id(struct anx6345 *anx6345) return false; } -static int anx6345_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int anx6345_i2c_probe(struct i2c_client *client) { struct anx6345 *anx6345; struct device *dev; @@ -817,7 +815,7 @@ static struct i2c_driver anx6345_driver = { .name = "anx6345", .of_match_table = of_match_ptr(anx6345_match_table), }, - .probe = anx6345_i2c_probe, + .probe_new = anx6345_i2c_probe, .remove = anx6345_i2c_remove, .id_table = anx6345_id, }; diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c index 5997049fde5b..a3a38bbe2786 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c @@ -1214,8 +1214,7 @@ static const u16 anx78xx_chipid_list[] = { 0x7818, }; -static int anx78xx_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int anx78xx_i2c_probe(struct i2c_client *client) { struct anx78xx *anx78xx; struct anx78xx_platform_data *pdata; @@ -1390,7 +1389,7 @@ static struct i2c_driver anx78xx_driver = { .name = "anx7814", .of_match_table = of_match_ptr(anx78xx_match_table), }, - .probe = anx78xx_i2c_probe, + .probe_new = anx78xx_i2c_probe, .remove = anx78xx_i2c_remove, .id_table = anx78xx_id, }; diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index b0ff1ecb80a5..6846199a2ee1 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -26,7 +26,6 @@ #include <drm/display/drm_hdcp_helper.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> @@ -1403,7 +1402,6 @@ static void anx7625_stop_dp_work(struct anx7625_data *ctx) { ctx->hpd_status = 0; ctx->hpd_high_cnt = 0; - ctx->display_timing_valid = 0; } static void anx7625_start_dp_work(struct anx7625_data *ctx) @@ -2562,8 +2560,7 @@ static void anx7625_runtime_disable(void *data) pm_runtime_disable(data); } -static int anx7625_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int anx7625_i2c_probe(struct i2c_client *client) { struct anx7625_data *platform; struct anx7625_platform_data *pdata; @@ -2756,7 +2753,7 @@ static struct i2c_driver anx7625_driver = { .of_match_table = anx_match_table, .pm = &anx7625_pm_ops, }, - .probe = anx7625_i2c_probe, + .probe_new = anx7625_i2c_probe, .remove = anx7625_i2c_remove, .id_table = anx7625_id, diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig index 1d06182bea71..ec35215a2003 100644 --- a/drivers/gpu/drm/bridge/cadence/Kconfig +++ b/drivers/gpu/drm/bridge/cadence/Kconfig @@ -1,4 +1,25 @@ # SPDX-License-Identifier: GPL-2.0-only +config DRM_CDNS_DSI + tristate "Cadence DPI/DSI bridge" + select DRM_KMS_HELPER + select DRM_MIPI_DSI + select DRM_PANEL_BRIDGE + select GENERIC_PHY_MIPI_DPHY + depends on OF + help + Support Cadence DPI to DSI bridge. This is an internal + bridge and is meant to be directly embedded in a SoC. + +if DRM_CDNS_DSI + +config DRM_CDNS_DSI_J721E + bool "J721E Cadence DSI wrapper support" + default y + help + Support J721E Cadence DSI wrapper. The wrapper manages + the routing of the DSS DPI signal to the Cadence DSI. +endif + config DRM_CDNS_MHDP8546 tristate "Cadence DPI/DP bridge" select DRM_DISPLAY_DP_HELPER diff --git a/drivers/gpu/drm/bridge/cadence/Makefile b/drivers/gpu/drm/bridge/cadence/Makefile index 4d2db8df1bc6..c95fd5b81d13 100644 --- a/drivers/gpu/drm/bridge/cadence/Makefile +++ b/drivers/gpu/drm/bridge/cadence/Makefile @@ -1,4 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o +cdns-dsi-y := cdns-dsi-core.o +cdns-dsi-$(CONFIG_DRM_CDNS_DSI_J721E) += cdns-dsi-j721e.o obj-$(CONFIG_DRM_CDNS_MHDP8546) += cdns-mhdp8546.o cdns-mhdp8546-y := cdns-mhdp8546-core.o cdns-mhdp8546-hdcp.o cdns-mhdp8546-$(CONFIG_DRM_CDNS_MHDP8546_J721E) += cdns-mhdp8546-j721e.o diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c new file mode 100644 index 000000000000..5dbfc7226b31 --- /dev/null +++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c @@ -0,0 +1,1319 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright: 2017 Cadence Design Systems, Inc. + * + * Author: Boris Brezillon <boris.brezillon@bootlin.com> + */ + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_probe_helper.h> +#include <video/mipi_display.h> + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> + +#include <linux/phy/phy-mipi-dphy.h> + +#include "cdns-dsi-core.h" +#ifdef CONFIG_DRM_CDNS_DSI_J721E +#include "cdns-dsi-j721e.h" +#endif + +#define IP_CONF 0x0 +#define SP_HS_FIFO_DEPTH(x) (((x) & GENMASK(30, 26)) >> 26) +#define SP_LP_FIFO_DEPTH(x) (((x) & GENMASK(25, 21)) >> 21) +#define VRS_FIFO_DEPTH(x) (((x) & GENMASK(20, 16)) >> 16) +#define DIRCMD_FIFO_DEPTH(x) (((x) & GENMASK(15, 13)) >> 13) +#define SDI_IFACE_32 BIT(12) +#define INTERNAL_DATAPATH_32 (0 << 10) +#define INTERNAL_DATAPATH_16 (1 << 10) +#define INTERNAL_DATAPATH_8 (3 << 10) +#define INTERNAL_DATAPATH_SIZE ((x) & GENMASK(11, 10)) +#define NUM_IFACE(x) ((((x) & GENMASK(9, 8)) >> 8) + 1) +#define MAX_LANE_NB(x) (((x) & GENMASK(7, 6)) >> 6) +#define RX_FIFO_DEPTH(x) ((x) & GENMASK(5, 0)) + +#define MCTL_MAIN_DATA_CTL 0x4 +#define TE_MIPI_POLLING_EN BIT(25) +#define TE_HW_POLLING_EN BIT(24) +#define DISP_EOT_GEN BIT(18) +#define HOST_EOT_GEN BIT(17) +#define DISP_GEN_CHECKSUM BIT(16) +#define DISP_GEN_ECC BIT(15) +#define BTA_EN BIT(14) +#define READ_EN BIT(13) +#define REG_TE_EN BIT(12) +#define IF_TE_EN(x) BIT(8 + (x)) +#define TVG_SEL BIT(6) +#define VID_EN BIT(5) +#define IF_VID_SELECT(x) ((x) << 2) +#define IF_VID_SELECT_MASK GENMASK(3, 2) +#define IF_VID_MODE BIT(1) +#define LINK_EN BIT(0) + +#define MCTL_MAIN_PHY_CTL 0x8 +#define HS_INVERT_DAT(x) BIT(19 + ((x) * 2)) +#define SWAP_PINS_DAT(x) BIT(18 + ((x) * 2)) +#define HS_INVERT_CLK BIT(17) +#define SWAP_PINS_CLK BIT(16) +#define HS_SKEWCAL_EN BIT(15) +#define WAIT_BURST_TIME(x) ((x) << 10) +#define DATA_ULPM_EN(x) BIT(6 + (x)) +#define CLK_ULPM_EN BIT(5) +#define CLK_CONTINUOUS BIT(4) +#define DATA_LANE_EN(x) BIT((x) - 1) + +#define MCTL_MAIN_EN 0xc +#define DATA_FORCE_STOP BIT(17) +#define CLK_FORCE_STOP BIT(16) +#define IF_EN(x) BIT(13 + (x)) +#define DATA_LANE_ULPM_REQ(l) BIT(9 + (l)) +#define CLK_LANE_ULPM_REQ BIT(8) +#define DATA_LANE_START(x) BIT(4 + (x)) +#define CLK_LANE_EN BIT(3) +#define PLL_START BIT(0) + +#define MCTL_DPHY_CFG0 0x10 +#define DPHY_C_RSTB BIT(20) +#define DPHY_D_RSTB(x) GENMASK(15 + (x), 16) +#define DPHY_PLL_PDN BIT(10) +#define DPHY_CMN_PDN BIT(9) +#define DPHY_C_PDN BIT(8) +#define DPHY_D_PDN(x) GENMASK(3 + (x), 4) +#define DPHY_ALL_D_PDN GENMASK(7, 4) +#define DPHY_PLL_PSO BIT(1) +#define DPHY_CMN_PSO BIT(0) + +#define MCTL_DPHY_TIMEOUT1 0x14 +#define HSTX_TIMEOUT(x) ((x) << 4) +#define HSTX_TIMEOUT_MAX GENMASK(17, 0) +#define CLK_DIV(x) (x) +#define CLK_DIV_MAX GENMASK(3, 0) + +#define MCTL_DPHY_TIMEOUT2 0x18 +#define LPRX_TIMEOUT(x) (x) + +#define MCTL_ULPOUT_TIME 0x1c +#define DATA_LANE_ULPOUT_TIME(x) ((x) << 9) +#define CLK_LANE_ULPOUT_TIME(x) (x) + +#define MCTL_3DVIDEO_CTL 0x20 +#define VID_VSYNC_3D_EN BIT(7) +#define VID_VSYNC_3D_LR BIT(5) +#define VID_VSYNC_3D_SECOND_EN BIT(4) +#define VID_VSYNC_3DFORMAT_LINE (0 << 2) +#define VID_VSYNC_3DFORMAT_FRAME (1 << 2) +#define VID_VSYNC_3DFORMAT_PIXEL (2 << 2) +#define VID_VSYNC_3DMODE_OFF 0 +#define VID_VSYNC_3DMODE_PORTRAIT 1 +#define VID_VSYNC_3DMODE_LANDSCAPE 2 + +#define MCTL_MAIN_STS 0x24 +#define MCTL_MAIN_STS_CTL 0x130 +#define MCTL_MAIN_STS_CLR 0x150 +#define MCTL_MAIN_STS_FLAG 0x170 +#define HS_SKEWCAL_DONE BIT(11) +#define IF_UNTERM_PKT_ERR(x) BIT(8 + (x)) +#define LPRX_TIMEOUT_ERR BIT(7) +#define HSTX_TIMEOUT_ERR BIT(6) +#define DATA_LANE_RDY(l) BIT(2 + (l)) +#define CLK_LANE_RDY BIT(1) +#define PLL_LOCKED BIT(0) + +#define MCTL_DPHY_ERR 0x28 +#define MCTL_DPHY_ERR_CTL1 0x148 +#define MCTL_DPHY_ERR_CLR 0x168 +#define MCTL_DPHY_ERR_FLAG 0x188 +#define ERR_CONT_LP(x, l) BIT(18 + ((x) * 4) + (l)) +#define ERR_CONTROL(l) BIT(14 + (l)) +#define ERR_SYNESC(l) BIT(10 + (l)) +#define ERR_ESC(l) BIT(6 + (l)) + +#define MCTL_DPHY_ERR_CTL2 0x14c +#define ERR_CONT_LP_EDGE(x, l) BIT(12 + ((x) * 4) + (l)) +#define ERR_CONTROL_EDGE(l) BIT(8 + (l)) +#define ERR_SYN_ESC_EDGE(l) BIT(4 + (l)) +#define ERR_ESC_EDGE(l) BIT(0 + (l)) + +#define MCTL_LANE_STS 0x2c +#define PPI_C_TX_READY_HS BIT(18) +#define DPHY_PLL_LOCK BIT(17) +#define PPI_D_RX_ULPS_ESC(x) (((x) & GENMASK(15, 12)) >> 12) +#define LANE_STATE_START 0 +#define LANE_STATE_IDLE 1 +#define LANE_STATE_WRITE 2 +#define LANE_STATE_ULPM 3 +#define LANE_STATE_READ 4 +#define DATA_LANE_STATE(l, val) \ + (((val) >> (2 + 2 * (l) + ((l) ? 1 : 0))) & GENMASK((l) ? 1 : 2, 0)) +#define CLK_LANE_STATE_HS 2 +#define CLK_LANE_STATE(val) ((val) & GENMASK(1, 0)) + +#define DSC_MODE_CTL 0x30 +#define DSC_MODE_EN BIT(0) + +#define DSC_CMD_SEND 0x34 +#define DSC_SEND_PPS BIT(0) +#define DSC_EXECUTE_QUEUE BIT(1) + +#define DSC_PPS_WRDAT 0x38 + +#define DSC_MODE_STS 0x3c +#define DSC_PPS_DONE BIT(1) +#define DSC_EXEC_DONE BIT(2) + +#define CMD_MODE_CTL 0x70 +#define IF_LP_EN(x) BIT(9 + (x)) +#define IF_VCHAN_ID(x, c) ((c) << ((x) * 2)) + +#define CMD_MODE_CTL2 0x74 +#define TE_TIMEOUT(x) ((x) << 11) +#define FILL_VALUE(x) ((x) << 3) +#define ARB_IF_WITH_HIGHEST_PRIORITY(x) ((x) << 1) +#define ARB_ROUND_ROBIN_MODE BIT(0) + +#define CMD_MODE_STS 0x78 +#define CMD_MODE_STS_CTL 0x134 +#define CMD_MODE_STS_CLR 0x154 +#define CMD_MODE_STS_FLAG 0x174 +#define ERR_IF_UNDERRUN(x) BIT(4 + (x)) +#define ERR_UNWANTED_READ BIT(3) +#define ERR_TE_MISS BIT(2) +#define ERR_NO_TE BIT(1) +#define CSM_RUNNING BIT(0) + +#define DIRECT_CMD_SEND 0x80 + +#define DIRECT_CMD_MAIN_SETTINGS 0x84 +#define TRIGGER_VAL(x) ((x) << 25) +#define CMD_LP_EN BIT(24) +#define CMD_SIZE(x) ((x) << 16) +#define CMD_VCHAN_ID(x) ((x) << 14) +#define CMD_DATATYPE(x) ((x) << 8) +#define CMD_LONG BIT(3) +#define WRITE_CMD 0 +#define READ_CMD 1 +#define TE_REQ 4 +#define TRIGGER_REQ 5 +#define BTA_REQ 6 + +#define DIRECT_CMD_STS 0x88 +#define DIRECT_CMD_STS_CTL 0x138 +#define DIRECT_CMD_STS_CLR 0x158 +#define DIRECT_CMD_STS_FLAG 0x178 +#define RCVD_ACK_VAL(val) ((val) >> 16) +#define RCVD_TRIGGER_VAL(val) (((val) & GENMASK(14, 11)) >> 11) +#define READ_COMPLETED_WITH_ERR BIT(10) +#define BTA_FINISHED BIT(9) +#define BTA_COMPLETED BIT(8) +#define TE_RCVD BIT(7) +#define TRIGGER_RCVD BIT(6) +#define ACK_WITH_ERR_RCVD BIT(5) +#define ACK_RCVD BIT(4) +#define READ_COMPLETED BIT(3) +#define TRIGGER_COMPLETED BIT(2) +#define WRITE_COMPLETED BIT(1) +#define SENDING_CMD BIT(0) + +#define DIRECT_CMD_STOP_READ 0x8c + +#define DIRECT_CMD_WRDATA 0x90 + +#define DIRECT_CMD_FIFO_RST 0x94 + +#define DIRECT_CMD_RDDATA 0xa0 + +#define DIRECT_CMD_RD_PROPS 0xa4 +#define RD_DCS BIT(18) +#define RD_VCHAN_ID(val) (((val) >> 16) & GENMASK(1, 0)) +#define RD_SIZE(val) ((val) & GENMASK(15, 0)) + +#define DIRECT_CMD_RD_STS 0xa8 +#define DIRECT_CMD_RD_STS_CTL 0x13c +#define DIRECT_CMD_RD_STS_CLR 0x15c +#define DIRECT_CMD_RD_STS_FLAG 0x17c +#define ERR_EOT_WITH_ERR BIT(8) +#define ERR_MISSING_EOT BIT(7) +#define ERR_WRONG_LENGTH BIT(6) +#define ERR_OVERSIZE BIT(5) +#define ERR_RECEIVE BIT(4) +#define ERR_UNDECODABLE BIT(3) +#define ERR_CHECKSUM BIT(2) +#define ERR_UNCORRECTABLE BIT(1) +#define ERR_FIXED BIT(0) + +#define VID_MAIN_CTL 0xb0 +#define VID_IGNORE_MISS_VSYNC BIT(31) +#define VID_FIELD_SW BIT(28) +#define VID_INTERLACED_EN BIT(27) +#define RECOVERY_MODE(x) ((x) << 25) +#define RECOVERY_MODE_NEXT_HSYNC 0 +#define RECOVERY_MODE_NEXT_STOP_POINT 2 +#define RECOVERY_MODE_NEXT_VSYNC 3 +#define REG_BLKEOL_MODE(x) ((x) << 23) +#define REG_BLKLINE_MODE(x) ((x) << 21) +#define REG_BLK_MODE_NULL_PKT 0 +#define REG_BLK_MODE_BLANKING_PKT 1 +#define REG_BLK_MODE_LP 2 +#define SYNC_PULSE_HORIZONTAL BIT(20) +#define SYNC_PULSE_ACTIVE BIT(19) +#define BURST_MODE BIT(18) +#define VID_PIXEL_MODE_MASK GENMASK(17, 14) +#define VID_PIXEL_MODE_RGB565 (0 << 14) +#define VID_PIXEL_MODE_RGB666_PACKED (1 << 14) +#define VID_PIXEL_MODE_RGB666 (2 << 14) +#define VID_PIXEL_MODE_RGB888 (3 << 14) +#define VID_PIXEL_MODE_RGB101010 (4 << 14) +#define VID_PIXEL_MODE_RGB121212 (5 << 14) +#define VID_PIXEL_MODE_YUV420 (8 << 14) +#define VID_PIXEL_MODE_YUV422_PACKED (9 << 14) +#define VID_PIXEL_MODE_YUV422 (10 << 14) +#define VID_PIXEL_MODE_YUV422_24B (11 << 14) +#define VID_PIXEL_MODE_DSC_COMP (12 << 14) +#define VID_DATATYPE(x) ((x) << 8) +#define VID_VIRTCHAN_ID(iface, x) ((x) << (4 + (iface) * 2)) +#define STOP_MODE(x) ((x) << 2) +#define START_MODE(x) (x) + +#define VID_VSIZE1 0xb4 +#define VFP_LEN(x) ((x) << 12) +#define VBP_LEN(x) ((x) << 6) +#define VSA_LEN(x) (x) + +#define VID_VSIZE2 0xb8 +#define VACT_LEN(x) (x) + +#define VID_HSIZE1 0xc0 +#define HBP_LEN(x) ((x) << 16) +#define HSA_LEN(x) (x) + +#define VID_HSIZE2 0xc4 +#define HFP_LEN(x) ((x) << 16) +#define HACT_LEN(x) (x) + +#define VID_BLKSIZE1 0xcc +#define BLK_EOL_PKT_LEN(x) ((x) << 15) +#define BLK_LINE_EVENT_PKT_LEN(x) (x) + +#define VID_BLKSIZE2 0xd0 +#define BLK_LINE_PULSE_PKT_LEN(x) (x) + +#define VID_PKT_TIME 0xd8 +#define BLK_EOL_DURATION(x) (x) + +#define VID_DPHY_TIME 0xdc +#define REG_WAKEUP_TIME(x) ((x) << 17) +#define REG_LINE_DURATION(x) (x) + +#define VID_ERR_COLOR1 0xe0 +#define COL_GREEN(x) ((x) << 12) +#define COL_RED(x) (x) + +#define VID_ERR_COLOR2 0xe4 +#define PAD_VAL(x) ((x) << 12) +#define COL_BLUE(x) (x) + +#define VID_VPOS 0xe8 +#define LINE_VAL(val) (((val) & GENMASK(14, 2)) >> 2) +#define LINE_POS(val) ((val) & GENMASK(1, 0)) + +#define VID_HPOS 0xec +#define HORIZ_VAL(val) (((val) & GENMASK(17, 3)) >> 3) +#define HORIZ_POS(val) ((val) & GENMASK(2, 0)) + +#define VID_MODE_STS 0xf0 +#define VID_MODE_STS_CTL 0x140 +#define VID_MODE_STS_CLR 0x160 +#define VID_MODE_STS_FLAG 0x180 +#define VSG_RECOVERY BIT(10) +#define ERR_VRS_WRONG_LEN BIT(9) +#define ERR_LONG_READ BIT(8) +#define ERR_LINE_WRITE BIT(7) +#define ERR_BURST_WRITE BIT(6) +#define ERR_SMALL_HEIGHT BIT(5) +#define ERR_SMALL_LEN BIT(4) +#define ERR_MISSING_VSYNC BIT(3) +#define ERR_MISSING_HSYNC BIT(2) +#define ERR_MISSING_DATA BIT(1) +#define VSG_RUNNING BIT(0) + +#define VID_VCA_SETTING1 0xf4 +#define BURST_LP BIT(16) +#define MAX_BURST_LIMIT(x) (x) + +#define VID_VCA_SETTING2 0xf8 +#define MAX_LINE_LIMIT(x) ((x) << 16) +#define EXACT_BURST_LIMIT(x) (x) + +#define TVG_CTL 0xfc +#define TVG_STRIPE_SIZE(x) ((x) << 5) +#define TVG_MODE_MASK GENMASK(4, 3) +#define TVG_MODE_SINGLE_COLOR (0 << 3) +#define TVG_MODE_VSTRIPES (2 << 3) +#define TVG_MODE_HSTRIPES (3 << 3) +#define TVG_STOPMODE_MASK GENMASK(2, 1) +#define TVG_STOPMODE_EOF (0 << 1) +#define TVG_STOPMODE_EOL (1 << 1) +#define TVG_STOPMODE_NOW (2 << 1) +#define TVG_RUN BIT(0) + +#define TVG_IMG_SIZE 0x100 +#define TVG_NBLINES(x) ((x) << 16) +#define TVG_LINE_SIZE(x) (x) + +#define TVG_COLOR1 0x104 +#define TVG_COL1_GREEN(x) ((x) << 12) +#define TVG_COL1_RED(x) (x) + +#define TVG_COLOR1_BIS 0x108 +#define TVG_COL1_BLUE(x) (x) + +#define TVG_COLOR2 0x10c +#define TVG_COL2_GREEN(x) ((x) << 12) +#define TVG_COL2_RED(x) (x) + +#define TVG_COLOR2_BIS 0x110 +#define TVG_COL2_BLUE(x) (x) + +#define TVG_STS 0x114 +#define TVG_STS_CTL 0x144 +#define TVG_STS_CLR 0x164 +#define TVG_STS_FLAG 0x184 +#define TVG_STS_RUNNING BIT(0) + +#define STS_CTL_EDGE(e) ((e) << 16) + +#define DPHY_LANES_MAP 0x198 +#define DAT_REMAP_CFG(b, l) ((l) << ((b) * 8)) + +#define DPI_IRQ_EN 0x1a0 +#define DPI_IRQ_CLR 0x1a4 +#define DPI_IRQ_STS 0x1a8 +#define PIXEL_BUF_OVERFLOW BIT(0) + +#define DPI_CFG 0x1ac +#define DPI_CFG_FIFO_DEPTH(x) ((x) >> 16) +#define DPI_CFG_FIFO_LEVEL(x) ((x) & GENMASK(15, 0)) + +#define TEST_GENERIC 0x1f0 +#define TEST_STATUS(x) ((x) >> 16) +#define TEST_CTRL(x) (x) + +#define ID_REG 0x1fc +#define REV_VENDOR_ID(x) (((x) & GENMASK(31, 20)) >> 20) +#define REV_PRODUCT_ID(x) (((x) & GENMASK(19, 12)) >> 12) +#define REV_HW(x) (((x) & GENMASK(11, 8)) >> 8) +#define REV_MAJOR(x) (((x) & GENMASK(7, 4)) >> 4) +#define REV_MINOR(x) ((x) & GENMASK(3, 0)) + +#define DSI_OUTPUT_PORT 0 +#define DSI_INPUT_PORT(inputid) (1 + (inputid)) + +#define DSI_HBP_FRAME_OVERHEAD 12 +#define DSI_HSA_FRAME_OVERHEAD 14 +#define DSI_HFP_FRAME_OVERHEAD 6 +#define DSI_HSS_VSS_VSE_FRAME_OVERHEAD 4 +#define DSI_BLANKING_FRAME_OVERHEAD 6 +#define DSI_NULL_FRAME_OVERHEAD 6 +#define DSI_EOT_PKT_SIZE 4 + +static inline struct cdns_dsi *input_to_dsi(struct cdns_dsi_input *input) +{ + return container_of(input, struct cdns_dsi, input); +} + +static inline struct cdns_dsi *to_cdns_dsi(struct mipi_dsi_host *host) +{ + return container_of(host, struct cdns_dsi, base); +} + +static inline struct cdns_dsi_input * +bridge_to_cdns_dsi_input(struct drm_bridge *bridge) +{ + return container_of(bridge, struct cdns_dsi_input, bridge); +} + +static unsigned int mode_to_dpi_hfp(const struct drm_display_mode *mode, + bool mode_valid_check) +{ + if (mode_valid_check) + return mode->hsync_start - mode->hdisplay; + + return mode->crtc_hsync_start - mode->crtc_hdisplay; +} + +static unsigned int dpi_to_dsi_timing(unsigned int dpi_timing, + unsigned int dpi_bpp, + unsigned int dsi_pkt_overhead) +{ + unsigned int dsi_timing = DIV_ROUND_UP(dpi_timing * dpi_bpp, 8); + + if (dsi_timing < dsi_pkt_overhead) + dsi_timing = 0; + else + dsi_timing -= dsi_pkt_overhead; + + return dsi_timing; +} + +static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi, + const struct drm_display_mode *mode, + struct cdns_dsi_cfg *dsi_cfg, + bool mode_valid_check) +{ + struct cdns_dsi_output *output = &dsi->output; + unsigned int tmp; + bool sync_pulse = false; + int bpp; + + memset(dsi_cfg, 0, sizeof(*dsi_cfg)); + + if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) + sync_pulse = true; + + bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format); + + if (mode_valid_check) + tmp = mode->htotal - + (sync_pulse ? mode->hsync_end : mode->hsync_start); + else + tmp = mode->crtc_htotal - + (sync_pulse ? + mode->crtc_hsync_end : mode->crtc_hsync_start); + + dsi_cfg->hbp = dpi_to_dsi_timing(tmp, bpp, DSI_HBP_FRAME_OVERHEAD); + + if (sync_pulse) { + if (mode_valid_check) + tmp = mode->hsync_end - mode->hsync_start; + else + tmp = mode->crtc_hsync_end - mode->crtc_hsync_start; + + dsi_cfg->hsa = dpi_to_dsi_timing(tmp, bpp, + DSI_HSA_FRAME_OVERHEAD); + } + + dsi_cfg->hact = dpi_to_dsi_timing(mode_valid_check ? + mode->hdisplay : mode->crtc_hdisplay, + bpp, 0); + dsi_cfg->hfp = dpi_to_dsi_timing(mode_to_dpi_hfp(mode, mode_valid_check), + bpp, DSI_HFP_FRAME_OVERHEAD); + + return 0; +} + +static int cdns_dsi_adjust_phy_config(struct cdns_dsi *dsi, + struct cdns_dsi_cfg *dsi_cfg, + struct phy_configure_opts_mipi_dphy *phy_cfg, + const struct drm_display_mode *mode, + bool mode_valid_check) +{ + struct cdns_dsi_output *output = &dsi->output; + unsigned long long dlane_bps; + unsigned long adj_dsi_htotal; + unsigned long dsi_htotal; + unsigned long dpi_htotal; + unsigned long dpi_hz; + unsigned int dsi_hfp_ext; + unsigned int lanes = output->dev->lanes; + + dsi_htotal = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD; + if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) + dsi_htotal += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD; + + dsi_htotal += dsi_cfg->hact; + dsi_htotal += dsi_cfg->hfp + DSI_HFP_FRAME_OVERHEAD; + + /* + * Make sure DSI htotal is aligned on a lane boundary when calculating + * the expected data rate. This is done by extending HFP in case of + * misalignment. + */ + adj_dsi_htotal = dsi_htotal; + if (dsi_htotal % lanes) + adj_dsi_htotal += lanes - (dsi_htotal % lanes); + + dpi_hz = (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000; + dlane_bps = (unsigned long long)dpi_hz * adj_dsi_htotal; + + /* data rate in bytes/sec is not an integer, refuse the mode. */ + dpi_htotal = mode_valid_check ? mode->htotal : mode->crtc_htotal; + if (do_div(dlane_bps, lanes * dpi_htotal)) + return -EINVAL; + + /* data rate was in bytes/sec, convert to bits/sec. */ + phy_cfg->hs_clk_rate = dlane_bps * 8; + + dsi_hfp_ext = adj_dsi_htotal - dsi_htotal; + dsi_cfg->hfp += dsi_hfp_ext; + dsi_cfg->htotal = dsi_htotal + dsi_hfp_ext; + + return 0; +} + +static int cdns_dsi_check_conf(struct cdns_dsi *dsi, + const struct drm_display_mode *mode, + struct cdns_dsi_cfg *dsi_cfg, + bool mode_valid_check) +{ + struct cdns_dsi_output *output = &dsi->output; + struct phy_configure_opts_mipi_dphy *phy_cfg = &output->phy_opts.mipi_dphy; + unsigned long dsi_hss_hsa_hse_hbp; + unsigned int nlanes = output->dev->lanes; + int ret; + + ret = cdns_dsi_mode2cfg(dsi, mode, dsi_cfg, mode_valid_check); + if (ret) + return ret; + + phy_mipi_dphy_get_default_config(mode->crtc_clock * 1000, + mipi_dsi_pixel_format_to_bpp(output->dev->format), + nlanes, phy_cfg); + + ret = cdns_dsi_adjust_phy_config(dsi, dsi_cfg, phy_cfg, mode, mode_valid_check); + if (ret) + return ret; + + ret = phy_validate(dsi->dphy, PHY_MODE_MIPI_DPHY, 0, &output->phy_opts); + if (ret) + return ret; + + dsi_hss_hsa_hse_hbp = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD; + if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) + dsi_hss_hsa_hse_hbp += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD; + + /* + * Make sure DPI(HFP) > DSI(HSS+HSA+HSE+HBP) to guarantee that the FIFO + * is empty before we start a receiving a new line on the DPI + * interface. + */ + if ((u64)phy_cfg->hs_clk_rate * + mode_to_dpi_hfp(mode, mode_valid_check) * nlanes < + (u64)dsi_hss_hsa_hse_hbp * + (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000) + return -EINVAL; + + return 0; +} + +static int cdns_dsi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); + struct cdns_dsi *dsi = input_to_dsi(input); + struct cdns_dsi_output *output = &dsi->output; + + if (!drm_core_check_feature(bridge->dev, DRIVER_ATOMIC)) { + dev_err(dsi->base.dev, + "cdns-dsi driver is only compatible with DRM devices supporting atomic updates"); + return -ENOTSUPP; + } + + return drm_bridge_attach(bridge->encoder, output->bridge, bridge, + flags); +} + +static enum drm_mode_status +cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); + struct cdns_dsi *dsi = input_to_dsi(input); + struct cdns_dsi_output *output = &dsi->output; + struct cdns_dsi_cfg dsi_cfg; + int bpp, ret; + + /* + * VFP_DSI should be less than VFP_DPI and VFP_DSI should be at + * least 1. + */ + if (mode->vtotal - mode->vsync_end < 2) + return MODE_V_ILLEGAL; + + /* VSA_DSI = VSA_DPI and must be at least 2. */ + if (mode->vsync_end - mode->vsync_start < 2) + return MODE_V_ILLEGAL; + + /* HACT must be 32-bits aligned. */ + bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format); + if ((mode->hdisplay * bpp) % 32) + return MODE_H_ILLEGAL; + + ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, true); + if (ret) + return MODE_BAD; + + return MODE_OK; +} + +static void cdns_dsi_bridge_disable(struct drm_bridge *bridge) +{ + struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); + struct cdns_dsi *dsi = input_to_dsi(input); + u32 val; + + val = readl(dsi->regs + MCTL_MAIN_DATA_CTL); + val &= ~(IF_VID_SELECT_MASK | IF_VID_MODE | VID_EN | HOST_EOT_GEN | + DISP_EOT_GEN); + writel(val, dsi->regs + MCTL_MAIN_DATA_CTL); + + val = readl(dsi->regs + MCTL_MAIN_EN) & ~IF_EN(input->id); + writel(val, dsi->regs + MCTL_MAIN_EN); + + if (dsi->platform_ops && dsi->platform_ops->disable) + dsi->platform_ops->disable(dsi); + + pm_runtime_put(dsi->base.dev); +} + +static void cdns_dsi_bridge_post_disable(struct drm_bridge *bridge) +{ + struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); + struct cdns_dsi *dsi = input_to_dsi(input); + + pm_runtime_put(dsi->base.dev); +} + +static void cdns_dsi_hs_init(struct cdns_dsi *dsi) +{ + struct cdns_dsi_output *output = &dsi->output; + u32 status; + + if (dsi->phy_initialized) + return; + /* + * Power all internal DPHY blocks down and maintain their reset line + * asserted before changing the DPHY config. + */ + writel(DPHY_CMN_PSO | DPHY_PLL_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | + DPHY_CMN_PDN | DPHY_PLL_PDN, + dsi->regs + MCTL_DPHY_CFG0); + + phy_init(dsi->dphy); + phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY); + phy_configure(dsi->dphy, &output->phy_opts); + phy_power_on(dsi->dphy); + + /* Activate the PLL and wait until it's locked. */ + writel(PLL_LOCKED, dsi->regs + MCTL_MAIN_STS_CLR); + writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN, + dsi->regs + MCTL_DPHY_CFG0); + WARN_ON_ONCE(readl_poll_timeout(dsi->regs + MCTL_MAIN_STS, status, + status & PLL_LOCKED, 100, 100)); + /* De-assert data and clock reset lines. */ + writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN | + DPHY_D_RSTB(output->dev->lanes) | DPHY_C_RSTB, + dsi->regs + MCTL_DPHY_CFG0); + dsi->phy_initialized = true; +} + +static void cdns_dsi_init_link(struct cdns_dsi *dsi) +{ + struct cdns_dsi_output *output = &dsi->output; + unsigned long sysclk_period, ulpout; + u32 val; + int i; + + if (dsi->link_initialized) + return; + + val = 0; + for (i = 1; i < output->dev->lanes; i++) + val |= DATA_LANE_EN(i); + + if (!(output->dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) + val |= CLK_CONTINUOUS; + + writel(val, dsi->regs + MCTL_MAIN_PHY_CTL); + + /* ULPOUT should be set to 1ms and is expressed in sysclk cycles. */ + sysclk_period = NSEC_PER_SEC / clk_get_rate(dsi->dsi_sys_clk); + ulpout = DIV_ROUND_UP(NSEC_PER_MSEC, sysclk_period); + writel(CLK_LANE_ULPOUT_TIME(ulpout) | DATA_LANE_ULPOUT_TIME(ulpout), + dsi->regs + MCTL_ULPOUT_TIME); + + writel(LINK_EN, dsi->regs + MCTL_MAIN_DATA_CTL); + + val = CLK_LANE_EN | PLL_START; + for (i = 0; i < output->dev->lanes; i++) + val |= DATA_LANE_START(i); + + writel(val, dsi->regs + MCTL_MAIN_EN); + + dsi->link_initialized = true; +} + +static void cdns_dsi_bridge_enable(struct drm_bridge *bridge) +{ + struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); + struct cdns_dsi *dsi = input_to_dsi(input); + struct cdns_dsi_output *output = &dsi->output; + struct drm_display_mode *mode; + struct phy_configure_opts_mipi_dphy *phy_cfg = &output->phy_opts.mipi_dphy; + unsigned long tx_byte_period; + struct cdns_dsi_cfg dsi_cfg; + u32 tmp, reg_wakeup, div; + int nlanes; + + if (WARN_ON(pm_runtime_get_sync(dsi->base.dev) < 0)) + return; + + if (dsi->platform_ops && dsi->platform_ops->enable) + dsi->platform_ops->enable(dsi); + + mode = &bridge->encoder->crtc->state->adjusted_mode; + nlanes = output->dev->lanes; + + WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, false)); + + cdns_dsi_hs_init(dsi); + cdns_dsi_init_link(dsi); + + writel(HBP_LEN(dsi_cfg.hbp) | HSA_LEN(dsi_cfg.hsa), + dsi->regs + VID_HSIZE1); + writel(HFP_LEN(dsi_cfg.hfp) | HACT_LEN(dsi_cfg.hact), + dsi->regs + VID_HSIZE2); + + writel(VBP_LEN(mode->crtc_vtotal - mode->crtc_vsync_end - 1) | + VFP_LEN(mode->crtc_vsync_start - mode->crtc_vdisplay) | + VSA_LEN(mode->crtc_vsync_end - mode->crtc_vsync_start + 1), + dsi->regs + VID_VSIZE1); + writel(mode->crtc_vdisplay, dsi->regs + VID_VSIZE2); + + tmp = dsi_cfg.htotal - + (dsi_cfg.hsa + DSI_BLANKING_FRAME_OVERHEAD + + DSI_HSA_FRAME_OVERHEAD); + writel(BLK_LINE_PULSE_PKT_LEN(tmp), dsi->regs + VID_BLKSIZE2); + if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) + writel(MAX_LINE_LIMIT(tmp - DSI_NULL_FRAME_OVERHEAD), + dsi->regs + VID_VCA_SETTING2); + + tmp = dsi_cfg.htotal - + (DSI_HSS_VSS_VSE_FRAME_OVERHEAD + DSI_BLANKING_FRAME_OVERHEAD); + writel(BLK_LINE_EVENT_PKT_LEN(tmp), dsi->regs + VID_BLKSIZE1); + if (!(output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)) + writel(MAX_LINE_LIMIT(tmp - DSI_NULL_FRAME_OVERHEAD), + dsi->regs + VID_VCA_SETTING2); + + tmp = DIV_ROUND_UP(dsi_cfg.htotal, nlanes) - + DIV_ROUND_UP(dsi_cfg.hsa, nlanes); + + if (!(output->dev->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET)) + tmp -= DIV_ROUND_UP(DSI_EOT_PKT_SIZE, nlanes); + + tx_byte_period = DIV_ROUND_DOWN_ULL((u64)NSEC_PER_SEC * 8, + phy_cfg->hs_clk_rate); + reg_wakeup = (phy_cfg->hs_prepare + phy_cfg->hs_zero) / tx_byte_period; + writel(REG_WAKEUP_TIME(reg_wakeup) | REG_LINE_DURATION(tmp), + dsi->regs + VID_DPHY_TIME); + + /* + * HSTX and LPRX timeouts are both expressed in TX byte clk cycles and + * both should be set to at least the time it takes to transmit a + * frame. + */ + tmp = NSEC_PER_SEC / drm_mode_vrefresh(mode); + tmp /= tx_byte_period; + + for (div = 0; div <= CLK_DIV_MAX; div++) { + if (tmp <= HSTX_TIMEOUT_MAX) + break; + + tmp >>= 1; + } + + if (tmp > HSTX_TIMEOUT_MAX) + tmp = HSTX_TIMEOUT_MAX; + + writel(CLK_DIV(div) | HSTX_TIMEOUT(tmp), + dsi->regs + MCTL_DPHY_TIMEOUT1); + + writel(LPRX_TIMEOUT(tmp), dsi->regs + MCTL_DPHY_TIMEOUT2); + + if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO) { + switch (output->dev->format) { + case MIPI_DSI_FMT_RGB888: + tmp = VID_PIXEL_MODE_RGB888 | + VID_DATATYPE(MIPI_DSI_PACKED_PIXEL_STREAM_24); + break; + + case MIPI_DSI_FMT_RGB666: + tmp = VID_PIXEL_MODE_RGB666 | + VID_DATATYPE(MIPI_DSI_PIXEL_STREAM_3BYTE_18); + break; + + case MIPI_DSI_FMT_RGB666_PACKED: + tmp = VID_PIXEL_MODE_RGB666_PACKED | + VID_DATATYPE(MIPI_DSI_PACKED_PIXEL_STREAM_18); + break; + + case MIPI_DSI_FMT_RGB565: + tmp = VID_PIXEL_MODE_RGB565 | + VID_DATATYPE(MIPI_DSI_PACKED_PIXEL_STREAM_16); + break; + + default: + dev_err(dsi->base.dev, "Unsupported DSI format\n"); + return; + } + + if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) + tmp |= SYNC_PULSE_ACTIVE | SYNC_PULSE_HORIZONTAL; + + tmp |= REG_BLKLINE_MODE(REG_BLK_MODE_BLANKING_PKT) | + REG_BLKEOL_MODE(REG_BLK_MODE_BLANKING_PKT) | + RECOVERY_MODE(RECOVERY_MODE_NEXT_HSYNC) | + VID_IGNORE_MISS_VSYNC; + + writel(tmp, dsi->regs + VID_MAIN_CTL); + } + + tmp = readl(dsi->regs + MCTL_MAIN_DATA_CTL); + tmp &= ~(IF_VID_SELECT_MASK | HOST_EOT_GEN | IF_VID_MODE); + + if (!(output->dev->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET)) + tmp |= HOST_EOT_GEN; + + if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO) + tmp |= IF_VID_MODE | IF_VID_SELECT(input->id) | VID_EN; + + writel(tmp, dsi->regs + MCTL_MAIN_DATA_CTL); + + tmp = readl(dsi->regs + MCTL_MAIN_EN) | IF_EN(input->id); + writel(tmp, dsi->regs + MCTL_MAIN_EN); +} + +static void cdns_dsi_bridge_pre_enable(struct drm_bridge *bridge) +{ + struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); + struct cdns_dsi *dsi = input_to_dsi(input); + + if (WARN_ON(pm_runtime_get_sync(dsi->base.dev) < 0)) + return; + + cdns_dsi_init_link(dsi); + cdns_dsi_hs_init(dsi); +} + +static const struct drm_bridge_funcs cdns_dsi_bridge_funcs = { + .attach = cdns_dsi_bridge_attach, + .mode_valid = cdns_dsi_bridge_mode_valid, + .disable = cdns_dsi_bridge_disable, + .pre_enable = cdns_dsi_bridge_pre_enable, + .enable = cdns_dsi_bridge_enable, + .post_disable = cdns_dsi_bridge_post_disable, +}; + +static int cdns_dsi_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *dev) +{ + struct cdns_dsi *dsi = to_cdns_dsi(host); + struct cdns_dsi_output *output = &dsi->output; + struct cdns_dsi_input *input = &dsi->input; + struct drm_bridge *bridge; + struct drm_panel *panel; + struct device_node *np; + int ret; + + /* + * We currently do not support connecting several DSI devices to the + * same host. In order to support that we'd need the DRM bridge + * framework to allow dynamic reconfiguration of the bridge chain. + */ + if (output->dev) + return -EBUSY; + + /* We do not support burst mode yet. */ + if (dev->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) + return -ENOTSUPP; + + /* + * The host <-> device link might be described using an OF-graph + * representation, in this case we extract the device of_node from + * this representation, otherwise we use dsidev->dev.of_node which + * should have been filled by the core. + */ + np = of_graph_get_remote_node(dsi->base.dev->of_node, DSI_OUTPUT_PORT, + dev->channel); + if (!np) + np = of_node_get(dev->dev.of_node); + + panel = of_drm_find_panel(np); + if (!IS_ERR(panel)) { + bridge = drm_panel_bridge_add_typed(panel, + DRM_MODE_CONNECTOR_DSI); + } else { + bridge = of_drm_find_bridge(dev->dev.of_node); + if (!bridge) + bridge = ERR_PTR(-EINVAL); + } + + of_node_put(np); + + if (IS_ERR(bridge)) { + ret = PTR_ERR(bridge); + dev_err(host->dev, "failed to add DSI device %s (err = %d)", + dev->name, ret); + return ret; + } + + output->dev = dev; + output->bridge = bridge; + output->panel = panel; + + /* + * The DSI output has been properly configured, we can now safely + * register the input to the bridge framework so that it can take place + * in a display pipeline. + */ + drm_bridge_add(&input->bridge); + + return 0; +} + +static int cdns_dsi_detach(struct mipi_dsi_host *host, + struct mipi_dsi_device *dev) +{ + struct cdns_dsi *dsi = to_cdns_dsi(host); + struct cdns_dsi_output *output = &dsi->output; + struct cdns_dsi_input *input = &dsi->input; + + drm_bridge_remove(&input->bridge); + if (output->panel) + drm_panel_bridge_remove(output->bridge); + + return 0; +} + +static irqreturn_t cdns_dsi_interrupt(int irq, void *data) +{ + struct cdns_dsi *dsi = data; + irqreturn_t ret = IRQ_NONE; + u32 flag, ctl; + + flag = readl(dsi->regs + DIRECT_CMD_STS_FLAG); + if (flag) { + ctl = readl(dsi->regs + DIRECT_CMD_STS_CTL); + ctl &= ~flag; + writel(ctl, dsi->regs + DIRECT_CMD_STS_CTL); + complete(&dsi->direct_cmd_comp); + ret = IRQ_HANDLED; + } + + return ret; +} + +static ssize_t cdns_dsi_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct cdns_dsi *dsi = to_cdns_dsi(host); + u32 cmd, sts, val, wait = WRITE_COMPLETED, ctl = 0; + struct mipi_dsi_packet packet; + int ret, i, tx_len, rx_len; + + ret = pm_runtime_resume_and_get(host->dev); + if (ret < 0) + return ret; + + cdns_dsi_init_link(dsi); + + ret = mipi_dsi_create_packet(&packet, msg); + if (ret) + goto out; + + tx_len = msg->tx_buf ? msg->tx_len : 0; + rx_len = msg->rx_buf ? msg->rx_len : 0; + + /* For read operations, the maximum TX len is 2. */ + if (rx_len && tx_len > 2) { + ret = -ENOTSUPP; + goto out; + } + + /* TX len is limited by the CMD FIFO depth. */ + if (tx_len > dsi->direct_cmd_fifo_depth) { + ret = -ENOTSUPP; + goto out; + } + + /* RX len is limited by the RX FIFO depth. */ + if (rx_len > dsi->rx_fifo_depth) { + ret = -ENOTSUPP; + goto out; + } + + cmd = CMD_SIZE(tx_len) | CMD_VCHAN_ID(msg->channel) | + CMD_DATATYPE(msg->type); + + if (msg->flags & MIPI_DSI_MSG_USE_LPM) + cmd |= CMD_LP_EN; + + if (mipi_dsi_packet_format_is_long(msg->type)) + cmd |= CMD_LONG; + + if (rx_len) { + cmd |= READ_CMD; + wait = READ_COMPLETED_WITH_ERR | READ_COMPLETED; + ctl = READ_EN | BTA_EN; + } else if (msg->flags & MIPI_DSI_MSG_REQ_ACK) { + cmd |= BTA_REQ; + wait = ACK_WITH_ERR_RCVD | ACK_RCVD; + ctl = BTA_EN; + } + + writel(readl(dsi->regs + MCTL_MAIN_DATA_CTL) | ctl, + dsi->regs + MCTL_MAIN_DATA_CTL); + + writel(cmd, dsi->regs + DIRECT_CMD_MAIN_SETTINGS); + + for (i = 0; i < tx_len; i += 4) { + const u8 *buf = msg->tx_buf; + int j; + + val = 0; + for (j = 0; j < 4 && j + i < tx_len; j++) + val |= (u32)buf[i + j] << (8 * j); + + writel(val, dsi->regs + DIRECT_CMD_WRDATA); + } + + /* Clear status flags before sending the command. */ + writel(wait, dsi->regs + DIRECT_CMD_STS_CLR); + writel(wait, dsi->regs + DIRECT_CMD_STS_CTL); + reinit_completion(&dsi->direct_cmd_comp); + writel(0, dsi->regs + DIRECT_CMD_SEND); + + wait_for_completion_timeout(&dsi->direct_cmd_comp, + msecs_to_jiffies(1000)); + + sts = readl(dsi->regs + DIRECT_CMD_STS); + writel(wait, dsi->regs + DIRECT_CMD_STS_CLR); + writel(0, dsi->regs + DIRECT_CMD_STS_CTL); + + writel(readl(dsi->regs + MCTL_MAIN_DATA_CTL) & ~ctl, + dsi->regs + MCTL_MAIN_DATA_CTL); + + /* We did not receive the events we were waiting for. */ + if (!(sts & wait)) { + ret = -ETIMEDOUT; + goto out; + } + + /* 'READ' or 'WRITE with ACK' failed. */ + if (sts & (READ_COMPLETED_WITH_ERR | ACK_WITH_ERR_RCVD)) { + ret = -EIO; + goto out; + } + + for (i = 0; i < rx_len; i += 4) { + u8 *buf = msg->rx_buf; + int j; + + val = readl(dsi->regs + DIRECT_CMD_RDDATA); + for (j = 0; j < 4 && j + i < rx_len; j++) + buf[i + j] = val >> (8 * j); + } + +out: + pm_runtime_put(host->dev); + return ret; +} + +static const struct mipi_dsi_host_ops cdns_dsi_ops = { + .attach = cdns_dsi_attach, + .detach = cdns_dsi_detach, + .transfer = cdns_dsi_transfer, +}; + +static int __maybe_unused cdns_dsi_resume(struct device *dev) +{ + struct cdns_dsi *dsi = dev_get_drvdata(dev); + + reset_control_deassert(dsi->dsi_p_rst); + clk_prepare_enable(dsi->dsi_p_clk); + clk_prepare_enable(dsi->dsi_sys_clk); + + return 0; +} + +static int __maybe_unused cdns_dsi_suspend(struct device *dev) +{ + struct cdns_dsi *dsi = dev_get_drvdata(dev); + + clk_disable_unprepare(dsi->dsi_sys_clk); + clk_disable_unprepare(dsi->dsi_p_clk); + reset_control_assert(dsi->dsi_p_rst); + dsi->link_initialized = false; + return 0; +} + +static UNIVERSAL_DEV_PM_OPS(cdns_dsi_pm_ops, cdns_dsi_suspend, cdns_dsi_resume, + NULL); + +static int cdns_dsi_drm_probe(struct platform_device *pdev) +{ + struct cdns_dsi *dsi; + struct cdns_dsi_input *input; + int ret, irq; + u32 val; + + dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; + + platform_set_drvdata(pdev, dsi); + + input = &dsi->input; + + dsi->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dsi->regs)) + return PTR_ERR(dsi->regs); + + dsi->dsi_p_clk = devm_clk_get(&pdev->dev, "dsi_p_clk"); + if (IS_ERR(dsi->dsi_p_clk)) + return PTR_ERR(dsi->dsi_p_clk); + + dsi->dsi_p_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, + "dsi_p_rst"); + if (IS_ERR(dsi->dsi_p_rst)) + return PTR_ERR(dsi->dsi_p_rst); + + dsi->dsi_sys_clk = devm_clk_get(&pdev->dev, "dsi_sys_clk"); + if (IS_ERR(dsi->dsi_sys_clk)) + return PTR_ERR(dsi->dsi_sys_clk); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + dsi->dphy = devm_phy_get(&pdev->dev, "dphy"); + if (IS_ERR(dsi->dphy)) + return PTR_ERR(dsi->dphy); + + ret = clk_prepare_enable(dsi->dsi_p_clk); + if (ret) + return ret; + + val = readl(dsi->regs + ID_REG); + if (REV_VENDOR_ID(val) != 0xcad) { + dev_err(&pdev->dev, "invalid vendor id\n"); + ret = -EINVAL; + goto err_disable_pclk; + } + + dsi->platform_ops = of_device_get_match_data(&pdev->dev); + + val = readl(dsi->regs + IP_CONF); + dsi->direct_cmd_fifo_depth = 1 << (DIRCMD_FIFO_DEPTH(val) + 2); + dsi->rx_fifo_depth = RX_FIFO_DEPTH(val); + init_completion(&dsi->direct_cmd_comp); + + writel(0, dsi->regs + MCTL_MAIN_DATA_CTL); + writel(0, dsi->regs + MCTL_MAIN_EN); + writel(0, dsi->regs + MCTL_MAIN_PHY_CTL); + + /* + * We only support the DPI input, so force input->id to + * CDNS_DPI_INPUT. + */ + input->id = CDNS_DPI_INPUT; + input->bridge.funcs = &cdns_dsi_bridge_funcs; + input->bridge.of_node = pdev->dev.of_node; + + /* Mask all interrupts before registering the IRQ handler. */ + writel(0, dsi->regs + MCTL_MAIN_STS_CTL); + writel(0, dsi->regs + MCTL_DPHY_ERR_CTL1); + writel(0, dsi->regs + CMD_MODE_STS_CTL); + writel(0, dsi->regs + DIRECT_CMD_STS_CTL); + writel(0, dsi->regs + DIRECT_CMD_RD_STS_CTL); + writel(0, dsi->regs + VID_MODE_STS_CTL); + writel(0, dsi->regs + TVG_STS_CTL); + writel(0, dsi->regs + DPI_IRQ_EN); + ret = devm_request_irq(&pdev->dev, irq, cdns_dsi_interrupt, 0, + dev_name(&pdev->dev), dsi); + if (ret) + goto err_disable_pclk; + + pm_runtime_enable(&pdev->dev); + dsi->base.dev = &pdev->dev; + dsi->base.ops = &cdns_dsi_ops; + + if (dsi->platform_ops && dsi->platform_ops->init) { + ret = dsi->platform_ops->init(dsi); + if (ret != 0) { + dev_err(&pdev->dev, "platform initialization failed: %d\n", + ret); + goto err_disable_runtime_pm; + } + } + + ret = mipi_dsi_host_register(&dsi->base); + if (ret) + goto err_deinit_platform; + + clk_disable_unprepare(dsi->dsi_p_clk); + + return 0; + +err_deinit_platform: + if (dsi->platform_ops && dsi->platform_ops->deinit) + dsi->platform_ops->deinit(dsi); + +err_disable_runtime_pm: + pm_runtime_disable(&pdev->dev); + +err_disable_pclk: + clk_disable_unprepare(dsi->dsi_p_clk); + + return ret; +} + +static int cdns_dsi_drm_remove(struct platform_device *pdev) +{ + struct cdns_dsi *dsi = platform_get_drvdata(pdev); + + mipi_dsi_host_unregister(&dsi->base); + + if (dsi->platform_ops && dsi->platform_ops->deinit) + dsi->platform_ops->deinit(dsi); + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id cdns_dsi_of_match[] = { + { .compatible = "cdns,dsi" }, +#ifdef CONFIG_DRM_CDNS_DSI_J721E + { .compatible = "ti,j721e-dsi", .data = &dsi_ti_j721e_ops, }, +#endif + { }, +}; +MODULE_DEVICE_TABLE(of, cdns_dsi_of_match); + +static struct platform_driver cdns_dsi_platform_driver = { + .probe = cdns_dsi_drm_probe, + .remove = cdns_dsi_drm_remove, + .driver = { + .name = "cdns-dsi", + .of_match_table = cdns_dsi_of_match, + .pm = &cdns_dsi_pm_ops, + }, +}; +module_platform_driver(cdns_dsi_platform_driver); + +MODULE_AUTHOR("Boris Brezillon <boris.brezillon@bootlin.com>"); +MODULE_DESCRIPTION("Cadence DSI driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cdns-dsi"); + diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.h b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.h new file mode 100644 index 000000000000..ca7ea2da635c --- /dev/null +++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright: 2017 Cadence Design Systems, Inc. + * + * Author: Boris Brezillon <boris.brezillon@bootlin.com> + */ + +#ifndef __CDNS_DSI_H__ +#define __CDNS_DSI_H__ + +#include <drm/drm_bridge.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> + +#include <linux/bits.h> +#include <linux/completion.h> +#include <linux/phy/phy.h> + +struct clk; +struct reset_control; + +struct cdns_dsi_output { + struct mipi_dsi_device *dev; + struct drm_panel *panel; + struct drm_bridge *bridge; + union phy_configure_opts phy_opts; +}; + +enum cdns_dsi_input_id { + CDNS_SDI_INPUT, + CDNS_DPI_INPUT, + CDNS_DSC_INPUT, +}; + +struct cdns_dsi_cfg { + unsigned int hfp; + unsigned int hsa; + unsigned int hbp; + unsigned int hact; + unsigned int htotal; +}; + +struct cdns_dsi_input { + enum cdns_dsi_input_id id; + struct drm_bridge bridge; +}; + +struct cdns_dsi; + +/** + * struct cdns_dsi_platform_ops - CDNS DSI Platform operations + * @init: Called in the CDNS DSI probe + * @deinit: Called in the CDNS DSI remove + * @enable: Called at the beginning of CDNS DSI bridge enable + * @disable: Called at the end of CDNS DSI bridge disable + */ +struct cdns_dsi_platform_ops { + int (*init)(struct cdns_dsi *dsi); + void (*deinit)(struct cdns_dsi *dsi); + void (*enable)(struct cdns_dsi *dsi); + void (*disable)(struct cdns_dsi *dsi); +}; + +struct cdns_dsi { + struct mipi_dsi_host base; + void __iomem *regs; +#ifdef CONFIG_DRM_CDNS_DSI_J721E + void __iomem *j721e_regs; +#endif + const struct cdns_dsi_platform_ops *platform_ops; + struct cdns_dsi_input input; + struct cdns_dsi_output output; + unsigned int direct_cmd_fifo_depth; + unsigned int rx_fifo_depth; + struct completion direct_cmd_comp; + struct clk *dsi_p_clk; + struct reset_control *dsi_p_rst; + struct clk *dsi_sys_clk; + bool link_initialized; + bool phy_initialized; + struct phy *dphy; +}; + +#endif /* !__CDNS_DSI_H__ */ diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-j721e.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-j721e.c new file mode 100644 index 000000000000..b654d4b3cb5c --- /dev/null +++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-j721e.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TI j721e Cadence DSI wrapper + * + * Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Rahul T R <r-ravikumar@ti.com> + */ + +#include <linux/io.h> +#include <linux/platform_device.h> + +#include "cdns-dsi-j721e.h" + +#define DSI_WRAP_REVISION 0x0 +#define DSI_WRAP_DPI_CONTROL 0x4 +#define DSI_WRAP_DSC_CONTROL 0x8 +#define DSI_WRAP_DPI_SECURE 0xc +#define DSI_WRAP_DSI_0_ASF_STATUS 0x10 + +#define DSI_WRAP_DPI_0_EN BIT(0) +#define DSI_WRAP_DSI2_MUX_SEL BIT(4) + +static int cdns_dsi_j721e_init(struct cdns_dsi *dsi) +{ + struct platform_device *pdev = to_platform_device(dsi->base.dev); + + dsi->j721e_regs = devm_platform_ioremap_resource(pdev, 1); + return PTR_ERR_OR_ZERO(dsi->j721e_regs); +} + +static void cdns_dsi_j721e_enable(struct cdns_dsi *dsi) +{ + /* + * Enable DPI0 as its input. DSS0 DPI2 is connected + * to DSI DPI0. This is the only supported configuration on + * J721E. + */ + writel(DSI_WRAP_DPI_0_EN, dsi->j721e_regs + DSI_WRAP_DPI_CONTROL); +} + +static void cdns_dsi_j721e_disable(struct cdns_dsi *dsi) +{ + /* Put everything to defaults */ + writel(0, dsi->j721e_regs + DSI_WRAP_DPI_CONTROL); +} + +const struct cdns_dsi_platform_ops dsi_ti_j721e_ops = { + .init = cdns_dsi_j721e_init, + .enable = cdns_dsi_j721e_enable, + .disable = cdns_dsi_j721e_disable, +}; diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-j721e.h b/drivers/gpu/drm/bridge/cadence/cdns-dsi-j721e.h new file mode 100644 index 000000000000..275e5e8e7583 --- /dev/null +++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-j721e.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * TI j721e Cadence DSI wrapper + * + * Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Rahul T R <r-ravikumar@ti.com> + */ + +#ifndef __CDNS_DSI_J721E_H__ +#define __CDNS_DSI_J721E_H__ + +#include "cdns-dsi-core.h" + +extern const struct cdns_dsi_platform_ops dsi_ti_j721e_ops; + +#endif /* !__CDNS_DSI_J721E_H__ */ diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index 31442a922502..f6822dfa3805 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -43,7 +43,6 @@ #include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_connector.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_print.h> diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c deleted file mode 100644 index 20bece84ff8c..000000000000 --- a/drivers/gpu/drm/bridge/cdns-dsi.c +++ /dev/null @@ -1,1330 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright: 2017 Cadence Design Systems, Inc. - * - * Author: Boris Brezillon <boris.brezillon@bootlin.com> - */ - -#include <drm/drm_atomic_helper.h> -#include <drm/drm_bridge.h> -#include <drm/drm_drv.h> -#include <drm/drm_mipi_dsi.h> -#include <drm/drm_panel.h> -#include <drm/drm_probe_helper.h> -#include <video/mipi_display.h> - -#include <linux/clk.h> -#include <linux/interrupt.h> -#include <linux/iopoll.h> -#include <linux/module.h> -#include <linux/of_address.h> -#include <linux/of_graph.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/reset.h> - -#include <linux/phy/phy.h> -#include <linux/phy/phy-mipi-dphy.h> - -#define IP_CONF 0x0 -#define SP_HS_FIFO_DEPTH(x) (((x) & GENMASK(30, 26)) >> 26) -#define SP_LP_FIFO_DEPTH(x) (((x) & GENMASK(25, 21)) >> 21) -#define VRS_FIFO_DEPTH(x) (((x) & GENMASK(20, 16)) >> 16) -#define DIRCMD_FIFO_DEPTH(x) (((x) & GENMASK(15, 13)) >> 13) -#define SDI_IFACE_32 BIT(12) -#define INTERNAL_DATAPATH_32 (0 << 10) -#define INTERNAL_DATAPATH_16 (1 << 10) -#define INTERNAL_DATAPATH_8 (3 << 10) -#define INTERNAL_DATAPATH_SIZE ((x) & GENMASK(11, 10)) -#define NUM_IFACE(x) ((((x) & GENMASK(9, 8)) >> 8) + 1) -#define MAX_LANE_NB(x) (((x) & GENMASK(7, 6)) >> 6) -#define RX_FIFO_DEPTH(x) ((x) & GENMASK(5, 0)) - -#define MCTL_MAIN_DATA_CTL 0x4 -#define TE_MIPI_POLLING_EN BIT(25) -#define TE_HW_POLLING_EN BIT(24) -#define DISP_EOT_GEN BIT(18) -#define HOST_EOT_GEN BIT(17) -#define DISP_GEN_CHECKSUM BIT(16) -#define DISP_GEN_ECC BIT(15) -#define BTA_EN BIT(14) -#define READ_EN BIT(13) -#define REG_TE_EN BIT(12) -#define IF_TE_EN(x) BIT(8 + (x)) -#define TVG_SEL BIT(6) -#define VID_EN BIT(5) -#define IF_VID_SELECT(x) ((x) << 2) -#define IF_VID_SELECT_MASK GENMASK(3, 2) -#define IF_VID_MODE BIT(1) -#define LINK_EN BIT(0) - -#define MCTL_MAIN_PHY_CTL 0x8 -#define HS_INVERT_DAT(x) BIT(19 + ((x) * 2)) -#define SWAP_PINS_DAT(x) BIT(18 + ((x) * 2)) -#define HS_INVERT_CLK BIT(17) -#define SWAP_PINS_CLK BIT(16) -#define HS_SKEWCAL_EN BIT(15) -#define WAIT_BURST_TIME(x) ((x) << 10) -#define DATA_ULPM_EN(x) BIT(6 + (x)) -#define CLK_ULPM_EN BIT(5) -#define CLK_CONTINUOUS BIT(4) -#define DATA_LANE_EN(x) BIT((x) - 1) - -#define MCTL_MAIN_EN 0xc -#define DATA_FORCE_STOP BIT(17) -#define CLK_FORCE_STOP BIT(16) -#define IF_EN(x) BIT(13 + (x)) -#define DATA_LANE_ULPM_REQ(l) BIT(9 + (l)) -#define CLK_LANE_ULPM_REQ BIT(8) -#define DATA_LANE_START(x) BIT(4 + (x)) -#define CLK_LANE_EN BIT(3) -#define PLL_START BIT(0) - -#define MCTL_DPHY_CFG0 0x10 -#define DPHY_C_RSTB BIT(20) -#define DPHY_D_RSTB(x) GENMASK(15 + (x), 16) -#define DPHY_PLL_PDN BIT(10) -#define DPHY_CMN_PDN BIT(9) -#define DPHY_C_PDN BIT(8) -#define DPHY_D_PDN(x) GENMASK(3 + (x), 4) -#define DPHY_ALL_D_PDN GENMASK(7, 4) -#define DPHY_PLL_PSO BIT(1) -#define DPHY_CMN_PSO BIT(0) - -#define MCTL_DPHY_TIMEOUT1 0x14 -#define HSTX_TIMEOUT(x) ((x) << 4) -#define HSTX_TIMEOUT_MAX GENMASK(17, 0) -#define CLK_DIV(x) (x) -#define CLK_DIV_MAX GENMASK(3, 0) - -#define MCTL_DPHY_TIMEOUT2 0x18 -#define LPRX_TIMEOUT(x) (x) - -#define MCTL_ULPOUT_TIME 0x1c -#define DATA_LANE_ULPOUT_TIME(x) ((x) << 9) -#define CLK_LANE_ULPOUT_TIME(x) (x) - -#define MCTL_3DVIDEO_CTL 0x20 -#define VID_VSYNC_3D_EN BIT(7) -#define VID_VSYNC_3D_LR BIT(5) -#define VID_VSYNC_3D_SECOND_EN BIT(4) -#define VID_VSYNC_3DFORMAT_LINE (0 << 2) -#define VID_VSYNC_3DFORMAT_FRAME (1 << 2) -#define VID_VSYNC_3DFORMAT_PIXEL (2 << 2) -#define VID_VSYNC_3DMODE_OFF 0 -#define VID_VSYNC_3DMODE_PORTRAIT 1 -#define VID_VSYNC_3DMODE_LANDSCAPE 2 - -#define MCTL_MAIN_STS 0x24 -#define MCTL_MAIN_STS_CTL 0x130 -#define MCTL_MAIN_STS_CLR 0x150 -#define MCTL_MAIN_STS_FLAG 0x170 -#define HS_SKEWCAL_DONE BIT(11) -#define IF_UNTERM_PKT_ERR(x) BIT(8 + (x)) -#define LPRX_TIMEOUT_ERR BIT(7) -#define HSTX_TIMEOUT_ERR BIT(6) -#define DATA_LANE_RDY(l) BIT(2 + (l)) -#define CLK_LANE_RDY BIT(1) -#define PLL_LOCKED BIT(0) - -#define MCTL_DPHY_ERR 0x28 -#define MCTL_DPHY_ERR_CTL1 0x148 -#define MCTL_DPHY_ERR_CLR 0x168 -#define MCTL_DPHY_ERR_FLAG 0x188 -#define ERR_CONT_LP(x, l) BIT(18 + ((x) * 4) + (l)) -#define ERR_CONTROL(l) BIT(14 + (l)) -#define ERR_SYNESC(l) BIT(10 + (l)) -#define ERR_ESC(l) BIT(6 + (l)) - -#define MCTL_DPHY_ERR_CTL2 0x14c -#define ERR_CONT_LP_EDGE(x, l) BIT(12 + ((x) * 4) + (l)) -#define ERR_CONTROL_EDGE(l) BIT(8 + (l)) -#define ERR_SYN_ESC_EDGE(l) BIT(4 + (l)) -#define ERR_ESC_EDGE(l) BIT(0 + (l)) - -#define MCTL_LANE_STS 0x2c -#define PPI_C_TX_READY_HS BIT(18) -#define DPHY_PLL_LOCK BIT(17) -#define PPI_D_RX_ULPS_ESC(x) (((x) & GENMASK(15, 12)) >> 12) -#define LANE_STATE_START 0 -#define LANE_STATE_IDLE 1 -#define LANE_STATE_WRITE 2 -#define LANE_STATE_ULPM 3 -#define LANE_STATE_READ 4 -#define DATA_LANE_STATE(l, val) \ - (((val) >> (2 + 2 * (l) + ((l) ? 1 : 0))) & GENMASK((l) ? 1 : 2, 0)) -#define CLK_LANE_STATE_HS 2 -#define CLK_LANE_STATE(val) ((val) & GENMASK(1, 0)) - -#define DSC_MODE_CTL 0x30 -#define DSC_MODE_EN BIT(0) - -#define DSC_CMD_SEND 0x34 -#define DSC_SEND_PPS BIT(0) -#define DSC_EXECUTE_QUEUE BIT(1) - -#define DSC_PPS_WRDAT 0x38 - -#define DSC_MODE_STS 0x3c -#define DSC_PPS_DONE BIT(1) -#define DSC_EXEC_DONE BIT(2) - -#define CMD_MODE_CTL 0x70 -#define IF_LP_EN(x) BIT(9 + (x)) -#define IF_VCHAN_ID(x, c) ((c) << ((x) * 2)) - -#define CMD_MODE_CTL2 0x74 -#define TE_TIMEOUT(x) ((x) << 11) -#define FILL_VALUE(x) ((x) << 3) -#define ARB_IF_WITH_HIGHEST_PRIORITY(x) ((x) << 1) -#define ARB_ROUND_ROBIN_MODE BIT(0) - -#define CMD_MODE_STS 0x78 -#define CMD_MODE_STS_CTL 0x134 -#define CMD_MODE_STS_CLR 0x154 -#define CMD_MODE_STS_FLAG 0x174 -#define ERR_IF_UNDERRUN(x) BIT(4 + (x)) -#define ERR_UNWANTED_READ BIT(3) -#define ERR_TE_MISS BIT(2) -#define ERR_NO_TE BIT(1) -#define CSM_RUNNING BIT(0) - -#define DIRECT_CMD_SEND 0x80 - -#define DIRECT_CMD_MAIN_SETTINGS 0x84 -#define TRIGGER_VAL(x) ((x) << 25) -#define CMD_LP_EN BIT(24) -#define CMD_SIZE(x) ((x) << 16) -#define CMD_VCHAN_ID(x) ((x) << 14) -#define CMD_DATATYPE(x) ((x) << 8) -#define CMD_LONG BIT(3) -#define WRITE_CMD 0 -#define READ_CMD 1 -#define TE_REQ 4 -#define TRIGGER_REQ 5 -#define BTA_REQ 6 - -#define DIRECT_CMD_STS 0x88 -#define DIRECT_CMD_STS_CTL 0x138 -#define DIRECT_CMD_STS_CLR 0x158 -#define DIRECT_CMD_STS_FLAG 0x178 -#define RCVD_ACK_VAL(val) ((val) >> 16) -#define RCVD_TRIGGER_VAL(val) (((val) & GENMASK(14, 11)) >> 11) -#define READ_COMPLETED_WITH_ERR BIT(10) -#define BTA_FINISHED BIT(9) -#define BTA_COMPLETED BIT(8) -#define TE_RCVD BIT(7) -#define TRIGGER_RCVD BIT(6) -#define ACK_WITH_ERR_RCVD BIT(5) -#define ACK_RCVD BIT(4) -#define READ_COMPLETED BIT(3) -#define TRIGGER_COMPLETED BIT(2) -#define WRITE_COMPLETED BIT(1) -#define SENDING_CMD BIT(0) - -#define DIRECT_CMD_STOP_READ 0x8c - -#define DIRECT_CMD_WRDATA 0x90 - -#define DIRECT_CMD_FIFO_RST 0x94 - -#define DIRECT_CMD_RDDATA 0xa0 - -#define DIRECT_CMD_RD_PROPS 0xa4 -#define RD_DCS BIT(18) -#define RD_VCHAN_ID(val) (((val) >> 16) & GENMASK(1, 0)) -#define RD_SIZE(val) ((val) & GENMASK(15, 0)) - -#define DIRECT_CMD_RD_STS 0xa8 -#define DIRECT_CMD_RD_STS_CTL 0x13c -#define DIRECT_CMD_RD_STS_CLR 0x15c -#define DIRECT_CMD_RD_STS_FLAG 0x17c -#define ERR_EOT_WITH_ERR BIT(8) -#define ERR_MISSING_EOT BIT(7) -#define ERR_WRONG_LENGTH BIT(6) -#define ERR_OVERSIZE BIT(5) -#define ERR_RECEIVE BIT(4) -#define ERR_UNDECODABLE BIT(3) -#define ERR_CHECKSUM BIT(2) -#define ERR_UNCORRECTABLE BIT(1) -#define ERR_FIXED BIT(0) - -#define VID_MAIN_CTL 0xb0 -#define VID_IGNORE_MISS_VSYNC BIT(31) -#define VID_FIELD_SW BIT(28) -#define VID_INTERLACED_EN BIT(27) -#define RECOVERY_MODE(x) ((x) << 25) -#define RECOVERY_MODE_NEXT_HSYNC 0 -#define RECOVERY_MODE_NEXT_STOP_POINT 2 -#define RECOVERY_MODE_NEXT_VSYNC 3 -#define REG_BLKEOL_MODE(x) ((x) << 23) -#define REG_BLKLINE_MODE(x) ((x) << 21) -#define REG_BLK_MODE_NULL_PKT 0 -#define REG_BLK_MODE_BLANKING_PKT 1 -#define REG_BLK_MODE_LP 2 -#define SYNC_PULSE_HORIZONTAL BIT(20) -#define SYNC_PULSE_ACTIVE BIT(19) -#define BURST_MODE BIT(18) -#define VID_PIXEL_MODE_MASK GENMASK(17, 14) -#define VID_PIXEL_MODE_RGB565 (0 << 14) -#define VID_PIXEL_MODE_RGB666_PACKED (1 << 14) -#define VID_PIXEL_MODE_RGB666 (2 << 14) -#define VID_PIXEL_MODE_RGB888 (3 << 14) -#define VID_PIXEL_MODE_RGB101010 (4 << 14) -#define VID_PIXEL_MODE_RGB121212 (5 << 14) -#define VID_PIXEL_MODE_YUV420 (8 << 14) -#define VID_PIXEL_MODE_YUV422_PACKED (9 << 14) -#define VID_PIXEL_MODE_YUV422 (10 << 14) -#define VID_PIXEL_MODE_YUV422_24B (11 << 14) -#define VID_PIXEL_MODE_DSC_COMP (12 << 14) -#define VID_DATATYPE(x) ((x) << 8) -#define VID_VIRTCHAN_ID(iface, x) ((x) << (4 + (iface) * 2)) -#define STOP_MODE(x) ((x) << 2) -#define START_MODE(x) (x) - -#define VID_VSIZE1 0xb4 -#define VFP_LEN(x) ((x) << 12) -#define VBP_LEN(x) ((x) << 6) -#define VSA_LEN(x) (x) - -#define VID_VSIZE2 0xb8 -#define VACT_LEN(x) (x) - -#define VID_HSIZE1 0xc0 -#define HBP_LEN(x) ((x) << 16) -#define HSA_LEN(x) (x) - -#define VID_HSIZE2 0xc4 -#define HFP_LEN(x) ((x) << 16) -#define HACT_LEN(x) (x) - -#define VID_BLKSIZE1 0xcc -#define BLK_EOL_PKT_LEN(x) ((x) << 15) -#define BLK_LINE_EVENT_PKT_LEN(x) (x) - -#define VID_BLKSIZE2 0xd0 -#define BLK_LINE_PULSE_PKT_LEN(x) (x) - -#define VID_PKT_TIME 0xd8 -#define BLK_EOL_DURATION(x) (x) - -#define VID_DPHY_TIME 0xdc -#define REG_WAKEUP_TIME(x) ((x) << 17) -#define REG_LINE_DURATION(x) (x) - -#define VID_ERR_COLOR1 0xe0 -#define COL_GREEN(x) ((x) << 12) -#define COL_RED(x) (x) - -#define VID_ERR_COLOR2 0xe4 -#define PAD_VAL(x) ((x) << 12) -#define COL_BLUE(x) (x) - -#define VID_VPOS 0xe8 -#define LINE_VAL(val) (((val) & GENMASK(14, 2)) >> 2) -#define LINE_POS(val) ((val) & GENMASK(1, 0)) - -#define VID_HPOS 0xec -#define HORIZ_VAL(val) (((val) & GENMASK(17, 3)) >> 3) -#define HORIZ_POS(val) ((val) & GENMASK(2, 0)) - -#define VID_MODE_STS 0xf0 -#define VID_MODE_STS_CTL 0x140 -#define VID_MODE_STS_CLR 0x160 -#define VID_MODE_STS_FLAG 0x180 -#define VSG_RECOVERY BIT(10) -#define ERR_VRS_WRONG_LEN BIT(9) -#define ERR_LONG_READ BIT(8) -#define ERR_LINE_WRITE BIT(7) -#define ERR_BURST_WRITE BIT(6) -#define ERR_SMALL_HEIGHT BIT(5) -#define ERR_SMALL_LEN BIT(4) -#define ERR_MISSING_VSYNC BIT(3) -#define ERR_MISSING_HSYNC BIT(2) -#define ERR_MISSING_DATA BIT(1) -#define VSG_RUNNING BIT(0) - -#define VID_VCA_SETTING1 0xf4 -#define BURST_LP BIT(16) -#define MAX_BURST_LIMIT(x) (x) - -#define VID_VCA_SETTING2 0xf8 -#define MAX_LINE_LIMIT(x) ((x) << 16) -#define EXACT_BURST_LIMIT(x) (x) - -#define TVG_CTL 0xfc -#define TVG_STRIPE_SIZE(x) ((x) << 5) -#define TVG_MODE_MASK GENMASK(4, 3) -#define TVG_MODE_SINGLE_COLOR (0 << 3) -#define TVG_MODE_VSTRIPES (2 << 3) -#define TVG_MODE_HSTRIPES (3 << 3) -#define TVG_STOPMODE_MASK GENMASK(2, 1) -#define TVG_STOPMODE_EOF (0 << 1) -#define TVG_STOPMODE_EOL (1 << 1) -#define TVG_STOPMODE_NOW (2 << 1) -#define TVG_RUN BIT(0) - -#define TVG_IMG_SIZE 0x100 -#define TVG_NBLINES(x) ((x) << 16) -#define TVG_LINE_SIZE(x) (x) - -#define TVG_COLOR1 0x104 -#define TVG_COL1_GREEN(x) ((x) << 12) -#define TVG_COL1_RED(x) (x) - -#define TVG_COLOR1_BIS 0x108 -#define TVG_COL1_BLUE(x) (x) - -#define TVG_COLOR2 0x10c -#define TVG_COL2_GREEN(x) ((x) << 12) -#define TVG_COL2_RED(x) (x) - -#define TVG_COLOR2_BIS 0x110 -#define TVG_COL2_BLUE(x) (x) - -#define TVG_STS 0x114 -#define TVG_STS_CTL 0x144 -#define TVG_STS_CLR 0x164 -#define TVG_STS_FLAG 0x184 -#define TVG_STS_RUNNING BIT(0) - -#define STS_CTL_EDGE(e) ((e) << 16) - -#define DPHY_LANES_MAP 0x198 -#define DAT_REMAP_CFG(b, l) ((l) << ((b) * 8)) - -#define DPI_IRQ_EN 0x1a0 -#define DPI_IRQ_CLR 0x1a4 -#define DPI_IRQ_STS 0x1a8 -#define PIXEL_BUF_OVERFLOW BIT(0) - -#define DPI_CFG 0x1ac -#define DPI_CFG_FIFO_DEPTH(x) ((x) >> 16) -#define DPI_CFG_FIFO_LEVEL(x) ((x) & GENMASK(15, 0)) - -#define TEST_GENERIC 0x1f0 -#define TEST_STATUS(x) ((x) >> 16) -#define TEST_CTRL(x) (x) - -#define ID_REG 0x1fc -#define REV_VENDOR_ID(x) (((x) & GENMASK(31, 20)) >> 20) -#define REV_PRODUCT_ID(x) (((x) & GENMASK(19, 12)) >> 12) -#define REV_HW(x) (((x) & GENMASK(11, 8)) >> 8) -#define REV_MAJOR(x) (((x) & GENMASK(7, 4)) >> 4) -#define REV_MINOR(x) ((x) & GENMASK(3, 0)) - -#define DSI_OUTPUT_PORT 0 -#define DSI_INPUT_PORT(inputid) (1 + (inputid)) - -#define DSI_HBP_FRAME_OVERHEAD 12 -#define DSI_HSA_FRAME_OVERHEAD 14 -#define DSI_HFP_FRAME_OVERHEAD 6 -#define DSI_HSS_VSS_VSE_FRAME_OVERHEAD 4 -#define DSI_BLANKING_FRAME_OVERHEAD 6 -#define DSI_NULL_FRAME_OVERHEAD 6 -#define DSI_EOT_PKT_SIZE 4 - -struct cdns_dsi_output { - struct mipi_dsi_device *dev; - struct drm_panel *panel; - struct drm_bridge *bridge; - union phy_configure_opts phy_opts; -}; - -enum cdns_dsi_input_id { - CDNS_SDI_INPUT, - CDNS_DPI_INPUT, - CDNS_DSC_INPUT, -}; - -struct cdns_dsi_cfg { - unsigned int hfp; - unsigned int hsa; - unsigned int hbp; - unsigned int hact; - unsigned int htotal; -}; - -struct cdns_dsi_input { - enum cdns_dsi_input_id id; - struct drm_bridge bridge; -}; - -struct cdns_dsi { - struct mipi_dsi_host base; - void __iomem *regs; - struct cdns_dsi_input input; - struct cdns_dsi_output output; - unsigned int direct_cmd_fifo_depth; - unsigned int rx_fifo_depth; - struct completion direct_cmd_comp; - struct clk *dsi_p_clk; - struct reset_control *dsi_p_rst; - struct clk *dsi_sys_clk; - bool link_initialized; - bool phy_initialized; - struct phy *dphy; -}; - -static inline struct cdns_dsi *input_to_dsi(struct cdns_dsi_input *input) -{ - return container_of(input, struct cdns_dsi, input); -} - -static inline struct cdns_dsi *to_cdns_dsi(struct mipi_dsi_host *host) -{ - return container_of(host, struct cdns_dsi, base); -} - -static inline struct cdns_dsi_input * -bridge_to_cdns_dsi_input(struct drm_bridge *bridge) -{ - return container_of(bridge, struct cdns_dsi_input, bridge); -} - -static unsigned int mode_to_dpi_hfp(const struct drm_display_mode *mode, - bool mode_valid_check) -{ - if (mode_valid_check) - return mode->hsync_start - mode->hdisplay; - - return mode->crtc_hsync_start - mode->crtc_hdisplay; -} - -static unsigned int dpi_to_dsi_timing(unsigned int dpi_timing, - unsigned int dpi_bpp, - unsigned int dsi_pkt_overhead) -{ - unsigned int dsi_timing = DIV_ROUND_UP(dpi_timing * dpi_bpp, 8); - - if (dsi_timing < dsi_pkt_overhead) - dsi_timing = 0; - else - dsi_timing -= dsi_pkt_overhead; - - return dsi_timing; -} - -static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi, - const struct drm_display_mode *mode, - struct cdns_dsi_cfg *dsi_cfg, - bool mode_valid_check) -{ - struct cdns_dsi_output *output = &dsi->output; - unsigned int tmp; - bool sync_pulse = false; - int bpp; - - memset(dsi_cfg, 0, sizeof(*dsi_cfg)); - - if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) - sync_pulse = true; - - bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format); - - if (mode_valid_check) - tmp = mode->htotal - - (sync_pulse ? mode->hsync_end : mode->hsync_start); - else - tmp = mode->crtc_htotal - - (sync_pulse ? - mode->crtc_hsync_end : mode->crtc_hsync_start); - - dsi_cfg->hbp = dpi_to_dsi_timing(tmp, bpp, DSI_HBP_FRAME_OVERHEAD); - - if (sync_pulse) { - if (mode_valid_check) - tmp = mode->hsync_end - mode->hsync_start; - else - tmp = mode->crtc_hsync_end - mode->crtc_hsync_start; - - dsi_cfg->hsa = dpi_to_dsi_timing(tmp, bpp, - DSI_HSA_FRAME_OVERHEAD); - } - - dsi_cfg->hact = dpi_to_dsi_timing(mode_valid_check ? - mode->hdisplay : mode->crtc_hdisplay, - bpp, 0); - dsi_cfg->hfp = dpi_to_dsi_timing(mode_to_dpi_hfp(mode, mode_valid_check), - bpp, DSI_HFP_FRAME_OVERHEAD); - - return 0; -} - -static int cdns_dsi_adjust_phy_config(struct cdns_dsi *dsi, - struct cdns_dsi_cfg *dsi_cfg, - struct phy_configure_opts_mipi_dphy *phy_cfg, - const struct drm_display_mode *mode, - bool mode_valid_check) -{ - struct cdns_dsi_output *output = &dsi->output; - unsigned long long dlane_bps; - unsigned long adj_dsi_htotal; - unsigned long dsi_htotal; - unsigned long dpi_htotal; - unsigned long dpi_hz; - unsigned int dsi_hfp_ext; - unsigned int lanes = output->dev->lanes; - - dsi_htotal = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD; - if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) - dsi_htotal += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD; - - dsi_htotal += dsi_cfg->hact; - dsi_htotal += dsi_cfg->hfp + DSI_HFP_FRAME_OVERHEAD; - - /* - * Make sure DSI htotal is aligned on a lane boundary when calculating - * the expected data rate. This is done by extending HFP in case of - * misalignment. - */ - adj_dsi_htotal = dsi_htotal; - if (dsi_htotal % lanes) - adj_dsi_htotal += lanes - (dsi_htotal % lanes); - - dpi_hz = (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000; - dlane_bps = (unsigned long long)dpi_hz * adj_dsi_htotal; - - /* data rate in bytes/sec is not an integer, refuse the mode. */ - dpi_htotal = mode_valid_check ? mode->htotal : mode->crtc_htotal; - if (do_div(dlane_bps, lanes * dpi_htotal)) - return -EINVAL; - - /* data rate was in bytes/sec, convert to bits/sec. */ - phy_cfg->hs_clk_rate = dlane_bps * 8; - - dsi_hfp_ext = adj_dsi_htotal - dsi_htotal; - dsi_cfg->hfp += dsi_hfp_ext; - dsi_cfg->htotal = dsi_htotal + dsi_hfp_ext; - - return 0; -} - -static int cdns_dsi_check_conf(struct cdns_dsi *dsi, - const struct drm_display_mode *mode, - struct cdns_dsi_cfg *dsi_cfg, - bool mode_valid_check) -{ - struct cdns_dsi_output *output = &dsi->output; - struct phy_configure_opts_mipi_dphy *phy_cfg = &output->phy_opts.mipi_dphy; - unsigned long dsi_hss_hsa_hse_hbp; - unsigned int nlanes = output->dev->lanes; - int ret; - - ret = cdns_dsi_mode2cfg(dsi, mode, dsi_cfg, mode_valid_check); - if (ret) - return ret; - - phy_mipi_dphy_get_default_config(mode->crtc_clock * 1000, - mipi_dsi_pixel_format_to_bpp(output->dev->format), - nlanes, phy_cfg); - - ret = cdns_dsi_adjust_phy_config(dsi, dsi_cfg, phy_cfg, mode, mode_valid_check); - if (ret) - return ret; - - ret = phy_validate(dsi->dphy, PHY_MODE_MIPI_DPHY, 0, &output->phy_opts); - if (ret) - return ret; - - dsi_hss_hsa_hse_hbp = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD; - if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) - dsi_hss_hsa_hse_hbp += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD; - - /* - * Make sure DPI(HFP) > DSI(HSS+HSA+HSE+HBP) to guarantee that the FIFO - * is empty before we start a receiving a new line on the DPI - * interface. - */ - if ((u64)phy_cfg->hs_clk_rate * - mode_to_dpi_hfp(mode, mode_valid_check) * nlanes < - (u64)dsi_hss_hsa_hse_hbp * - (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000) - return -EINVAL; - - return 0; -} - -static int cdns_dsi_bridge_attach(struct drm_bridge *bridge, - enum drm_bridge_attach_flags flags) -{ - struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); - struct cdns_dsi *dsi = input_to_dsi(input); - struct cdns_dsi_output *output = &dsi->output; - - if (!drm_core_check_feature(bridge->dev, DRIVER_ATOMIC)) { - dev_err(dsi->base.dev, - "cdns-dsi driver is only compatible with DRM devices supporting atomic updates"); - return -ENOTSUPP; - } - - return drm_bridge_attach(bridge->encoder, output->bridge, bridge, - flags); -} - -static enum drm_mode_status -cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge, - const struct drm_display_info *info, - const struct drm_display_mode *mode) -{ - struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); - struct cdns_dsi *dsi = input_to_dsi(input); - struct cdns_dsi_output *output = &dsi->output; - struct cdns_dsi_cfg dsi_cfg; - int bpp, ret; - - /* - * VFP_DSI should be less than VFP_DPI and VFP_DSI should be at - * least 1. - */ - if (mode->vtotal - mode->vsync_end < 2) - return MODE_V_ILLEGAL; - - /* VSA_DSI = VSA_DPI and must be at least 2. */ - if (mode->vsync_end - mode->vsync_start < 2) - return MODE_V_ILLEGAL; - - /* HACT must be 32-bits aligned. */ - bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format); - if ((mode->hdisplay * bpp) % 32) - return MODE_H_ILLEGAL; - - ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, true); - if (ret) - return MODE_BAD; - - return MODE_OK; -} - -static void cdns_dsi_bridge_disable(struct drm_bridge *bridge) -{ - struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); - struct cdns_dsi *dsi = input_to_dsi(input); - u32 val; - - val = readl(dsi->regs + MCTL_MAIN_DATA_CTL); - val &= ~(IF_VID_SELECT_MASK | IF_VID_MODE | VID_EN | HOST_EOT_GEN | - DISP_EOT_GEN); - writel(val, dsi->regs + MCTL_MAIN_DATA_CTL); - - val = readl(dsi->regs + MCTL_MAIN_EN) & ~IF_EN(input->id); - writel(val, dsi->regs + MCTL_MAIN_EN); - pm_runtime_put(dsi->base.dev); -} - -static void cdns_dsi_bridge_post_disable(struct drm_bridge *bridge) -{ - struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); - struct cdns_dsi *dsi = input_to_dsi(input); - - pm_runtime_put(dsi->base.dev); -} - -static void cdns_dsi_hs_init(struct cdns_dsi *dsi) -{ - struct cdns_dsi_output *output = &dsi->output; - u32 status; - - if (dsi->phy_initialized) - return; - /* - * Power all internal DPHY blocks down and maintain their reset line - * asserted before changing the DPHY config. - */ - writel(DPHY_CMN_PSO | DPHY_PLL_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | - DPHY_CMN_PDN | DPHY_PLL_PDN, - dsi->regs + MCTL_DPHY_CFG0); - - phy_init(dsi->dphy); - phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY); - phy_configure(dsi->dphy, &output->phy_opts); - phy_power_on(dsi->dphy); - - /* Activate the PLL and wait until it's locked. */ - writel(PLL_LOCKED, dsi->regs + MCTL_MAIN_STS_CLR); - writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN, - dsi->regs + MCTL_DPHY_CFG0); - WARN_ON_ONCE(readl_poll_timeout(dsi->regs + MCTL_MAIN_STS, status, - status & PLL_LOCKED, 100, 100)); - /* De-assert data and clock reset lines. */ - writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN | - DPHY_D_RSTB(output->dev->lanes) | DPHY_C_RSTB, - dsi->regs + MCTL_DPHY_CFG0); - dsi->phy_initialized = true; -} - -static void cdns_dsi_init_link(struct cdns_dsi *dsi) -{ - struct cdns_dsi_output *output = &dsi->output; - unsigned long sysclk_period, ulpout; - u32 val; - int i; - - if (dsi->link_initialized) - return; - - val = 0; - for (i = 1; i < output->dev->lanes; i++) - val |= DATA_LANE_EN(i); - - if (!(output->dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) - val |= CLK_CONTINUOUS; - - writel(val, dsi->regs + MCTL_MAIN_PHY_CTL); - - /* ULPOUT should be set to 1ms and is expressed in sysclk cycles. */ - sysclk_period = NSEC_PER_SEC / clk_get_rate(dsi->dsi_sys_clk); - ulpout = DIV_ROUND_UP(NSEC_PER_MSEC, sysclk_period); - writel(CLK_LANE_ULPOUT_TIME(ulpout) | DATA_LANE_ULPOUT_TIME(ulpout), - dsi->regs + MCTL_ULPOUT_TIME); - - writel(LINK_EN, dsi->regs + MCTL_MAIN_DATA_CTL); - - val = CLK_LANE_EN | PLL_START; - for (i = 0; i < output->dev->lanes; i++) - val |= DATA_LANE_START(i); - - writel(val, dsi->regs + MCTL_MAIN_EN); - - dsi->link_initialized = true; -} - -static void cdns_dsi_bridge_enable(struct drm_bridge *bridge) -{ - struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); - struct cdns_dsi *dsi = input_to_dsi(input); - struct cdns_dsi_output *output = &dsi->output; - struct drm_display_mode *mode; - struct phy_configure_opts_mipi_dphy *phy_cfg = &output->phy_opts.mipi_dphy; - unsigned long tx_byte_period; - struct cdns_dsi_cfg dsi_cfg; - u32 tmp, reg_wakeup, div; - int nlanes; - - if (WARN_ON(pm_runtime_get_sync(dsi->base.dev) < 0)) - return; - - mode = &bridge->encoder->crtc->state->adjusted_mode; - nlanes = output->dev->lanes; - - WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, false)); - - cdns_dsi_hs_init(dsi); - cdns_dsi_init_link(dsi); - - writel(HBP_LEN(dsi_cfg.hbp) | HSA_LEN(dsi_cfg.hsa), - dsi->regs + VID_HSIZE1); - writel(HFP_LEN(dsi_cfg.hfp) | HACT_LEN(dsi_cfg.hact), - dsi->regs + VID_HSIZE2); - - writel(VBP_LEN(mode->crtc_vtotal - mode->crtc_vsync_end - 1) | - VFP_LEN(mode->crtc_vsync_start - mode->crtc_vdisplay) | - VSA_LEN(mode->crtc_vsync_end - mode->crtc_vsync_start + 1), - dsi->regs + VID_VSIZE1); - writel(mode->crtc_vdisplay, dsi->regs + VID_VSIZE2); - - tmp = dsi_cfg.htotal - - (dsi_cfg.hsa + DSI_BLANKING_FRAME_OVERHEAD + - DSI_HSA_FRAME_OVERHEAD); - writel(BLK_LINE_PULSE_PKT_LEN(tmp), dsi->regs + VID_BLKSIZE2); - if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) - writel(MAX_LINE_LIMIT(tmp - DSI_NULL_FRAME_OVERHEAD), - dsi->regs + VID_VCA_SETTING2); - - tmp = dsi_cfg.htotal - - (DSI_HSS_VSS_VSE_FRAME_OVERHEAD + DSI_BLANKING_FRAME_OVERHEAD); - writel(BLK_LINE_EVENT_PKT_LEN(tmp), dsi->regs + VID_BLKSIZE1); - if (!(output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)) - writel(MAX_LINE_LIMIT(tmp - DSI_NULL_FRAME_OVERHEAD), - dsi->regs + VID_VCA_SETTING2); - - tmp = DIV_ROUND_UP(dsi_cfg.htotal, nlanes) - - DIV_ROUND_UP(dsi_cfg.hsa, nlanes); - - if (!(output->dev->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET)) - tmp -= DIV_ROUND_UP(DSI_EOT_PKT_SIZE, nlanes); - - tx_byte_period = DIV_ROUND_DOWN_ULL((u64)NSEC_PER_SEC * 8, - phy_cfg->hs_clk_rate); - reg_wakeup = (phy_cfg->hs_prepare + phy_cfg->hs_zero) / tx_byte_period; - writel(REG_WAKEUP_TIME(reg_wakeup) | REG_LINE_DURATION(tmp), - dsi->regs + VID_DPHY_TIME); - - /* - * HSTX and LPRX timeouts are both expressed in TX byte clk cycles and - * both should be set to at least the time it takes to transmit a - * frame. - */ - tmp = NSEC_PER_SEC / drm_mode_vrefresh(mode); - tmp /= tx_byte_period; - - for (div = 0; div <= CLK_DIV_MAX; div++) { - if (tmp <= HSTX_TIMEOUT_MAX) - break; - - tmp >>= 1; - } - - if (tmp > HSTX_TIMEOUT_MAX) - tmp = HSTX_TIMEOUT_MAX; - - writel(CLK_DIV(div) | HSTX_TIMEOUT(tmp), - dsi->regs + MCTL_DPHY_TIMEOUT1); - - writel(LPRX_TIMEOUT(tmp), dsi->regs + MCTL_DPHY_TIMEOUT2); - - if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO) { - switch (output->dev->format) { - case MIPI_DSI_FMT_RGB888: - tmp = VID_PIXEL_MODE_RGB888 | - VID_DATATYPE(MIPI_DSI_PACKED_PIXEL_STREAM_24); - break; - - case MIPI_DSI_FMT_RGB666: - tmp = VID_PIXEL_MODE_RGB666 | - VID_DATATYPE(MIPI_DSI_PIXEL_STREAM_3BYTE_18); - break; - - case MIPI_DSI_FMT_RGB666_PACKED: - tmp = VID_PIXEL_MODE_RGB666_PACKED | - VID_DATATYPE(MIPI_DSI_PACKED_PIXEL_STREAM_18); - break; - - case MIPI_DSI_FMT_RGB565: - tmp = VID_PIXEL_MODE_RGB565 | - VID_DATATYPE(MIPI_DSI_PACKED_PIXEL_STREAM_16); - break; - - default: - dev_err(dsi->base.dev, "Unsupported DSI format\n"); - return; - } - - if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) - tmp |= SYNC_PULSE_ACTIVE | SYNC_PULSE_HORIZONTAL; - - tmp |= REG_BLKLINE_MODE(REG_BLK_MODE_BLANKING_PKT) | - REG_BLKEOL_MODE(REG_BLK_MODE_BLANKING_PKT) | - RECOVERY_MODE(RECOVERY_MODE_NEXT_HSYNC) | - VID_IGNORE_MISS_VSYNC; - - writel(tmp, dsi->regs + VID_MAIN_CTL); - } - - tmp = readl(dsi->regs + MCTL_MAIN_DATA_CTL); - tmp &= ~(IF_VID_SELECT_MASK | HOST_EOT_GEN | IF_VID_MODE); - - if (!(output->dev->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET)) - tmp |= HOST_EOT_GEN; - - if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO) - tmp |= IF_VID_MODE | IF_VID_SELECT(input->id) | VID_EN; - - writel(tmp, dsi->regs + MCTL_MAIN_DATA_CTL); - - tmp = readl(dsi->regs + MCTL_MAIN_EN) | IF_EN(input->id); - writel(tmp, dsi->regs + MCTL_MAIN_EN); -} - -static void cdns_dsi_bridge_pre_enable(struct drm_bridge *bridge) -{ - struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); - struct cdns_dsi *dsi = input_to_dsi(input); - - if (WARN_ON(pm_runtime_get_sync(dsi->base.dev) < 0)) - return; - - cdns_dsi_init_link(dsi); - cdns_dsi_hs_init(dsi); -} - -static const struct drm_bridge_funcs cdns_dsi_bridge_funcs = { - .attach = cdns_dsi_bridge_attach, - .mode_valid = cdns_dsi_bridge_mode_valid, - .disable = cdns_dsi_bridge_disable, - .pre_enable = cdns_dsi_bridge_pre_enable, - .enable = cdns_dsi_bridge_enable, - .post_disable = cdns_dsi_bridge_post_disable, -}; - -static int cdns_dsi_attach(struct mipi_dsi_host *host, - struct mipi_dsi_device *dev) -{ - struct cdns_dsi *dsi = to_cdns_dsi(host); - struct cdns_dsi_output *output = &dsi->output; - struct cdns_dsi_input *input = &dsi->input; - struct drm_bridge *bridge; - struct drm_panel *panel; - struct device_node *np; - int ret; - - /* - * We currently do not support connecting several DSI devices to the - * same host. In order to support that we'd need the DRM bridge - * framework to allow dynamic reconfiguration of the bridge chain. - */ - if (output->dev) - return -EBUSY; - - /* We do not support burst mode yet. */ - if (dev->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) - return -ENOTSUPP; - - /* - * The host <-> device link might be described using an OF-graph - * representation, in this case we extract the device of_node from - * this representation, otherwise we use dsidev->dev.of_node which - * should have been filled by the core. - */ - np = of_graph_get_remote_node(dsi->base.dev->of_node, DSI_OUTPUT_PORT, - dev->channel); - if (!np) - np = of_node_get(dev->dev.of_node); - - panel = of_drm_find_panel(np); - if (!IS_ERR(panel)) { - bridge = drm_panel_bridge_add_typed(panel, - DRM_MODE_CONNECTOR_DSI); - } else { - bridge = of_drm_find_bridge(dev->dev.of_node); - if (!bridge) - bridge = ERR_PTR(-EINVAL); - } - - of_node_put(np); - - if (IS_ERR(bridge)) { - ret = PTR_ERR(bridge); - dev_err(host->dev, "failed to add DSI device %s (err = %d)", - dev->name, ret); - return ret; - } - - output->dev = dev; - output->bridge = bridge; - output->panel = panel; - - /* - * The DSI output has been properly configured, we can now safely - * register the input to the bridge framework so that it can take place - * in a display pipeline. - */ - drm_bridge_add(&input->bridge); - - return 0; -} - -static int cdns_dsi_detach(struct mipi_dsi_host *host, - struct mipi_dsi_device *dev) -{ - struct cdns_dsi *dsi = to_cdns_dsi(host); - struct cdns_dsi_output *output = &dsi->output; - struct cdns_dsi_input *input = &dsi->input; - - drm_bridge_remove(&input->bridge); - if (output->panel) - drm_panel_bridge_remove(output->bridge); - - return 0; -} - -static irqreturn_t cdns_dsi_interrupt(int irq, void *data) -{ - struct cdns_dsi *dsi = data; - irqreturn_t ret = IRQ_NONE; - u32 flag, ctl; - - flag = readl(dsi->regs + DIRECT_CMD_STS_FLAG); - if (flag) { - ctl = readl(dsi->regs + DIRECT_CMD_STS_CTL); - ctl &= ~flag; - writel(ctl, dsi->regs + DIRECT_CMD_STS_CTL); - complete(&dsi->direct_cmd_comp); - ret = IRQ_HANDLED; - } - - return ret; -} - -static ssize_t cdns_dsi_transfer(struct mipi_dsi_host *host, - const struct mipi_dsi_msg *msg) -{ - struct cdns_dsi *dsi = to_cdns_dsi(host); - u32 cmd, sts, val, wait = WRITE_COMPLETED, ctl = 0; - struct mipi_dsi_packet packet; - int ret, i, tx_len, rx_len; - - ret = pm_runtime_resume_and_get(host->dev); - if (ret < 0) - return ret; - - cdns_dsi_init_link(dsi); - - ret = mipi_dsi_create_packet(&packet, msg); - if (ret) - goto out; - - tx_len = msg->tx_buf ? msg->tx_len : 0; - rx_len = msg->rx_buf ? msg->rx_len : 0; - - /* For read operations, the maximum TX len is 2. */ - if (rx_len && tx_len > 2) { - ret = -ENOTSUPP; - goto out; - } - - /* TX len is limited by the CMD FIFO depth. */ - if (tx_len > dsi->direct_cmd_fifo_depth) { - ret = -ENOTSUPP; - goto out; - } - - /* RX len is limited by the RX FIFO depth. */ - if (rx_len > dsi->rx_fifo_depth) { - ret = -ENOTSUPP; - goto out; - } - - cmd = CMD_SIZE(tx_len) | CMD_VCHAN_ID(msg->channel) | - CMD_DATATYPE(msg->type); - - if (msg->flags & MIPI_DSI_MSG_USE_LPM) - cmd |= CMD_LP_EN; - - if (mipi_dsi_packet_format_is_long(msg->type)) - cmd |= CMD_LONG; - - if (rx_len) { - cmd |= READ_CMD; - wait = READ_COMPLETED_WITH_ERR | READ_COMPLETED; - ctl = READ_EN | BTA_EN; - } else if (msg->flags & MIPI_DSI_MSG_REQ_ACK) { - cmd |= BTA_REQ; - wait = ACK_WITH_ERR_RCVD | ACK_RCVD; - ctl = BTA_EN; - } - - writel(readl(dsi->regs + MCTL_MAIN_DATA_CTL) | ctl, - dsi->regs + MCTL_MAIN_DATA_CTL); - - writel(cmd, dsi->regs + DIRECT_CMD_MAIN_SETTINGS); - - for (i = 0; i < tx_len; i += 4) { - const u8 *buf = msg->tx_buf; - int j; - - val = 0; - for (j = 0; j < 4 && j + i < tx_len; j++) - val |= (u32)buf[i + j] << (8 * j); - - writel(val, dsi->regs + DIRECT_CMD_WRDATA); - } - - /* Clear status flags before sending the command. */ - writel(wait, dsi->regs + DIRECT_CMD_STS_CLR); - writel(wait, dsi->regs + DIRECT_CMD_STS_CTL); - reinit_completion(&dsi->direct_cmd_comp); - writel(0, dsi->regs + DIRECT_CMD_SEND); - - wait_for_completion_timeout(&dsi->direct_cmd_comp, - msecs_to_jiffies(1000)); - - sts = readl(dsi->regs + DIRECT_CMD_STS); - writel(wait, dsi->regs + DIRECT_CMD_STS_CLR); - writel(0, dsi->regs + DIRECT_CMD_STS_CTL); - - writel(readl(dsi->regs + MCTL_MAIN_DATA_CTL) & ~ctl, - dsi->regs + MCTL_MAIN_DATA_CTL); - - /* We did not receive the events we were waiting for. */ - if (!(sts & wait)) { - ret = -ETIMEDOUT; - goto out; - } - - /* 'READ' or 'WRITE with ACK' failed. */ - if (sts & (READ_COMPLETED_WITH_ERR | ACK_WITH_ERR_RCVD)) { - ret = -EIO; - goto out; - } - - for (i = 0; i < rx_len; i += 4) { - u8 *buf = msg->rx_buf; - int j; - - val = readl(dsi->regs + DIRECT_CMD_RDDATA); - for (j = 0; j < 4 && j + i < rx_len; j++) - buf[i + j] = val >> (8 * j); - } - -out: - pm_runtime_put(host->dev); - return ret; -} - -static const struct mipi_dsi_host_ops cdns_dsi_ops = { - .attach = cdns_dsi_attach, - .detach = cdns_dsi_detach, - .transfer = cdns_dsi_transfer, -}; - -static int __maybe_unused cdns_dsi_resume(struct device *dev) -{ - struct cdns_dsi *dsi = dev_get_drvdata(dev); - - reset_control_deassert(dsi->dsi_p_rst); - clk_prepare_enable(dsi->dsi_p_clk); - clk_prepare_enable(dsi->dsi_sys_clk); - - return 0; -} - -static int __maybe_unused cdns_dsi_suspend(struct device *dev) -{ - struct cdns_dsi *dsi = dev_get_drvdata(dev); - - clk_disable_unprepare(dsi->dsi_sys_clk); - clk_disable_unprepare(dsi->dsi_p_clk); - reset_control_assert(dsi->dsi_p_rst); - dsi->link_initialized = false; - return 0; -} - -static UNIVERSAL_DEV_PM_OPS(cdns_dsi_pm_ops, cdns_dsi_suspend, cdns_dsi_resume, - NULL); - -static int cdns_dsi_drm_probe(struct platform_device *pdev) -{ - struct cdns_dsi *dsi; - struct cdns_dsi_input *input; - int ret, irq; - u32 val; - - dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; - - platform_set_drvdata(pdev, dsi); - - input = &dsi->input; - - dsi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(dsi->regs)) - return PTR_ERR(dsi->regs); - - dsi->dsi_p_clk = devm_clk_get(&pdev->dev, "dsi_p_clk"); - if (IS_ERR(dsi->dsi_p_clk)) - return PTR_ERR(dsi->dsi_p_clk); - - dsi->dsi_p_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, - "dsi_p_rst"); - if (IS_ERR(dsi->dsi_p_rst)) - return PTR_ERR(dsi->dsi_p_rst); - - dsi->dsi_sys_clk = devm_clk_get(&pdev->dev, "dsi_sys_clk"); - if (IS_ERR(dsi->dsi_sys_clk)) - return PTR_ERR(dsi->dsi_sys_clk); - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - dsi->dphy = devm_phy_get(&pdev->dev, "dphy"); - if (IS_ERR(dsi->dphy)) - return PTR_ERR(dsi->dphy); - - ret = clk_prepare_enable(dsi->dsi_p_clk); - if (ret) - return ret; - - val = readl(dsi->regs + ID_REG); - if (REV_VENDOR_ID(val) != 0xcad) { - dev_err(&pdev->dev, "invalid vendor id\n"); - ret = -EINVAL; - goto err_disable_pclk; - } - - val = readl(dsi->regs + IP_CONF); - dsi->direct_cmd_fifo_depth = 1 << (DIRCMD_FIFO_DEPTH(val) + 2); - dsi->rx_fifo_depth = RX_FIFO_DEPTH(val); - init_completion(&dsi->direct_cmd_comp); - - writel(0, dsi->regs + MCTL_MAIN_DATA_CTL); - writel(0, dsi->regs + MCTL_MAIN_EN); - writel(0, dsi->regs + MCTL_MAIN_PHY_CTL); - - /* - * We only support the DPI input, so force input->id to - * CDNS_DPI_INPUT. - */ - input->id = CDNS_DPI_INPUT; - input->bridge.funcs = &cdns_dsi_bridge_funcs; - input->bridge.of_node = pdev->dev.of_node; - - /* Mask all interrupts before registering the IRQ handler. */ - writel(0, dsi->regs + MCTL_MAIN_STS_CTL); - writel(0, dsi->regs + MCTL_DPHY_ERR_CTL1); - writel(0, dsi->regs + CMD_MODE_STS_CTL); - writel(0, dsi->regs + DIRECT_CMD_STS_CTL); - writel(0, dsi->regs + DIRECT_CMD_RD_STS_CTL); - writel(0, dsi->regs + VID_MODE_STS_CTL); - writel(0, dsi->regs + TVG_STS_CTL); - writel(0, dsi->regs + DPI_IRQ_EN); - ret = devm_request_irq(&pdev->dev, irq, cdns_dsi_interrupt, 0, - dev_name(&pdev->dev), dsi); - if (ret) - goto err_disable_pclk; - - pm_runtime_enable(&pdev->dev); - dsi->base.dev = &pdev->dev; - dsi->base.ops = &cdns_dsi_ops; - - ret = mipi_dsi_host_register(&dsi->base); - if (ret) - goto err_disable_runtime_pm; - - clk_disable_unprepare(dsi->dsi_p_clk); - - return 0; - -err_disable_runtime_pm: - pm_runtime_disable(&pdev->dev); - -err_disable_pclk: - clk_disable_unprepare(dsi->dsi_p_clk); - - return ret; -} - -static int cdns_dsi_drm_remove(struct platform_device *pdev) -{ - struct cdns_dsi *dsi = platform_get_drvdata(pdev); - - mipi_dsi_host_unregister(&dsi->base); - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static const struct of_device_id cdns_dsi_of_match[] = { - { .compatible = "cdns,dsi" }, - { }, -}; -MODULE_DEVICE_TABLE(of, cdns_dsi_of_match); - -static struct platform_driver cdns_dsi_platform_driver = { - .probe = cdns_dsi_drm_probe, - .remove = cdns_dsi_drm_remove, - .driver = { - .name = "cdns-dsi", - .of_match_table = cdns_dsi_of_match, - .pm = &cdns_dsi_pm_ops, - }, -}; -module_platform_driver(cdns_dsi_platform_driver); - -MODULE_AUTHOR("Boris Brezillon <boris.brezillon@bootlin.com>"); -MODULE_DESCRIPTION("Cadence DSI driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:cdns-dsi"); - diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index bf920c3503aa..0e37840cd7a8 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -740,8 +740,7 @@ static int chipone_dsi_probe(struct mipi_dsi_device *dsi) return ret; } -static int chipone_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int chipone_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct chipone *icn; @@ -796,7 +795,7 @@ static struct i2c_device_id chipone_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, chipone_i2c_id); static struct i2c_driver chipone_i2c_driver = { - .probe = chipone_i2c_probe, + .probe_new = chipone_i2c_probe, .id_table = chipone_i2c_id, .driver = { .name = "chipone-icn6211-i2c", diff --git a/drivers/gpu/drm/bridge/chrontel-ch7033.c b/drivers/gpu/drm/bridge/chrontel-ch7033.c index b94f39a86846..339b759e4c81 100644 --- a/drivers/gpu/drm/bridge/chrontel-ch7033.c +++ b/drivers/gpu/drm/bridge/chrontel-ch7033.c @@ -528,8 +528,7 @@ static const struct regmap_config ch7033_regmap_config = { .max_register = 0x7f, }; -static int ch7033_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ch7033_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ch7033_priv *priv; @@ -604,7 +603,7 @@ static const struct i2c_device_id ch7033_ids[] = { MODULE_DEVICE_TABLE(i2c, ch7033_ids); static struct i2c_driver ch7033_driver = { - .probe = ch7033_probe, + .probe_new = ch7033_probe, .remove = ch7033_remove, .driver = { .name = "ch7033", diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c index f9e0f8d99268..6bac160b395b 100644 --- a/drivers/gpu/drm/bridge/fsl-ldb.c +++ b/drivers/gpu/drm/bridge/fsl-ldb.c @@ -18,7 +18,6 @@ #include <drm/drm_of.h> #include <drm/drm_panel.h> -#define LDB_CTRL 0x5c #define LDB_CTRL_CH0_ENABLE BIT(0) #define LDB_CTRL_CH0_DI_SELECT BIT(1) #define LDB_CTRL_CH1_ENABLE BIT(2) @@ -35,9 +34,13 @@ #define LDB_CTRL_ASYNC_FIFO_ENABLE BIT(24) #define LDB_CTRL_ASYNC_FIFO_THRESHOLD_MASK GENMASK(27, 25) -#define LVDS_CTRL 0x128 #define LVDS_CTRL_CH0_EN BIT(0) #define LVDS_CTRL_CH1_EN BIT(1) +/* + * LVDS_CTRL_LVDS_EN bit is poorly named in i.MX93 reference manual. + * Clear it to enable LVDS and set it to disable LVDS. + */ +#define LVDS_CTRL_LVDS_EN BIT(1) #define LVDS_CTRL_VBG_EN BIT(2) #define LVDS_CTRL_HS_EN BIT(3) #define LVDS_CTRL_PRE_EMPH_EN BIT(4) @@ -52,6 +55,29 @@ #define LVDS_CTRL_VBG_ADJ(n) (((n) & 0x7) << 17) #define LVDS_CTRL_VBG_ADJ_MASK GENMASK(19, 17) +enum fsl_ldb_devtype { + IMX8MP_LDB, + IMX93_LDB, +}; + +struct fsl_ldb_devdata { + u32 ldb_ctrl; + u32 lvds_ctrl; + bool lvds_en_bit; +}; + +static const struct fsl_ldb_devdata fsl_ldb_devdata[] = { + [IMX8MP_LDB] = { + .ldb_ctrl = 0x5c, + .lvds_ctrl = 0x128, + }, + [IMX93_LDB] = { + .ldb_ctrl = 0x20, + .lvds_ctrl = 0x24, + .lvds_en_bit = true, + }, +}; + struct fsl_ldb { struct device *dev; struct drm_bridge bridge; @@ -59,6 +85,7 @@ struct fsl_ldb { struct clk *clk; struct regmap *regmap; bool lvds_dual_link; + const struct fsl_ldb_devdata *devdata; }; static inline struct fsl_ldb *to_fsl_ldb(struct drm_bridge *bridge) @@ -66,6 +93,14 @@ static inline struct fsl_ldb *to_fsl_ldb(struct drm_bridge *bridge) return container_of(bridge, struct fsl_ldb, bridge); } +static unsigned long fsl_ldb_link_frequency(struct fsl_ldb *fsl_ldb, int clock) +{ + if (fsl_ldb->lvds_dual_link) + return clock * 3500; + else + return clock * 7000; +} + static int fsl_ldb_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) { @@ -85,6 +120,8 @@ static void fsl_ldb_atomic_enable(struct drm_bridge *bridge, const struct drm_display_mode *mode; struct drm_connector *connector; struct drm_crtc *crtc; + unsigned long configured_link_freq; + unsigned long requested_link_freq; bool lvds_format_24bpp; bool lvds_format_jeida; u32 reg; @@ -128,10 +165,15 @@ static void fsl_ldb_atomic_enable(struct drm_bridge *bridge, crtc_state = drm_atomic_get_new_crtc_state(state, crtc); mode = &crtc_state->adjusted_mode; - if (fsl_ldb->lvds_dual_link) - clk_set_rate(fsl_ldb->clk, mode->clock * 3500); - else - clk_set_rate(fsl_ldb->clk, mode->clock * 7000); + requested_link_freq = fsl_ldb_link_frequency(fsl_ldb, mode->clock); + clk_set_rate(fsl_ldb->clk, requested_link_freq); + + configured_link_freq = clk_get_rate(fsl_ldb->clk); + if (configured_link_freq != requested_link_freq) + dev_warn(fsl_ldb->dev, "Configured LDB clock (%lu Hz) does not match requested LVDS clock: %lu Hz", + configured_link_freq, + requested_link_freq); + clk_prepare_enable(fsl_ldb->clk); /* Program LDB_CTRL */ @@ -158,12 +200,12 @@ static void fsl_ldb_atomic_enable(struct drm_bridge *bridge, reg |= LDB_CTRL_DI1_VSYNC_POLARITY; } - regmap_write(fsl_ldb->regmap, LDB_CTRL, reg); + regmap_write(fsl_ldb->regmap, fsl_ldb->devdata->ldb_ctrl, reg); /* Program LVDS_CTRL */ reg = LVDS_CTRL_CC_ADJ(2) | LVDS_CTRL_PRE_EMPH_EN | LVDS_CTRL_PRE_EMPH_ADJ(3) | LVDS_CTRL_VBG_EN; - regmap_write(fsl_ldb->regmap, LVDS_CTRL, reg); + regmap_write(fsl_ldb->regmap, fsl_ldb->devdata->lvds_ctrl, reg); /* Wait for VBG to stabilize. */ usleep_range(15, 20); @@ -172,7 +214,7 @@ static void fsl_ldb_atomic_enable(struct drm_bridge *bridge, if (fsl_ldb->lvds_dual_link) reg |= LVDS_CTRL_CH1_EN; - regmap_write(fsl_ldb->regmap, LVDS_CTRL, reg); + regmap_write(fsl_ldb->regmap, fsl_ldb->devdata->lvds_ctrl, reg); } static void fsl_ldb_atomic_disable(struct drm_bridge *bridge, @@ -180,9 +222,14 @@ static void fsl_ldb_atomic_disable(struct drm_bridge *bridge, { struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge); - /* Stop both channels. */ - regmap_write(fsl_ldb->regmap, LVDS_CTRL, 0); - regmap_write(fsl_ldb->regmap, LDB_CTRL, 0); + /* Stop channel(s). */ + if (fsl_ldb->devdata->lvds_en_bit) + /* Set LVDS_CTRL_LVDS_EN bit to disable. */ + regmap_write(fsl_ldb->regmap, fsl_ldb->devdata->lvds_ctrl, + LVDS_CTRL_LVDS_EN); + else + regmap_write(fsl_ldb->regmap, fsl_ldb->devdata->lvds_ctrl, 0); + regmap_write(fsl_ldb->regmap, fsl_ldb->devdata->ldb_ctrl, 0); clk_disable_unprepare(fsl_ldb->clk); } @@ -248,6 +295,10 @@ static int fsl_ldb_probe(struct platform_device *pdev) if (!fsl_ldb) return -ENOMEM; + fsl_ldb->devdata = of_device_get_match_data(dev); + if (!fsl_ldb->devdata) + return -EINVAL; + fsl_ldb->dev = &pdev->dev; fsl_ldb->bridge.funcs = &funcs; fsl_ldb->bridge.of_node = dev->of_node; @@ -306,7 +357,10 @@ static int fsl_ldb_remove(struct platform_device *pdev) } static const struct of_device_id fsl_ldb_match[] = { - { .compatible = "fsl,imx8mp-ldb", }, + { .compatible = "fsl,imx8mp-ldb", + .data = &fsl_ldb_devdata[IMX8MP_LDB], }, + { .compatible = "fsl,imx93-ldb", + .data = &fsl_ldb_devdata[IMX93_LDB], }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, fsl_ldb_match); diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 21a9b8422bda..bc451b2a77c2 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -26,7 +26,6 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> @@ -412,7 +411,6 @@ struct it6505 { * Mutex protects extcon and interrupt functions from interfering * each other. */ - struct mutex irq_lock; struct mutex extcon_lock; struct mutex mode_lock; /* used to bridge_detect */ struct mutex aux_lock; /* used to aux data transfers */ @@ -438,6 +436,8 @@ struct it6505 { bool powered; bool hpd_state; u32 afe_setting; + u32 max_dpi_pixel_clock; + u32 max_lane_count; enum hdcp_state hdcp_status; struct delayed_work hdcp_work; struct work_struct hdcp_wait_ksv_list; @@ -457,6 +457,8 @@ struct it6505 { /* it6505 driver hold option */ bool enable_drv_hold; + + struct edid *cached_edid; }; struct it6505_step_train_para { @@ -1463,7 +1465,8 @@ static void it6505_parse_link_capabilities(struct it6505 *it6505) it6505->lane_count = link->num_lanes; DRM_DEV_DEBUG_DRIVER(dev, "Sink support %d lanes training", it6505->lane_count); - it6505->lane_count = min_t(int, it6505->lane_count, MAX_LANE_COUNT); + it6505->lane_count = min_t(int, it6505->lane_count, + it6505->max_lane_count); it6505->branch_device = drm_dp_is_branch(it6505->dpcd); DRM_DEV_DEBUG_DRIVER(dev, "Sink %sbranch device", @@ -2244,6 +2247,12 @@ static void it6505_plugged_status_to_codec(struct it6505 *it6505) status == connector_status_connected); } +static void it6505_remove_edid(struct it6505 *it6505) +{ + kfree(it6505->cached_edid); + it6505->cached_edid = NULL; +} + static int it6505_process_hpd_irq(struct it6505 *it6505) { struct device *dev = &it6505->client->dev; @@ -2270,6 +2279,7 @@ static int it6505_process_hpd_irq(struct it6505 *it6505) it6505_reset_logic(it6505); it6505_int_mask_enable(it6505); it6505_init(it6505); + it6505_remove_edid(it6505); return 0; } @@ -2353,6 +2363,7 @@ static void it6505_irq_hpd(struct it6505 *it6505) it6505_video_reset(it6505); } else { memset(it6505->dpcd, 0, sizeof(it6505->dpcd)); + it6505_remove_edid(it6505); if (it6505->hdcp_desired) it6505_stop_hdcp(it6505); @@ -2494,10 +2505,8 @@ static irqreturn_t it6505_int_threaded_handler(int unused, void *data) }; int int_status[3], i; - mutex_lock(&it6505->irq_lock); - - if (it6505->enable_drv_hold || !it6505->powered) - goto unlock; + if (it6505->enable_drv_hold || pm_runtime_get_if_in_use(dev) <= 0) + return IRQ_HANDLED; int_status[0] = it6505_read(it6505, INT_STATUS_01); int_status[1] = it6505_read(it6505, INT_STATUS_02); @@ -2515,16 +2524,14 @@ static irqreturn_t it6505_int_threaded_handler(int unused, void *data) if (it6505_test_bit(irq_vec[0].bit, (unsigned int *)int_status)) irq_vec[0].handler(it6505); - if (!it6505->hpd_state) - goto unlock; - - for (i = 1; i < ARRAY_SIZE(irq_vec); i++) { - if (it6505_test_bit(irq_vec[i].bit, (unsigned int *)int_status)) - irq_vec[i].handler(it6505); + if (it6505->hpd_state) { + for (i = 1; i < ARRAY_SIZE(irq_vec); i++) { + if (it6505_test_bit(irq_vec[i].bit, (unsigned int *)int_status)) + irq_vec[i].handler(it6505); + } } -unlock: - mutex_unlock(&it6505->irq_lock); + pm_runtime_put_sync(dev); return IRQ_HANDLED; } @@ -2902,7 +2909,7 @@ it6505_bridge_mode_valid(struct drm_bridge *bridge, if (mode->flags & DRM_MODE_FLAG_INTERLACE) return MODE_NO_INTERLACE; - if (mode->clock > DPI_PIXEL_CLK_MAX) + if (mode->clock > it6505->max_dpi_pixel_clock) return MODE_CLOCK_HIGH; it6505->video_info.clock = mode->clock; @@ -3016,16 +3023,18 @@ static struct edid *it6505_bridge_get_edid(struct drm_bridge *bridge, { struct it6505 *it6505 = bridge_to_it6505(bridge); struct device *dev = &it6505->client->dev; - struct edid *edid; - edid = drm_do_get_edid(connector, it6505_get_edid_block, it6505); + if (!it6505->cached_edid) { + it6505->cached_edid = drm_do_get_edid(connector, it6505_get_edid_block, + it6505); - if (!edid) { - DRM_DEV_DEBUG_DRIVER(dev, "failed to get edid!"); - return NULL; + if (!it6505->cached_edid) { + DRM_DEV_DEBUG_DRIVER(dev, "failed to get edid!"); + return NULL; + } } - return edid; + return drm_edid_duplicate(it6505->cached_edid); } static const struct drm_bridge_funcs it6505_bridge_funcs = { @@ -3089,10 +3098,32 @@ static int it6505_init_pdata(struct it6505 *it6505) return 0; } +static int it6505_get_data_lanes_count(const struct device_node *endpoint, + const unsigned int min, + const unsigned int max) +{ + int ret; + + ret = of_property_count_u32_elems(endpoint, "data-lanes"); + if (ret < 0) + return ret; + + if (ret < min || ret > max) + return -EINVAL; + + return ret; +} + static void it6505_parse_dt(struct it6505 *it6505) { struct device *dev = &it6505->client->dev; + struct device_node *np = dev->of_node, *ep = NULL; + int len; + u64 link_frequencies; + u32 data_lanes[4]; u32 *afe_setting = &it6505->afe_setting; + u32 *max_lane_count = &it6505->max_lane_count; + u32 *max_dpi_pixel_clock = &it6505->max_dpi_pixel_clock; it6505->lane_swap_disabled = device_property_read_bool(dev, "no-laneswap"); @@ -3108,7 +3139,56 @@ static void it6505_parse_dt(struct it6505 *it6505) } else { *afe_setting = 0; } - DRM_DEV_DEBUG_DRIVER(dev, "using afe_setting: %d", *afe_setting); + + ep = of_graph_get_endpoint_by_regs(np, 1, 0); + of_node_put(ep); + + if (ep) { + len = it6505_get_data_lanes_count(ep, 1, 4); + + if (len > 0 && len != 3) { + of_property_read_u32_array(ep, "data-lanes", + data_lanes, len); + *max_lane_count = len; + } else { + *max_lane_count = MAX_LANE_COUNT; + dev_err(dev, "error data-lanes, use default"); + } + } else { + *max_lane_count = MAX_LANE_COUNT; + dev_err(dev, "error endpoint, use default"); + } + + ep = of_graph_get_endpoint_by_regs(np, 0, 0); + of_node_put(ep); + + if (ep) { + len = of_property_read_variable_u64_array(ep, + "link-frequencies", + &link_frequencies, 0, + 1); + if (len >= 0) { + do_div(link_frequencies, 1000); + if (link_frequencies > 297000) { + dev_err(dev, + "max pixel clock error, use default"); + *max_dpi_pixel_clock = DPI_PIXEL_CLK_MAX; + } else { + *max_dpi_pixel_clock = link_frequencies; + } + } else { + dev_err(dev, "error link frequencies, use default"); + *max_dpi_pixel_clock = DPI_PIXEL_CLK_MAX; + } + } else { + dev_err(dev, "error endpoint, use default"); + *max_dpi_pixel_clock = DPI_PIXEL_CLK_MAX; + } + + DRM_DEV_DEBUG_DRIVER(dev, "using afe_setting: %u, max_lane_count: %u", + it6505->afe_setting, it6505->max_lane_count); + DRM_DEV_DEBUG_DRIVER(dev, "using max_dpi_pixel_clock: %u kHz", + it6505->max_dpi_pixel_clock); } static ssize_t receive_timing_debugfs_show(struct file *file, char __user *buf, @@ -3265,8 +3345,7 @@ static void it6505_shutdown(struct i2c_client *client) it6505_lane_off(it6505); } -static int it6505_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int it6505_i2c_probe(struct i2c_client *client) { struct it6505 *it6505; struct device *dev = &client->dev; @@ -3277,7 +3356,6 @@ static int it6505_i2c_probe(struct i2c_client *client, if (!it6505) return -ENOMEM; - mutex_init(&it6505->irq_lock); mutex_init(&it6505->extcon_lock); mutex_init(&it6505->mode_lock); mutex_init(&it6505->aux_lock); @@ -3367,6 +3445,7 @@ static void it6505_i2c_remove(struct i2c_client *client) drm_dp_aux_unregister(&it6505->aux); it6505_debugfs_remove(it6505); it6505_poweroff(it6505); + it6505_remove_edid(it6505); } static const struct i2c_device_id it6505_id[] = { @@ -3387,7 +3466,7 @@ static struct i2c_driver it6505_i2c_driver = { .of_match_table = it6505_of_match, .pm = &it6505_bridge_pm_ops, }, - .probe = it6505_i2c_probe, + .probe_new = it6505_i2c_probe, .remove = it6505_i2c_remove, .shutdown = it6505_shutdown, .id_table = it6505_id, diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 4f6f1deba28c..a2d723d6a4be 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -22,7 +22,6 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> #include <drm/drm_modes.h> #include <drm/drm_print.h> @@ -35,10 +34,6 @@ #define IT66121_DEVICE_ID0_REG 0x02 #define IT66121_DEVICE_ID1_REG 0x03 -#define IT66121_VENDOR_ID0 0x54 -#define IT66121_VENDOR_ID1 0x49 -#define IT66121_DEVICE_ID0 0x12 -#define IT66121_DEVICE_ID1 0x06 #define IT66121_REVISION_MASK GENMASK(7, 4) #define IT66121_DEVICE_ID1_MASK GENMASK(3, 0) @@ -72,6 +67,7 @@ #define IT66121_AFE_XP_ENO BIT(4) #define IT66121_AFE_XP_RESETB BIT(3) #define IT66121_AFE_XP_PWDI BIT(2) +#define IT6610_AFE_XP_BYPASS BIT(0) #define IT66121_AFE_IP_REG 0x64 #define IT66121_AFE_IP_GAINBIT BIT(7) @@ -286,13 +282,18 @@ #define IT66121_AUD_SWL_16BIT 0x2 #define IT66121_AUD_SWL_NOT_INDICATED 0x0 -#define IT66121_VENDOR_ID0 0x54 -#define IT66121_VENDOR_ID1 0x49 -#define IT66121_DEVICE_ID0 0x12 -#define IT66121_DEVICE_ID1 0x06 -#define IT66121_DEVICE_MASK 0x0F #define IT66121_AFE_CLK_HIGH 80000 /* Khz */ +enum chip_id { + ID_IT6610, + ID_IT66121, +}; + +struct it66121_chip_info { + enum chip_id id; + u16 vid, pid; +}; + struct it66121_ctx { struct regmap *regmap; struct drm_bridge bridge; @@ -301,7 +302,6 @@ struct it66121_ctx { struct device *dev; struct gpio_desc *gpio_reset; struct i2c_client *client; - struct regulator_bulk_data supplies[3]; u32 bus_width; struct mutex lock; /* Protects fields below and device registers */ struct hdmi_avi_infoframe hdmi_avi_infoframe; @@ -312,6 +312,7 @@ struct it66121_ctx { u8 swl; bool auto_cts; } audio; + const struct it66121_chip_info *info; }; static const struct regmap_range_cfg it66121_regmap_banks[] = { @@ -342,16 +343,6 @@ static void it66121_hw_reset(struct it66121_ctx *ctx) gpiod_set_value(ctx->gpio_reset, 0); } -static inline int ite66121_power_on(struct it66121_ctx *ctx) -{ - return regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); -} - -static inline int ite66121_power_off(struct it66121_ctx *ctx) -{ - return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); -} - static inline int it66121_preamble_ddc(struct it66121_ctx *ctx) { return regmap_write(ctx->regmap, IT66121_MASTER_SEL_REG, IT66121_MASTER_SEL_HOST); @@ -406,16 +397,22 @@ static int it66121_configure_afe(struct it66121_ctx *ctx, ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, IT66121_AFE_IP_GAINBIT | - IT66121_AFE_IP_ER0 | - IT66121_AFE_IP_EC1, + IT66121_AFE_IP_ER0, IT66121_AFE_IP_GAINBIT); if (ret) return ret; - ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, - IT66121_AFE_XP_EC1_LOWCLK, 0x80); - if (ret) - return ret; + if (ctx->info->id == ID_IT66121) { + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_EC1, 0); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, + IT66121_AFE_XP_EC1_LOWCLK, 0x80); + if (ret) + return ret; + } } else { ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG, IT66121_AFE_XP_GAINBIT | @@ -426,17 +423,24 @@ static int it66121_configure_afe(struct it66121_ctx *ctx, ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, IT66121_AFE_IP_GAINBIT | - IT66121_AFE_IP_ER0 | - IT66121_AFE_IP_EC1, IT66121_AFE_IP_ER0 | - IT66121_AFE_IP_EC1); + IT66121_AFE_IP_ER0, + IT66121_AFE_IP_ER0); if (ret) return ret; - ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, - IT66121_AFE_XP_EC1_LOWCLK, - IT66121_AFE_XP_EC1_LOWCLK); - if (ret) - return ret; + if (ctx->info->id == ID_IT66121) { + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_EC1, + IT66121_AFE_IP_EC1); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, + IT66121_AFE_XP_EC1_LOWCLK, + IT66121_AFE_XP_EC1_LOWCLK); + if (ret) + return ret; + } } /* Clear reset flags */ @@ -445,38 +449,36 @@ static int it66121_configure_afe(struct it66121_ctx *ctx, if (ret) return ret; + if (ctx->info->id == ID_IT6610) { + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG, + IT6610_AFE_XP_BYPASS, + IT6610_AFE_XP_BYPASS); + if (ret) + return ret; + } + return it66121_fire_afe(ctx); } static inline int it66121_wait_ddc_ready(struct it66121_ctx *ctx) { int ret, val; - u32 busy = IT66121_DDC_STATUS_NOACK | IT66121_DDC_STATUS_WAIT_BUS | - IT66121_DDC_STATUS_ARBI_LOSE; + u32 error = IT66121_DDC_STATUS_NOACK | IT66121_DDC_STATUS_WAIT_BUS | + IT66121_DDC_STATUS_ARBI_LOSE; + u32 done = IT66121_DDC_STATUS_TX_DONE; - ret = regmap_read_poll_timeout(ctx->regmap, IT66121_DDC_STATUS_REG, val, true, - IT66121_EDID_SLEEP_US, IT66121_EDID_TIMEOUT_US); + ret = regmap_read_poll_timeout(ctx->regmap, IT66121_DDC_STATUS_REG, val, + val & (error | done), IT66121_EDID_SLEEP_US, + IT66121_EDID_TIMEOUT_US); if (ret) return ret; - if (val & busy) + if (val & error) return -EAGAIN; return 0; } -static int it66121_clear_ddc_fifo(struct it66121_ctx *ctx) -{ - int ret; - - ret = it66121_preamble_ddc(ctx); - if (ret) - return ret; - - return regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG, - IT66121_DDC_COMMAND_FIFO_CLR); -} - static int it66121_abort_ddc_ops(struct it66121_ctx *ctx) { int ret; @@ -516,7 +518,6 @@ static int it66121_get_edid_block(void *context, u8 *buf, unsigned int block, size_t len) { struct it66121_ctx *ctx = context; - unsigned int val; int remain = len; int offset = 0; int ret, cnt; @@ -524,26 +525,9 @@ static int it66121_get_edid_block(void *context, u8 *buf, offset = (block % 2) * len; block = block / 2; - ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); - if (ret) - return ret; - - if (val & IT66121_INT_STATUS1_DDC_BUSHANG) { - ret = it66121_abort_ddc_ops(ctx); - if (ret) - return ret; - } - - ret = it66121_clear_ddc_fifo(ctx); - if (ret) - return ret; - while (remain > 0) { cnt = (remain > IT66121_EDID_FIFO_SIZE) ? IT66121_EDID_FIFO_SIZE : remain; - ret = it66121_preamble_ddc(ctx); - if (ret) - return ret; ret = regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG, IT66121_DDC_COMMAND_FIFO_CLR); @@ -554,25 +538,6 @@ static int it66121_get_edid_block(void *context, u8 *buf, if (ret) return ret; - ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); - if (ret) - return ret; - - if (val & IT66121_INT_STATUS1_DDC_BUSHANG) { - ret = it66121_abort_ddc_ops(ctx); - if (ret) - return ret; - } - - ret = it66121_preamble_ddc(ctx); - if (ret) - return ret; - - ret = regmap_write(ctx->regmap, IT66121_DDC_HEADER_REG, - IT66121_DDC_HEADER_EDID); - if (ret) - return ret; - ret = regmap_write(ctx->regmap, IT66121_DDC_OFFSET_REG, offset); if (ret) return ret; @@ -593,20 +558,18 @@ static int it66121_get_edid_block(void *context, u8 *buf, offset += cnt; remain -= cnt; - /* Per programming manual, sleep here before emptying the FIFO */ - msleep(20); - ret = it66121_wait_ddc_ready(ctx); + if (ret) { + it66121_abort_ddc_ops(ctx); + return ret; + } + + ret = regmap_noinc_read(ctx->regmap, IT66121_DDC_RD_FIFO_REG, + buf, cnt); if (ret) return ret; - do { - ret = regmap_read(ctx->regmap, IT66121_DDC_RD_FIFO_REG, &val); - if (ret) - return ret; - *(buf++) = val; - cnt--; - } while (cnt > 0); + buf += cnt; } return 0; @@ -635,10 +598,12 @@ static int it66121_bridge_attach(struct drm_bridge *bridge, if (ret) return ret; - ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, - IT66121_CLK_BANK_PWROFF_RCLK, 0); - if (ret) - return ret; + if (ctx->info->id == ID_IT66121) { + ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_RCLK, 0); + if (ret) + return ret; + } ret = regmap_write_bits(ctx->regmap, IT66121_INT_REG, IT66121_INT_TX_CLK_OFF, 0); @@ -684,11 +649,7 @@ static int it66121_bridge_attach(struct drm_bridge *bridge, /* Per programming manual, sleep here for bridge to settle */ msleep(50); - /* Start interrupts */ - return regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, - IT66121_INT_MASK1_DDC_NOACK | - IT66121_INT_MASK1_DDC_FIFOERR | - IT66121_INT_MASK1_DDC_BUSHANG, 0); + return 0; } static int it66121_set_mute(struct it66121_ctx *ctx, bool mute) @@ -780,29 +741,32 @@ static void it66121_bridge_disable(struct drm_bridge *bridge, ctx->connector = NULL; } +static int it66121_bridge_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + + if (ctx->info->id == ID_IT6610) { + /* The IT6610 only supports these settings */ + bridge_state->input_bus_cfg.flags |= DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; + bridge_state->input_bus_cfg.flags &= + ~DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; + } + + return 0; +} + static void it66121_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) { - int ret, i; u8 buf[HDMI_INFOFRAME_SIZE(AVI)]; struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); - const u16 aviinfo_reg[HDMI_AVI_INFOFRAME_SIZE] = { - IT66121_AVIINFO_DB1_REG, - IT66121_AVIINFO_DB2_REG, - IT66121_AVIINFO_DB3_REG, - IT66121_AVIINFO_DB4_REG, - IT66121_AVIINFO_DB5_REG, - IT66121_AVIINFO_DB6_REG, - IT66121_AVIINFO_DB7_REG, - IT66121_AVIINFO_DB8_REG, - IT66121_AVIINFO_DB9_REG, - IT66121_AVIINFO_DB10_REG, - IT66121_AVIINFO_DB11_REG, - IT66121_AVIINFO_DB12_REG, - IT66121_AVIINFO_DB13_REG - }; + int ret; mutex_lock(&ctx->lock); @@ -822,10 +786,12 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge, } /* Write new AVI infoframe packet */ - for (i = 0; i < HDMI_AVI_INFOFRAME_SIZE; i++) { - if (regmap_write(ctx->regmap, aviinfo_reg[i], buf[i + HDMI_INFOFRAME_HEADER_SIZE])) - goto unlock; - } + ret = regmap_bulk_write(ctx->regmap, IT66121_AVIINFO_DB1_REG, + &buf[HDMI_INFOFRAME_HEADER_SIZE], + HDMI_AVI_INFOFRAME_SIZE); + if (ret) + goto unlock; + if (regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buf[3])) goto unlock; @@ -838,9 +804,12 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge, if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI)) goto unlock; - if (regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, - IT66121_CLK_BANK_PWROFF_TXCLK, IT66121_CLK_BANK_PWROFF_TXCLK)) + if (ctx->info->id == ID_IT66121 && + regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_TXCLK, + IT66121_CLK_BANK_PWROFF_TXCLK)) { goto unlock; + } if (it66121_configure_input(ctx)) goto unlock; @@ -848,7 +817,11 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge, if (it66121_configure_afe(ctx, adjusted_mode)) goto unlock; - regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, IT66121_CLK_BANK_PWROFF_TXCLK, 0); + if (ctx->info->id == ID_IT66121 && + regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_TXCLK, 0)) { + goto unlock; + } unlock: mutex_unlock(&ctx->lock); @@ -906,9 +879,25 @@ static struct edid *it66121_bridge_get_edid(struct drm_bridge *bridge, { struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); struct edid *edid; + int ret; mutex_lock(&ctx->lock); + ret = it66121_preamble_ddc(ctx); + if (ret) { + edid = ERR_PTR(ret); + goto out_unlock; + } + + ret = regmap_write(ctx->regmap, IT66121_DDC_HEADER_REG, + IT66121_DDC_HEADER_EDID); + if (ret) { + edid = ERR_PTR(ret); + goto out_unlock; + } + edid = drm_do_get_edid(connector, it66121_get_edid_block, ctx); + +out_unlock: mutex_unlock(&ctx->lock); return edid; @@ -923,6 +912,7 @@ static const struct drm_bridge_funcs it66121_bridge_funcs = { .atomic_get_input_bus_fmts = it66121_bridge_atomic_get_input_bus_fmts, .atomic_enable = it66121_bridge_enable, .atomic_disable = it66121_bridge_disable, + .atomic_check = it66121_bridge_check, .mode_set = it66121_bridge_mode_set, .mode_valid = it66121_bridge_mode_valid, .detect = it66121_bridge_detect, @@ -952,21 +942,14 @@ static irqreturn_t it66121_irq_threaded_handler(int irq, void *dev_id) ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); if (ret) { dev_err(dev, "Cannot read STATUS1_REG %d\n", ret); - } else { - if (val & IT66121_INT_STATUS1_DDC_FIFOERR) - it66121_clear_ddc_fifo(ctx); - if (val & (IT66121_INT_STATUS1_DDC_BUSHANG | - IT66121_INT_STATUS1_DDC_NOACK)) - it66121_abort_ddc_ops(ctx); - if (val & IT66121_INT_STATUS1_HPD_STATUS) { - regmap_write_bits(ctx->regmap, IT66121_INT_CLR1_REG, - IT66121_INT_CLR1_HPD, IT66121_INT_CLR1_HPD); + } else if (val & IT66121_INT_STATUS1_HPD_STATUS) { + regmap_write_bits(ctx->regmap, IT66121_INT_CLR1_REG, + IT66121_INT_CLR1_HPD, IT66121_INT_CLR1_HPD); - status = it66121_is_hpd_detect(ctx) ? connector_status_connected - : connector_status_disconnected; + status = it66121_is_hpd_detect(ctx) ? connector_status_connected + : connector_status_disconnected; - event = true; - } + event = true; } regmap_write_bits(ctx->regmap, IT66121_SYS_STATUS_REG, @@ -1512,9 +1495,13 @@ static int it66121_audio_codec_init(struct it66121_ctx *ctx, struct device *dev) return PTR_ERR_OR_ZERO(ctx->audio.pdev); } -static int it66121_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const char * const it66121_supplies[] = { + "vcn33", "vcn18", "vrf12" +}; + +static int it66121_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); u32 revision_id, vendor_ids[2] = { 0 }, device_ids[2] = { 0 }; struct device_node *ep; int ret; @@ -1536,6 +1523,7 @@ static int it66121_probe(struct i2c_client *client, ctx->dev = dev; ctx->client = client; + ctx->info = (const struct it66121_chip_info *) id->driver_data; of_property_read_u32(ep, "bus-width", &ctx->bus_width); of_node_put(ep); @@ -1565,26 +1553,18 @@ static int it66121_probe(struct i2c_client *client, i2c_set_clientdata(client, ctx); mutex_init(&ctx->lock); - ctx->supplies[0].supply = "vcn33"; - ctx->supplies[1].supply = "vcn18"; - ctx->supplies[2].supply = "vrf12"; - ret = devm_regulator_bulk_get(ctx->dev, 3, ctx->supplies); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(it66121_supplies), + it66121_supplies); if (ret) { - dev_err(ctx->dev, "regulator_bulk failed\n"); + dev_err(dev, "Failed to enable power supplies\n"); return ret; } - ret = ite66121_power_on(ctx); - if (ret) - return ret; - it66121_hw_reset(ctx); ctx->regmap = devm_regmap_init_i2c(client, &it66121_regmap_config); - if (IS_ERR(ctx->regmap)) { - ite66121_power_off(ctx); + if (IS_ERR(ctx->regmap)) return PTR_ERR(ctx->regmap); - } regmap_read(ctx->regmap, IT66121_VENDOR_ID0_REG, &vendor_ids[0]); regmap_read(ctx->regmap, IT66121_VENDOR_ID1_REG, &vendor_ids[1]); @@ -1595,9 +1575,8 @@ static int it66121_probe(struct i2c_client *client, revision_id = FIELD_GET(IT66121_REVISION_MASK, device_ids[1]); device_ids[1] &= IT66121_DEVICE_ID1_MASK; - if (vendor_ids[0] != IT66121_VENDOR_ID0 || vendor_ids[1] != IT66121_VENDOR_ID1 || - device_ids[0] != IT66121_DEVICE_ID0 || device_ids[1] != IT66121_DEVICE_ID1) { - ite66121_power_off(ctx); + if ((vendor_ids[1] << 8 | vendor_ids[0]) != ctx->info->vid || + (device_ids[1] << 8 | device_ids[0]) != ctx->info->pid) { return -ENODEV; } @@ -1610,7 +1589,6 @@ static int it66121_probe(struct i2c_client *client, IRQF_ONESHOT, dev_name(dev), ctx); if (ret < 0) { dev_err(dev, "Failed to request irq %d:%d\n", client->irq, ret); - ite66121_power_off(ctx); return ret; } @@ -1627,19 +1605,32 @@ static void it66121_remove(struct i2c_client *client) { struct it66121_ctx *ctx = i2c_get_clientdata(client); - ite66121_power_off(ctx); drm_bridge_remove(&ctx->bridge); mutex_destroy(&ctx->lock); } static const struct of_device_id it66121_dt_match[] = { { .compatible = "ite,it66121" }, + { .compatible = "ite,it6610" }, { } }; MODULE_DEVICE_TABLE(of, it66121_dt_match); +static const struct it66121_chip_info it66121_chip_info = { + .id = ID_IT66121, + .vid = 0x4954, + .pid = 0x0612, +}; + +static const struct it66121_chip_info it6610_chip_info = { + .id = ID_IT6610, + .vid = 0xca00, + .pid = 0x0611, +}; + static const struct i2c_device_id it66121_id[] = { - { "it66121", 0 }, + { "it66121", (kernel_ulong_t) &it66121_chip_info }, + { "it6610", (kernel_ulong_t) &it6610_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, it66121_id); @@ -1649,7 +1640,7 @@ static struct i2c_driver it66121_driver = { .name = "it66121", .of_match_table = it66121_dt_match, }, - .probe = it66121_probe, + .probe_new = it66121_probe, .remove = it66121_remove, .id_table = it66121_id, }; diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c index a98efef0ba0e..2019a8167d69 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8912b.c +++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c @@ -517,14 +517,27 @@ static int lt8912_attach_dsi(struct lt8912 *lt) return 0; } +static void lt8912_bridge_hpd_cb(void *data, enum drm_connector_status status) +{ + struct lt8912 *lt = data; + + if (lt->bridge.dev) + drm_helper_hpd_irq_event(lt->bridge.dev); +} + static int lt8912_bridge_connector_init(struct drm_bridge *bridge) { int ret; struct lt8912 *lt = bridge_to_lt8912(bridge); struct drm_connector *connector = <->connector; - connector->polled = DRM_CONNECTOR_POLL_CONNECT | - DRM_CONNECTOR_POLL_DISCONNECT; + if (lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD) { + drm_bridge_hpd_enable(lt->hdmi_port, lt8912_bridge_hpd_cb, lt); + connector->polled = DRM_CONNECTOR_POLL_HPD; + } else { + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; + } ret = drm_connector_init(bridge->dev, connector, <8912_connector_funcs, @@ -578,6 +591,10 @@ static void lt8912_bridge_detach(struct drm_bridge *bridge) if (lt->is_attached) { lt8912_hard_power_off(lt); + + if (lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD) + drm_bridge_hpd_disable(lt->hdmi_port); + drm_connector_unregister(<->connector); drm_connector_cleanup(<->connector); } @@ -685,8 +702,7 @@ static int lt8912_put_dt(struct lt8912 *lt) return 0; } -static int lt8912_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lt8912_probe(struct i2c_client *client) { static struct lt8912 *lt; int ret = 0; @@ -758,7 +774,7 @@ static struct i2c_driver lt8912_i2c_driver = { .name = "lt8912", .of_match_table = lt8912_dt_match, }, - .probe = lt8912_probe, + .probe_new = lt8912_probe, .remove = lt8912_remove, .id_table = lt8912_id, }; diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c index 933ca028d612..3e19fff6547a 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9211.c +++ b/drivers/gpu/drm/bridge/lontium-lt9211.c @@ -720,8 +720,7 @@ static int lt9211_host_attach(struct lt9211 *ctx) return 0; } -static int lt9211_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lt9211_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct lt9211 *ctx; @@ -786,7 +785,7 @@ static const struct of_device_id lt9211_match_table[] = { MODULE_DEVICE_TABLE(of, lt9211_match_table); static struct i2c_driver lt9211_driver = { - .probe = lt9211_probe, + .probe_new = lt9211_probe, .remove = lt9211_remove, .id_table = lt9211_id, .driver = { diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index 7c0a99173b39..a25d21a7d5c1 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -19,6 +19,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_mipi_dsi.h> +#include <drm/drm_of.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> @@ -33,7 +34,7 @@ struct lt9611 { struct device *dev; struct drm_bridge bridge; - struct drm_connector connector; + struct drm_bridge *next_bridge; struct regmap *regmap; @@ -58,7 +59,6 @@ struct lt9611 { enum drm_connector_status status; u8 edid_buf[EDID_SEG_SIZE]; - u32 vic; }; #define LT9611_PAGE_CONTROL 0xff @@ -84,34 +84,11 @@ static const struct regmap_config lt9611_regmap_config = { .num_ranges = ARRAY_SIZE(lt9611_ranges), }; -struct lt9611_mode { - u16 hdisplay; - u16 vdisplay; - u8 vrefresh; - u8 lanes; - u8 intfs; -}; - -static struct lt9611_mode lt9611_modes[] = { - { 3840, 2160, 30, 4, 2 }, /* 3840x2160 24bit 30Hz 4Lane 2ports */ - { 1920, 1080, 60, 4, 1 }, /* 1080P 24bit 60Hz 4lane 1port */ - { 1920, 1080, 30, 3, 1 }, /* 1080P 24bit 30Hz 3lane 1port */ - { 1920, 1080, 24, 3, 1 }, - { 720, 480, 60, 4, 1 }, - { 720, 576, 50, 2, 1 }, - { 640, 480, 60, 2, 1 }, -}; - static struct lt9611 *bridge_to_lt9611(struct drm_bridge *bridge) { return container_of(bridge, struct lt9611, bridge); } -static struct lt9611 *connector_to_lt9611(struct drm_connector *connector) -{ - return container_of(connector, struct lt9611, connector); -} - static int lt9611_mipi_input_analog(struct lt9611 *lt9611) { const struct reg_sequence reg_cfg[] = { @@ -141,7 +118,7 @@ static int lt9611_mipi_input_digital(struct lt9611 *lt9611, { 0x8306, 0x0a }, }; - if (mode->hdisplay == 3840) + if (lt9611->dsi1_node) reg_cfg[1].def = 0x03; return regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg)); @@ -159,12 +136,12 @@ static void lt9611_mipi_video_setup(struct lt9611 *lt9611, hactive = mode->hdisplay; hsync_len = mode->hsync_end - mode->hsync_start; hfront_porch = mode->hsync_start - mode->hdisplay; - hsync_porch = hsync_len + mode->htotal - mode->hsync_end; + hsync_porch = mode->htotal - mode->hsync_start; vactive = mode->vdisplay; vsync_len = mode->vsync_end - mode->vsync_start; vfront_porch = mode->vsync_start - mode->vdisplay; - vsync_porch = vsync_len + mode->vtotal - mode->vsync_end; + vsync_porch = mode->vtotal - mode->vsync_start; regmap_write(lt9611->regmap, 0x830d, (u8)(v_total / 256)); regmap_write(lt9611->regmap, 0x830e, (u8)(v_total % 256)); @@ -187,12 +164,14 @@ static void lt9611_mipi_video_setup(struct lt9611 *lt9611, regmap_write(lt9611->regmap, 0x8319, (u8)(hfront_porch % 256)); - regmap_write(lt9611->regmap, 0x831a, (u8)(hsync_porch / 256)); + regmap_write(lt9611->regmap, 0x831a, (u8)(hsync_porch / 256) | + ((hfront_porch / 256) << 4)); regmap_write(lt9611->regmap, 0x831b, (u8)(hsync_porch % 256)); } -static void lt9611_pcr_setup(struct lt9611 *lt9611, const struct drm_display_mode *mode) +static void lt9611_pcr_setup(struct lt9611 *lt9611, const struct drm_display_mode *mode, unsigned int postdiv) { + unsigned int pcr_m = mode->clock * 5 * postdiv / 27000; const struct reg_sequence reg_cfg[] = { { 0x830b, 0x01 }, { 0x830c, 0x10 }, @@ -207,45 +186,40 @@ static void lt9611_pcr_setup(struct lt9611 *lt9611, const struct drm_display_mod /* stage 2 */ { 0x834a, 0x40 }, - { 0x831d, 0x10 }, /* MK limit */ { 0x832d, 0x38 }, { 0x8331, 0x08 }, }; - const struct reg_sequence reg_cfg2[] = { - { 0x830b, 0x03 }, - { 0x830c, 0xd0 }, - { 0x8348, 0x03 }, - { 0x8349, 0xe0 }, - { 0x8324, 0x72 }, - { 0x8325, 0x00 }, - { 0x832a, 0x01 }, - { 0x834a, 0x10 }, - { 0x831d, 0x10 }, - { 0x8326, 0x37 }, - }; + u8 pol = 0x10; - regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg)); + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + pol |= 0x2; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + pol |= 0x1; + regmap_write(lt9611->regmap, 0x831d, pol); - switch (mode->hdisplay) { - case 640: - regmap_write(lt9611->regmap, 0x8326, 0x14); - break; - case 1920: - regmap_write(lt9611->regmap, 0x8326, 0x37); - break; - case 3840: - regmap_multi_reg_write(lt9611->regmap, reg_cfg2, ARRAY_SIZE(reg_cfg2)); - break; + regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg)); + if (lt9611->dsi1_node) { + unsigned int hact = mode->hdisplay; + + hact >>= 2; + hact += 0x50; + hact = min(hact, 0x3e0U); + regmap_write(lt9611->regmap, 0x830b, hact / 256); + regmap_write(lt9611->regmap, 0x830c, hact % 256); + regmap_write(lt9611->regmap, 0x8348, hact / 256); + regmap_write(lt9611->regmap, 0x8349, hact % 256); } + regmap_write(lt9611->regmap, 0x8326, pcr_m); + /* pcr rst */ regmap_write(lt9611->regmap, 0x8011, 0x5a); regmap_write(lt9611->regmap, 0x8011, 0xfa); } -static int lt9611_pll_setup(struct lt9611 *lt9611, const struct drm_display_mode *mode) +static int lt9611_pll_setup(struct lt9611 *lt9611, const struct drm_display_mode *mode, unsigned int *postdiv) { unsigned int pclk = mode->clock; const struct reg_sequence reg_cfg[] = { @@ -259,16 +233,21 @@ static int lt9611_pll_setup(struct lt9611 *lt9611, const struct drm_display_mode { 0x8126, 0x55 }, { 0x8127, 0x66 }, { 0x8128, 0x88 }, + { 0x812a, 0x20 }, }; regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg)); - if (pclk > 150000) + if (pclk > 150000) { regmap_write(lt9611->regmap, 0x812d, 0x88); - else if (pclk > 70000) + *postdiv = 1; + } else if (pclk > 70000) { regmap_write(lt9611->regmap, 0x812d, 0x99); - else + *postdiv = 2; + } else { regmap_write(lt9611->regmap, 0x812d, 0xaa); + *postdiv = 4; + } /* * first divide pclk by 2 first @@ -353,13 +332,55 @@ end: return temp; } -static void lt9611_hdmi_tx_digital(struct lt9611 *lt9611) +static void lt9611_hdmi_set_infoframes(struct lt9611 *lt9611, + struct drm_connector *connector, + struct drm_display_mode *mode) { - regmap_write(lt9611->regmap, 0x8443, 0x46 - lt9611->vic); - regmap_write(lt9611->regmap, 0x8447, lt9611->vic); - regmap_write(lt9611->regmap, 0x843d, 0x0a); /* UD1 infoframe */ + union hdmi_infoframe infoframe; + ssize_t len; + u8 iframes = 0x0a; /* UD1 infoframe */ + u8 buf[32]; + int ret; + int i; - regmap_write(lt9611->regmap, 0x82d6, 0x8c); + ret = drm_hdmi_avi_infoframe_from_display_mode(&infoframe.avi, + connector, + mode); + if (ret < 0) + goto out; + + len = hdmi_infoframe_pack(&infoframe, buf, sizeof(buf)); + if (len < 0) + goto out; + + for (i = 0; i < len; i++) + regmap_write(lt9611->regmap, 0x8440 + i, buf[i]); + + ret = drm_hdmi_vendor_infoframe_from_display_mode(&infoframe.vendor.hdmi, + connector, + mode); + if (ret < 0) + goto out; + + len = hdmi_infoframe_pack(&infoframe, buf, sizeof(buf)); + if (len < 0) + goto out; + + for (i = 0; i < len; i++) + regmap_write(lt9611->regmap, 0x8474 + i, buf[i]); + + iframes |= 0x20; + +out: + regmap_write(lt9611->regmap, 0x843d, iframes); /* UD1 infoframe */ +} + +static void lt9611_hdmi_tx_digital(struct lt9611 *lt9611, bool is_hdmi) +{ + if (is_hdmi) + regmap_write(lt9611->regmap, 0x82d6, 0x8c); + else + regmap_write(lt9611->regmap, 0x82d6, 0x0c); regmap_write(lt9611->regmap, 0x82d7, 0x04); } @@ -448,12 +469,11 @@ static void lt9611_sleep_setup(struct lt9611 *lt9611) { 0x8023, 0x01 }, { 0x8157, 0x03 }, /* set addr pin as output */ { 0x8149, 0x0b }, - { 0x8151, 0x30 }, /* disable IRQ */ + { 0x8102, 0x48 }, /* MIPI Rx power down */ { 0x8123, 0x80 }, { 0x8130, 0x00 }, - { 0x8100, 0x01 }, /* bandgap power down */ - { 0x8101, 0x00 }, /* system clk power down */ + { 0x8011, 0x0a }, }; regmap_multi_reg_write(lt9611->regmap, @@ -564,24 +584,9 @@ static int lt9611_regulator_enable(struct lt9611 *lt9611) return 0; } -static struct lt9611_mode *lt9611_find_mode(const struct drm_display_mode *mode) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(lt9611_modes); i++) { - if (lt9611_modes[i].hdisplay == mode->hdisplay && - lt9611_modes[i].vdisplay == mode->vdisplay && - lt9611_modes[i].vrefresh == drm_mode_vrefresh(mode)) { - return <9611_modes[i]; - } - } - - return NULL; -} - -/* connector funcs */ -static enum drm_connector_status __lt9611_detect(struct lt9611 *lt9611) +static enum drm_connector_status lt9611_bridge_detect(struct drm_bridge *bridge) { + struct lt9611 *lt9611 = bridge_to_lt9611(bridge); unsigned int reg_val = 0; int connected = 0; @@ -594,12 +599,6 @@ static enum drm_connector_status __lt9611_detect(struct lt9611 *lt9611) return lt9611->status; } -static enum drm_connector_status -lt9611_connector_detect(struct drm_connector *connector, bool force) -{ - return __lt9611_detect(connector_to_lt9611(connector)); -} - static int lt9611_read_edid(struct lt9611 *lt9611) { unsigned int temp; @@ -681,36 +680,37 @@ lt9611_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len) return 0; } -static int lt9611_connector_get_modes(struct drm_connector *connector) -{ - struct lt9611 *lt9611 = connector_to_lt9611(connector); - unsigned int count; - struct edid *edid; - - lt9611_power_on(lt9611); - edid = drm_do_get_edid(connector, lt9611_get_edid_block, lt9611); - drm_connector_update_edid_property(connector, edid); - count = drm_add_edid_modes(connector, edid); - kfree(edid); - - return count; -} - -static enum drm_mode_status -lt9611_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct lt9611_mode *lt9611_mode = lt9611_find_mode(mode); - - return lt9611_mode ? MODE_OK : MODE_BAD; -} - /* bridge funcs */ static void lt9611_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_bridge_state *old_bridge_state) { struct lt9611 *lt9611 = bridge_to_lt9611(bridge); + struct drm_atomic_state *state = old_bridge_state->base.state; + struct drm_connector *connector; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + struct drm_display_mode *mode; + unsigned int postdiv; + + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + if (WARN_ON(!connector)) + return; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; + + mode = &crtc_state->adjusted_mode; + + lt9611_mipi_input_digital(lt9611, mode); + lt9611_pll_setup(lt9611, mode, &postdiv); + lt9611_mipi_video_setup(lt9611, mode); + lt9611_pcr_setup(lt9611, mode, postdiv); if (lt9611_power_on(lt9611)) { dev_err(lt9611->dev, "power on failed\n"); @@ -718,7 +718,8 @@ lt9611_bridge_atomic_enable(struct drm_bridge *bridge, } lt9611_mipi_input_analog(lt9611); - lt9611_hdmi_tx_digital(lt9611); + lt9611_hdmi_set_infoframes(lt9611, connector, mode); + lt9611_hdmi_tx_digital(lt9611, connector->display_info.is_hdmi); lt9611_hdmi_tx_phy(lt9611); msleep(500); @@ -749,25 +750,10 @@ lt9611_bridge_atomic_disable(struct drm_bridge *bridge, } } -static struct -drm_connector_helper_funcs lt9611_bridge_connector_helper_funcs = { - .get_modes = lt9611_connector_get_modes, - .mode_valid = lt9611_connector_mode_valid, -}; - -static const struct drm_connector_funcs lt9611_bridge_connector_funcs = { - .fill_modes = drm_helper_probe_single_connector_modes, - .detect = lt9611_connector_detect, - .destroy = drm_connector_cleanup, - .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 struct mipi_dsi_device *lt9611_attach_dsi(struct lt9611 *lt9611, struct device_node *dsi_node) { - const struct mipi_dsi_device_info info = { "lt9611", 0, NULL }; + const struct mipi_dsi_device_info info = { "lt9611", 0, lt9611->dev->of_node}; struct mipi_dsi_device *dsi; struct mipi_dsi_host *host; struct device *dev = lt9611->dev; @@ -799,70 +785,54 @@ static struct mipi_dsi_device *lt9611_attach_dsi(struct lt9611 *lt9611, return dsi; } -static int lt9611_connector_init(struct drm_bridge *bridge, struct lt9611 *lt9611) -{ - int ret; - - ret = drm_connector_init(bridge->dev, <9611->connector, - <9611_bridge_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); - if (ret) { - DRM_ERROR("Failed to initialize connector with drm\n"); - return ret; - } - - drm_connector_helper_add(<9611->connector, - <9611_bridge_connector_helper_funcs); - - if (!bridge->encoder) { - DRM_ERROR("Parent encoder object not found"); - return -ENODEV; - } - - drm_connector_attach_encoder(<9611->connector, bridge->encoder); - - return 0; -} - static int lt9611_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) { struct lt9611 *lt9611 = bridge_to_lt9611(bridge); - int ret; - if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { - ret = lt9611_connector_init(bridge, lt9611); - if (ret < 0) - return ret; - } - - return 0; + return drm_bridge_attach(bridge->encoder, lt9611->next_bridge, + bridge, flags); } static enum drm_mode_status lt9611_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, const struct drm_display_mode *mode) { - struct lt9611_mode *lt9611_mode = lt9611_find_mode(mode); struct lt9611 *lt9611 = bridge_to_lt9611(bridge); - if (!lt9611_mode) - return MODE_BAD; - else if (lt9611_mode->intfs > 1 && !lt9611->dsi1) + if (mode->hdisplay > 3840) + return MODE_BAD_HVALUE; + + if (mode->vdisplay > 2160) + return MODE_BAD_VVALUE; + + if (mode->hdisplay == 3840 && + mode->vdisplay == 2160 && + drm_mode_vrefresh(mode) > 30) + return MODE_CLOCK_HIGH; + + if (mode->hdisplay > 2000 && !lt9611->dsi1_node) return MODE_PANEL; else return MODE_OK; } -static void lt9611_bridge_pre_enable(struct drm_bridge *bridge) +static void lt9611_bridge_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct lt9611 *lt9611 = bridge_to_lt9611(bridge); + static const struct reg_sequence reg_cfg[] = { + { 0x8102, 0x12 }, + { 0x8123, 0x40 }, + { 0x8130, 0xea }, + { 0x8011, 0xfa }, + }; if (!lt9611->sleep) return; - lt9611_reset(lt9611); - regmap_write(lt9611->regmap, 0x80ee, 0x01); + regmap_multi_reg_write(lt9611->regmap, + reg_cfg, ARRAY_SIZE(reg_cfg)); lt9611->sleep = false; } @@ -876,33 +846,6 @@ lt9611_bridge_atomic_post_disable(struct drm_bridge *bridge, lt9611_sleep_setup(lt9611); } -static void lt9611_bridge_mode_set(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - const struct drm_display_mode *adj_mode) -{ - struct lt9611 *lt9611 = bridge_to_lt9611(bridge); - struct hdmi_avi_infoframe avi_frame; - int ret; - - lt9611_bridge_pre_enable(bridge); - - lt9611_mipi_input_digital(lt9611, mode); - lt9611_pll_setup(lt9611, mode); - lt9611_mipi_video_setup(lt9611, mode); - lt9611_pcr_setup(lt9611, mode); - - ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame, - <9611->connector, - mode); - if (!ret) - lt9611->vic = avi_frame.video_code; -} - -static enum drm_connector_status lt9611_bridge_detect(struct drm_bridge *bridge) -{ - return __lt9611_detect(bridge_to_lt9611(bridge)); -} - static struct edid *lt9611_bridge_get_edid(struct drm_bridge *bridge, struct drm_connector *connector) { @@ -948,11 +891,11 @@ lt9611_atomic_get_input_bus_fmts(struct drm_bridge *bridge, static const struct drm_bridge_funcs lt9611_bridge_funcs = { .attach = lt9611_bridge_attach, .mode_valid = lt9611_bridge_mode_valid, - .mode_set = lt9611_bridge_mode_set, .detect = lt9611_bridge_detect, .get_edid = lt9611_bridge_get_edid, .hpd_enable = lt9611_bridge_hpd_enable, + .atomic_pre_enable = lt9611_bridge_atomic_pre_enable, .atomic_enable = lt9611_bridge_atomic_enable, .atomic_disable = lt9611_bridge_atomic_disable, .atomic_post_disable = lt9611_bridge_atomic_post_disable, @@ -975,7 +918,7 @@ static int lt9611_parse_dt(struct device *dev, lt9611->ac_mode = of_property_read_bool(dev->of_node, "lt,ac-mode"); - return 0; + return drm_of_find_panel_or_bridge(dev->of_node, 2, -1, NULL, <9611->next_bridge); } static int lt9611_gpio_init(struct lt9611 *lt9611) @@ -1108,8 +1051,7 @@ static void lt9611_audio_exit(struct lt9611 *lt9611) } } -static int lt9611_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lt9611_probe(struct i2c_client *client) { struct lt9611 *lt9611; struct device *dev = &client->dev; @@ -1248,7 +1190,7 @@ static struct i2c_driver lt9611_driver = { .name = "lt9611", .of_match_table = lt9611_match_table, }, - .probe = lt9611_probe, + .probe_new = lt9611_probe, .remove = lt9611_remove, .id_table = lt9611_id, }; diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c index fa1ee6264d92..583daacf3705 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c @@ -844,8 +844,7 @@ static const struct attribute_group *lt9611uxc_attr_groups[] = { NULL, }; -static int lt9611uxc_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lt9611uxc_probe(struct i2c_client *client) { struct lt9611uxc *lt9611uxc; struct device *dev = &client->dev; @@ -1012,7 +1011,7 @@ static struct i2c_driver lt9611uxc_driver = { .of_match_table = lt9611uxc_match_table, .dev_groups = lt9611uxc_attr_groups, }, - .probe = lt9611uxc_probe, + .probe_new = lt9611uxc_probe, .remove = lt9611uxc_remove, .id_table = lt9611uxc_id, }; diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index 97359f807bfc..4fc494d9084b 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -336,8 +336,7 @@ static int ge_b850v3_register(void) "ge-b850v3-lvds-dp", ge_b850v3_lvds_ptr); } -static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c, - const struct i2c_device_id *id) +static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c) { struct device *dev = &stdp4028_i2c->dev; int ret; @@ -376,7 +375,7 @@ MODULE_DEVICE_TABLE(of, stdp4028_ge_b850v3_fw_match); static struct i2c_driver stdp4028_ge_b850v3_fw_driver = { .id_table = stdp4028_ge_b850v3_fw_i2c_table, - .probe = stdp4028_ge_b850v3_fw_probe, + .probe_new = stdp4028_ge_b850v3_fw_probe, .remove = stdp4028_ge_b850v3_fw_remove, .driver = { .name = "stdp4028-ge-b850v3-fw", @@ -384,8 +383,7 @@ static struct i2c_driver stdp4028_ge_b850v3_fw_driver = { }, }; -static int stdp2690_ge_b850v3_fw_probe(struct i2c_client *stdp2690_i2c, - const struct i2c_device_id *id) +static int stdp2690_ge_b850v3_fw_probe(struct i2c_client *stdp2690_i2c) { struct device *dev = &stdp2690_i2c->dev; int ret; @@ -424,7 +422,7 @@ MODULE_DEVICE_TABLE(of, stdp2690_ge_b850v3_fw_match); static struct i2c_driver stdp2690_ge_b850v3_fw_driver = { .id_table = stdp2690_ge_b850v3_fw_i2c_table, - .probe = stdp2690_ge_b850v3_fw_probe, + .probe_new = stdp2690_ge_b850v3_fw_probe, .remove = stdp2690_ge_b850v3_fw_remove, .driver = { .name = "stdp2690-ge-b850v3-fw", @@ -440,7 +438,11 @@ static int __init stdpxxxx_ge_b850v3_init(void) if (ret) return ret; - return i2c_add_driver(&stdp2690_ge_b850v3_fw_driver); + ret = i2c_add_driver(&stdp2690_ge_b850v3_fw_driver); + if (ret) + i2c_del_driver(&stdp4028_ge_b850v3_fw_driver); + + return ret; } module_init(stdpxxxx_ge_b850v3_init); diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index 0851101a8c72..cd292a2f894c 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -257,8 +257,7 @@ static const struct drm_bridge_funcs ptn3460_bridge_funcs = { .get_edid = ptn3460_get_edid, }; -static int ptn3460_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ptn3460_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ptn3460_bridge *ptn_bridge; @@ -336,7 +335,7 @@ MODULE_DEVICE_TABLE(of, ptn3460_match); static struct i2c_driver ptn3460_driver = { .id_table = ptn3460_i2c_table, - .probe = ptn3460_probe, + .probe_new = ptn3460_probe, .remove = ptn3460_remove, .driver = { .name = "nxp,ptn3460", diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 216af76d0042..e8aae3cdc73d 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -357,13 +357,16 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, return ERR_PTR(-ENOMEM); bridge = drm_panel_bridge_add_typed(panel, connector_type); - if (!IS_ERR(bridge)) { - *ptr = bridge; - devres_add(dev, ptr); - } else { + if (IS_ERR(bridge)) { devres_free(ptr); + return bridge; } + bridge->pre_enable_prev_first = panel->prepare_prev_first; + + *ptr = bridge; + devres_add(dev, ptr); + return bridge; } EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed); @@ -402,6 +405,8 @@ struct drm_bridge *drmm_panel_bridge_add(struct drm_device *drm, if (ret) return ERR_PTR(ret); + bridge->pre_enable_prev_first = panel->prepare_prev_first; + return bridge; } EXPORT_SYMBOL(drmm_panel_bridge_add); diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index 309de802863d..530ee6a19e7e 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -442,9 +442,9 @@ static const struct of_device_id ps8622_devices[] = { }; MODULE_DEVICE_TABLE(of, ps8622_devices); -static int ps8622_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ps8622_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); struct device *dev = &client->dev; struct ps8622_bridge *ps8622; struct drm_bridge *panel_bridge; @@ -538,7 +538,7 @@ MODULE_DEVICE_TABLE(i2c, ps8622_i2c_table); static struct i2c_driver ps8622_driver = { .id_table = ps8622_i2c_table, - .probe = ps8622_probe, + .probe_new = ps8622_probe, .remove = ps8622_remove, .driver = { .name = "ps8622", diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index 6a614e54b383..4b361d7d5e44 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -15,6 +15,7 @@ #include <drm/display/drm_dp_aux_bus.h> #include <drm/display/drm_dp_helper.h> +#include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_edid.h> #include <drm/drm_mipi_dsi.h> @@ -442,7 +443,8 @@ static const struct dev_pm_ops ps8640_pm_ops = { pm_runtime_force_resume) }; -static void ps8640_pre_enable(struct drm_bridge *bridge) +static void ps8640_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); struct regmap *map = ps_bridge->regmap[PAGE2_TOP_CNTL]; @@ -476,7 +478,8 @@ static void ps8640_pre_enable(struct drm_bridge *bridge) ps_bridge->pre_enabled = true; } -static void ps8640_post_disable(struct drm_bridge *bridge) +static void ps8640_atomic_post_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); @@ -554,7 +557,7 @@ static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge, * EDID, for this chip, we need to do a full poweron, otherwise it will * fail. */ - drm_bridge_chain_pre_enable(bridge); + drm_atomic_bridge_chain_pre_enable(bridge, connector->state->state); edid = drm_get_edid(connector, ps_bridge->page[PAGE0_DP_CNTL]->adapter); @@ -564,7 +567,7 @@ static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge, * before, return the chip to its original power state. */ if (poweroff) - drm_bridge_chain_post_disable(bridge); + drm_atomic_bridge_chain_post_disable(bridge, connector->state->state); return edid; } @@ -579,8 +582,11 @@ static const struct drm_bridge_funcs ps8640_bridge_funcs = { .attach = ps8640_bridge_attach, .detach = ps8640_bridge_detach, .get_edid = ps8640_bridge_get_edid, - .post_disable = ps8640_post_disable, - .pre_enable = ps8640_pre_enable, + .atomic_post_disable = ps8640_atomic_post_disable, + .atomic_pre_enable = ps8640_atomic_pre_enable, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, }; static int ps8640_bridge_get_dsi_resources(struct device *dev, struct ps8640 *ps_bridge) @@ -734,13 +740,13 @@ static int ps8640_probe(struct i2c_client *client) pm_runtime_enable(dev); /* * Powering on ps8640 takes ~300ms. To avoid wasting time on power - * cycling ps8640 too often, set autosuspend_delay to 1000ms to ensure + * cycling ps8640 too often, set autosuspend_delay to 2000ms to ensure * the bridge wouldn't suspend in between each _aux_transfer_msg() call * during EDID read (~20ms in my experiment) and in between the last * _aux_transfer_msg() call during EDID read and the _pre_enable() call * (~100ms in my experiment). */ - pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_set_autosuspend_delay(dev, 2000); pm_runtime_use_autosuspend(dev); pm_suspend_ignore_children(dev, true); ret = devm_add_action_or_reset(dev, ps8640_runtime_disable, dev); diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index 878fb7d3732b..ef66461e7f7c 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -171,7 +171,6 @@ struct sii902x { struct drm_connector connector; struct gpio_desc *reset_gpio; struct i2c_mux_core *i2cmux; - struct regulator_bulk_data supplies[2]; bool sink_is_hdmi; /* * Mutex protects audio and video functions from interfering @@ -240,12 +239,12 @@ static void sii902x_reset(struct sii902x *sii902x) if (!sii902x->reset_gpio) return; - gpiod_set_value(sii902x->reset_gpio, 1); + gpiod_set_value_cansleep(sii902x->reset_gpio, 1); /* The datasheet says treset-min = 100us. Make it 150us to be sure. */ usleep_range(150, 200); - gpiod_set_value(sii902x->reset_gpio, 0); + gpiod_set_value_cansleep(sii902x->reset_gpio, 0); } static enum drm_connector_status sii902x_detect(struct sii902x *sii902x) @@ -1066,12 +1065,12 @@ static int sii902x_init(struct sii902x *sii902x) return i2c_mux_add_adapter(sii902x->i2cmux, 0, 0, 0); } -static int sii902x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sii902x_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device_node *endpoint; struct sii902x *sii902x; + static const char * const supplies[] = {"iovcc", "cvcc12"}; int ret; ret = i2c_check_functionality(client->adapter, @@ -1117,32 +1116,17 @@ static int sii902x_probe(struct i2c_client *client, sii902x->next_bridge = of_drm_find_bridge(remote); of_node_put(remote); if (!sii902x->next_bridge) - return -EPROBE_DEFER; + return dev_err_probe(dev, -EPROBE_DEFER, + "Failed to find remote bridge\n"); } mutex_init(&sii902x->mutex); - sii902x->supplies[0].supply = "iovcc"; - sii902x->supplies[1].supply = "cvcc12"; - ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sii902x->supplies), - sii902x->supplies); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(supplies), supplies); if (ret < 0) - return ret; - - ret = regulator_bulk_enable(ARRAY_SIZE(sii902x->supplies), - sii902x->supplies); - if (ret < 0) { - dev_err_probe(dev, ret, "Failed to enable supplies"); - return ret; - } + return dev_err_probe(dev, ret, "Failed to enable supplies"); - ret = sii902x_init(sii902x); - if (ret < 0) { - regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies), - sii902x->supplies); - } - - return ret; + return sii902x_init(sii902x); } static void sii902x_remove(struct i2c_client *client) @@ -1152,8 +1136,6 @@ static void sii902x_remove(struct i2c_client *client) i2c_mux_del_adapters(sii902x->i2cmux); drm_bridge_remove(&sii902x->bridge); - regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies), - sii902x->supplies); } static const struct of_device_id sii902x_dt_ids[] = { @@ -1169,7 +1151,7 @@ static const struct i2c_device_id sii902x_i2c_ids[] = { MODULE_DEVICE_TABLE(i2c, sii902x_i2c_ids); static struct i2c_driver sii902x_driver = { - .probe = sii902x_probe, + .probe_new = sii902x_probe, .remove = sii902x_remove, .driver = { .name = "sii902x", diff --git a/drivers/gpu/drm/bridge/sii9234.c b/drivers/gpu/drm/bridge/sii9234.c index 5b3061d4b5c3..099b510ff285 100644 --- a/drivers/gpu/drm/bridge/sii9234.c +++ b/drivers/gpu/drm/bridge/sii9234.c @@ -886,8 +886,7 @@ static const struct drm_bridge_funcs sii9234_bridge_funcs = { .mode_valid = sii9234_mode_valid, }; -static int sii9234_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sii9234_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct sii9234 *ctx; @@ -961,7 +960,7 @@ static struct i2c_driver sii9234_driver = { .name = "sii9234", .of_match_table = sii9234_dt_match, }, - .probe = sii9234_probe, + .probe_new = sii9234_probe, .remove = sii9234_remove, .id_table = sii9234_id, }; diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 511982a1cedb..b96d03cd878d 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -2284,8 +2284,7 @@ static const struct drm_bridge_funcs sii8620_bridge_funcs = { .mode_valid = sii8620_mode_valid, }; -static int sii8620_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sii8620_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct sii8620 *ctx; @@ -2379,7 +2378,7 @@ static struct i2c_driver sii8620_driver = { .name = "sii8620", .of_match_table = of_match_ptr(sii8620_dt_match), }, - .probe = sii8620_probe, + .probe_new = sii8620_probe, .remove = sii8620_remove, .id_table = sii8620_id, }; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index a2f0860b20bb..d751820c6da6 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -193,6 +193,7 @@ static int snd_dw_hdmi_probe(struct platform_device *pdev) struct hdmi_codec_pdata pdata; struct platform_device *platform; + memset(&pdata, 0, sizeof(pdata)); pdata.ops = &dw_hdmi_i2s_ops; pdata.i2s = 1; pdata.max_i2s_channels = 8; diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index 53259c12d777..f85654f1b104 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -369,6 +369,7 @@ static int tc358764_probe(struct mipi_dsi_device *dsi) ctx->bridge.funcs = &tc358764_bridge_funcs; ctx->bridge.of_node = dev->of_node; + ctx->bridge.pre_enable_prev_first = true; drm_bridge_add(&ctx->bridge); diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 2a58eb271f70..6d16ec45ea61 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1264,10 +1264,10 @@ static int tc_dsi_rx_enable(struct tc_data *tc) u32 value; int ret; - regmap_write(tc->regmap, PPI_D0S_CLRSIPOCOUNT, 5); - regmap_write(tc->regmap, PPI_D1S_CLRSIPOCOUNT, 5); - regmap_write(tc->regmap, PPI_D2S_CLRSIPOCOUNT, 5); - regmap_write(tc->regmap, PPI_D3S_CLRSIPOCOUNT, 5); + regmap_write(tc->regmap, PPI_D0S_CLRSIPOCOUNT, 25); + regmap_write(tc->regmap, PPI_D1S_CLRSIPOCOUNT, 25); + regmap_write(tc->regmap, PPI_D2S_CLRSIPOCOUNT, 25); + regmap_write(tc->regmap, PPI_D3S_CLRSIPOCOUNT, 25); regmap_write(tc->regmap, PPI_D0S_ATMR, 0); regmap_write(tc->regmap, PPI_D1S_ATMR, 0); regmap_write(tc->regmap, PPI_TX_RX_TA, TTA_GET | TTA_SURE); @@ -2029,7 +2029,7 @@ static void tc_clk_disable(void *data) clk_disable_unprepare(refclk); } -static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int tc_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct tc_data *tc; @@ -2209,7 +2209,7 @@ static struct i2c_driver tc358767_driver = { .of_match_table = tc358767_of_ids, }, .id_table = tc358767_i2c_ids, - .probe = tc_probe, + .probe_new = tc_probe, .remove = tc_remove, }; module_i2c_driver(tc358767_driver); diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 4c4b77ce8aba..7c0cbe84611b 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -15,7 +15,6 @@ #include <linux/slab.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_drv.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> @@ -1018,8 +1017,7 @@ static int tc358768_get_regulators(struct tc358768_priv *priv) return ret; } -static int tc358768_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tc358768_i2c_probe(struct i2c_client *client) { struct tc358768_priv *priv; struct device *dev = &client->dev; @@ -1085,7 +1083,7 @@ static struct i2c_driver tc358768_driver = { .of_match_table = tc358768_of_ids, }, .id_table = tc358768_i2c_ids, - .probe = tc358768_i2c_probe, + .probe_new = tc358768_i2c_probe, .remove = tc358768_i2c_remove, }; module_i2c_driver(tc358768_driver); diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c index 3ceb0e9f9bdc..19316994ddd1 100644 --- a/drivers/gpu/drm/bridge/tc358775.c +++ b/drivers/gpu/drm/bridge/tc358775.c @@ -23,7 +23,6 @@ #include <drm/display/drm_dp_helper.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> @@ -637,7 +636,7 @@ static int tc_attach_host(struct tc_data *tc) return 0; } -static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int tc_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct tc_data *tc; @@ -729,7 +728,7 @@ static struct i2c_driver tc358775_driver = { .of_match_table = tc358775_of_ids, }, .id_table = tc358775_i2c_ids, - .probe = tc_probe, + .probe_new = tc_probe, .remove = tc_remove, }; module_i2c_driver(tc358775_driver); diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index 7ba9467fff12..91ecfbe45bf9 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -346,7 +346,7 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge, /* Deassert reset */ gpiod_set_value_cansleep(ctx->enable_gpio, 1); - usleep_range(1000, 1100); + usleep_range(10000, 11000); /* Get the LVDS format from the bridge state. */ bridge_state = drm_atomic_get_new_bridge_state(state, bridge); @@ -653,9 +653,9 @@ static int sn65dsi83_host_attach(struct sn65dsi83 *ctx) return 0; } -static int sn65dsi83_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sn65dsi83_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); struct device *dev = &client->dev; enum sn65dsi83_model model; struct sn65dsi83 *ctx; @@ -730,7 +730,7 @@ static const struct of_device_id sn65dsi83_match_table[] = { MODULE_DEVICE_TABLE(of, sn65dsi83_match_table); static struct i2c_driver sn65dsi83_driver = { - .probe = sn65dsi83_probe, + .probe_new = sn65dsi83_probe, .remove = sn65dsi83_remove, .id_table = sn65dsi83_id, .driver = { diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 05f8756d1aaf..1e26fa63845a 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -1852,8 +1852,7 @@ static int ti_sn65dsi86_parse_regulators(struct ti_sn65dsi86 *pdata) pdata->supplies); } -static int ti_sn65dsi86_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ti_sn65dsi86_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ti_sn65dsi86 *pdata; @@ -1952,7 +1951,7 @@ static struct i2c_driver ti_sn65dsi86_driver = { .of_match_table = ti_sn65dsi86_match_table, .pm = &ti_sn65dsi86_pm_ops, }, - .probe = ti_sn65dsi86_probe, + .probe_new = ti_sn65dsi86_probe, .id_table = ti_sn65dsi86_id, }; diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index b9635abbad16..6db69df0e18b 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -379,8 +379,7 @@ static struct platform_driver tfp410_platform_driver = { #if IS_ENABLED(CONFIG_I2C) /* There is currently no i2c functionality. */ -static int tfp410_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tfp410_i2c_probe(struct i2c_client *client) { int reg; @@ -411,7 +410,7 @@ static struct i2c_driver tfp410_i2c_driver = { .of_match_table = of_match_ptr(tfp410_match), }, .id_table = tfp410_i2c_ids, - .probe = tfp410_i2c_probe, + .probe_new = tfp410_i2c_probe, .remove = tfp410_i2c_remove, }; #endif /* IS_ENABLED(CONFIG_I2C) */ |