162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * TI SN65DSI83,84,85 driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Currently supported:
662306a36Sopenharmony_ci * - SN65DSI83
762306a36Sopenharmony_ci *   = 1x Single-link DSI ~ 1x Single-link LVDS
862306a36Sopenharmony_ci *   - Supported
962306a36Sopenharmony_ci *   - Single-link LVDS mode tested
1062306a36Sopenharmony_ci * - SN65DSI84
1162306a36Sopenharmony_ci *   = 1x Single-link DSI ~ 2x Single-link or 1x Dual-link LVDS
1262306a36Sopenharmony_ci *   - Supported
1362306a36Sopenharmony_ci *   - Dual-link LVDS mode tested
1462306a36Sopenharmony_ci *   - 2x Single-link LVDS mode unsupported
1562306a36Sopenharmony_ci *     (should be easy to add by someone who has the HW)
1662306a36Sopenharmony_ci * - SN65DSI85
1762306a36Sopenharmony_ci *   = 2x Single-link or 1x Dual-link DSI ~ 2x Single-link or 1x Dual-link LVDS
1862306a36Sopenharmony_ci *   - Unsupported
1962306a36Sopenharmony_ci *     (should be easy to add by someone who has the HW)
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * Copyright (C) 2021 Marek Vasut <marex@denx.de>
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Based on previous work of:
2462306a36Sopenharmony_ci * Valentin Raevsky <valentin@compulab.co.il>
2562306a36Sopenharmony_ci * Philippe Schenker <philippe.schenker@toradex.com>
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <linux/bits.h>
2962306a36Sopenharmony_ci#include <linux/clk.h>
3062306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
3162306a36Sopenharmony_ci#include <linux/i2c.h>
3262306a36Sopenharmony_ci#include <linux/media-bus-format.h>
3362306a36Sopenharmony_ci#include <linux/module.h>
3462306a36Sopenharmony_ci#include <linux/of.h>
3562306a36Sopenharmony_ci#include <linux/of_graph.h>
3662306a36Sopenharmony_ci#include <linux/regmap.h>
3762306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
4062306a36Sopenharmony_ci#include <drm/drm_bridge.h>
4162306a36Sopenharmony_ci#include <drm/drm_mipi_dsi.h>
4262306a36Sopenharmony_ci#include <drm/drm_of.h>
4362306a36Sopenharmony_ci#include <drm/drm_panel.h>
4462306a36Sopenharmony_ci#include <drm/drm_print.h>
4562306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* ID registers */
4862306a36Sopenharmony_ci#define REG_ID(n)				(0x00 + (n))
4962306a36Sopenharmony_ci/* Reset and clock registers */
5062306a36Sopenharmony_ci#define REG_RC_RESET				0x09
5162306a36Sopenharmony_ci#define  REG_RC_RESET_SOFT_RESET		BIT(0)
5262306a36Sopenharmony_ci#define REG_RC_LVDS_PLL				0x0a
5362306a36Sopenharmony_ci#define  REG_RC_LVDS_PLL_PLL_EN_STAT		BIT(7)
5462306a36Sopenharmony_ci#define  REG_RC_LVDS_PLL_LVDS_CLK_RANGE(n)	(((n) & 0x7) << 1)
5562306a36Sopenharmony_ci#define  REG_RC_LVDS_PLL_HS_CLK_SRC_DPHY	BIT(0)
5662306a36Sopenharmony_ci#define REG_RC_DSI_CLK				0x0b
5762306a36Sopenharmony_ci#define  REG_RC_DSI_CLK_DSI_CLK_DIVIDER(n)	(((n) & 0x1f) << 3)
5862306a36Sopenharmony_ci#define  REG_RC_DSI_CLK_REFCLK_MULTIPLIER(n)	((n) & 0x3)
5962306a36Sopenharmony_ci#define REG_RC_PLL_EN				0x0d
6062306a36Sopenharmony_ci#define  REG_RC_PLL_EN_PLL_EN			BIT(0)
6162306a36Sopenharmony_ci/* DSI registers */
6262306a36Sopenharmony_ci#define REG_DSI_LANE				0x10
6362306a36Sopenharmony_ci#define  REG_DSI_LANE_LEFT_RIGHT_PIXELS		BIT(7)	/* DSI85-only */
6462306a36Sopenharmony_ci#define  REG_DSI_LANE_DSI_CHANNEL_MODE_DUAL	0	/* DSI85-only */
6562306a36Sopenharmony_ci#define  REG_DSI_LANE_DSI_CHANNEL_MODE_2SINGLE	BIT(6)	/* DSI85-only */
6662306a36Sopenharmony_ci#define  REG_DSI_LANE_DSI_CHANNEL_MODE_SINGLE	BIT(5)
6762306a36Sopenharmony_ci#define  REG_DSI_LANE_CHA_DSI_LANES(n)		(((n) & 0x3) << 3)
6862306a36Sopenharmony_ci#define  REG_DSI_LANE_CHB_DSI_LANES(n)		(((n) & 0x3) << 1)
6962306a36Sopenharmony_ci#define  REG_DSI_LANE_SOT_ERR_TOL_DIS		BIT(0)
7062306a36Sopenharmony_ci#define REG_DSI_EQ				0x11
7162306a36Sopenharmony_ci#define  REG_DSI_EQ_CHA_DSI_DATA_EQ(n)		(((n) & 0x3) << 6)
7262306a36Sopenharmony_ci#define  REG_DSI_EQ_CHA_DSI_CLK_EQ(n)		(((n) & 0x3) << 2)
7362306a36Sopenharmony_ci#define REG_DSI_CLK				0x12
7462306a36Sopenharmony_ci#define  REG_DSI_CLK_CHA_DSI_CLK_RANGE(n)	((n) & 0xff)
7562306a36Sopenharmony_ci/* LVDS registers */
7662306a36Sopenharmony_ci#define REG_LVDS_FMT				0x18
7762306a36Sopenharmony_ci#define  REG_LVDS_FMT_DE_NEG_POLARITY		BIT(7)
7862306a36Sopenharmony_ci#define  REG_LVDS_FMT_HS_NEG_POLARITY		BIT(6)
7962306a36Sopenharmony_ci#define  REG_LVDS_FMT_VS_NEG_POLARITY		BIT(5)
8062306a36Sopenharmony_ci#define  REG_LVDS_FMT_LVDS_LINK_CFG		BIT(4)	/* 0:AB 1:A-only */
8162306a36Sopenharmony_ci#define  REG_LVDS_FMT_CHA_24BPP_MODE		BIT(3)
8262306a36Sopenharmony_ci#define  REG_LVDS_FMT_CHB_24BPP_MODE		BIT(2)
8362306a36Sopenharmony_ci#define  REG_LVDS_FMT_CHA_24BPP_FORMAT1		BIT(1)
8462306a36Sopenharmony_ci#define  REG_LVDS_FMT_CHB_24BPP_FORMAT1		BIT(0)
8562306a36Sopenharmony_ci#define REG_LVDS_VCOM				0x19
8662306a36Sopenharmony_ci#define  REG_LVDS_VCOM_CHA_LVDS_VOCM		BIT(6)
8762306a36Sopenharmony_ci#define  REG_LVDS_VCOM_CHB_LVDS_VOCM		BIT(4)
8862306a36Sopenharmony_ci#define  REG_LVDS_VCOM_CHA_LVDS_VOD_SWING(n)	(((n) & 0x3) << 2)
8962306a36Sopenharmony_ci#define  REG_LVDS_VCOM_CHB_LVDS_VOD_SWING(n)	((n) & 0x3)
9062306a36Sopenharmony_ci#define REG_LVDS_LANE				0x1a
9162306a36Sopenharmony_ci#define  REG_LVDS_LANE_EVEN_ODD_SWAP		BIT(6)
9262306a36Sopenharmony_ci#define  REG_LVDS_LANE_CHA_REVERSE_LVDS		BIT(5)
9362306a36Sopenharmony_ci#define  REG_LVDS_LANE_CHB_REVERSE_LVDS		BIT(4)
9462306a36Sopenharmony_ci#define  REG_LVDS_LANE_CHA_LVDS_TERM		BIT(1)
9562306a36Sopenharmony_ci#define  REG_LVDS_LANE_CHB_LVDS_TERM		BIT(0)
9662306a36Sopenharmony_ci#define REG_LVDS_CM				0x1b
9762306a36Sopenharmony_ci#define  REG_LVDS_CM_CHA_LVDS_CM_ADJUST(n)	(((n) & 0x3) << 4)
9862306a36Sopenharmony_ci#define  REG_LVDS_CM_CHB_LVDS_CM_ADJUST(n)	((n) & 0x3)
9962306a36Sopenharmony_ci/* Video registers */
10062306a36Sopenharmony_ci#define REG_VID_CHA_ACTIVE_LINE_LENGTH_LOW	0x20
10162306a36Sopenharmony_ci#define REG_VID_CHA_ACTIVE_LINE_LENGTH_HIGH	0x21
10262306a36Sopenharmony_ci#define REG_VID_CHA_VERTICAL_DISPLAY_SIZE_LOW	0x24
10362306a36Sopenharmony_ci#define REG_VID_CHA_VERTICAL_DISPLAY_SIZE_HIGH	0x25
10462306a36Sopenharmony_ci#define REG_VID_CHA_SYNC_DELAY_LOW		0x28
10562306a36Sopenharmony_ci#define REG_VID_CHA_SYNC_DELAY_HIGH		0x29
10662306a36Sopenharmony_ci#define REG_VID_CHA_HSYNC_PULSE_WIDTH_LOW	0x2c
10762306a36Sopenharmony_ci#define REG_VID_CHA_HSYNC_PULSE_WIDTH_HIGH	0x2d
10862306a36Sopenharmony_ci#define REG_VID_CHA_VSYNC_PULSE_WIDTH_LOW	0x30
10962306a36Sopenharmony_ci#define REG_VID_CHA_VSYNC_PULSE_WIDTH_HIGH	0x31
11062306a36Sopenharmony_ci#define REG_VID_CHA_HORIZONTAL_BACK_PORCH	0x34
11162306a36Sopenharmony_ci#define REG_VID_CHA_VERTICAL_BACK_PORCH		0x36
11262306a36Sopenharmony_ci#define REG_VID_CHA_HORIZONTAL_FRONT_PORCH	0x38
11362306a36Sopenharmony_ci#define REG_VID_CHA_VERTICAL_FRONT_PORCH	0x3a
11462306a36Sopenharmony_ci#define REG_VID_CHA_TEST_PATTERN		0x3c
11562306a36Sopenharmony_ci/* IRQ registers */
11662306a36Sopenharmony_ci#define REG_IRQ_GLOBAL				0xe0
11762306a36Sopenharmony_ci#define  REG_IRQ_GLOBAL_IRQ_EN			BIT(0)
11862306a36Sopenharmony_ci#define REG_IRQ_EN				0xe1
11962306a36Sopenharmony_ci#define  REG_IRQ_EN_CHA_SYNCH_ERR_EN		BIT(7)
12062306a36Sopenharmony_ci#define  REG_IRQ_EN_CHA_CRC_ERR_EN		BIT(6)
12162306a36Sopenharmony_ci#define  REG_IRQ_EN_CHA_UNC_ECC_ERR_EN		BIT(5)
12262306a36Sopenharmony_ci#define  REG_IRQ_EN_CHA_COR_ECC_ERR_EN		BIT(4)
12362306a36Sopenharmony_ci#define  REG_IRQ_EN_CHA_LLP_ERR_EN		BIT(3)
12462306a36Sopenharmony_ci#define  REG_IRQ_EN_CHA_SOT_BIT_ERR_EN		BIT(2)
12562306a36Sopenharmony_ci#define  REG_IRQ_EN_CHA_PLL_UNLOCK_EN		BIT(0)
12662306a36Sopenharmony_ci#define REG_IRQ_STAT				0xe5
12762306a36Sopenharmony_ci#define  REG_IRQ_STAT_CHA_SYNCH_ERR		BIT(7)
12862306a36Sopenharmony_ci#define  REG_IRQ_STAT_CHA_CRC_ERR		BIT(6)
12962306a36Sopenharmony_ci#define  REG_IRQ_STAT_CHA_UNC_ECC_ERR		BIT(5)
13062306a36Sopenharmony_ci#define  REG_IRQ_STAT_CHA_COR_ECC_ERR		BIT(4)
13162306a36Sopenharmony_ci#define  REG_IRQ_STAT_CHA_LLP_ERR		BIT(3)
13262306a36Sopenharmony_ci#define  REG_IRQ_STAT_CHA_SOT_BIT_ERR		BIT(2)
13362306a36Sopenharmony_ci#define  REG_IRQ_STAT_CHA_PLL_UNLOCK		BIT(0)
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cienum sn65dsi83_model {
13662306a36Sopenharmony_ci	MODEL_SN65DSI83,
13762306a36Sopenharmony_ci	MODEL_SN65DSI84,
13862306a36Sopenharmony_ci};
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistruct sn65dsi83 {
14162306a36Sopenharmony_ci	struct drm_bridge		bridge;
14262306a36Sopenharmony_ci	struct device			*dev;
14362306a36Sopenharmony_ci	struct regmap			*regmap;
14462306a36Sopenharmony_ci	struct mipi_dsi_device		*dsi;
14562306a36Sopenharmony_ci	struct drm_bridge		*panel_bridge;
14662306a36Sopenharmony_ci	struct gpio_desc		*enable_gpio;
14762306a36Sopenharmony_ci	struct regulator		*vcc;
14862306a36Sopenharmony_ci	bool				lvds_dual_link;
14962306a36Sopenharmony_ci	bool				lvds_dual_link_even_odd_swap;
15062306a36Sopenharmony_ci};
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic const struct regmap_range sn65dsi83_readable_ranges[] = {
15362306a36Sopenharmony_ci	regmap_reg_range(REG_ID(0), REG_ID(8)),
15462306a36Sopenharmony_ci	regmap_reg_range(REG_RC_LVDS_PLL, REG_RC_DSI_CLK),
15562306a36Sopenharmony_ci	regmap_reg_range(REG_RC_PLL_EN, REG_RC_PLL_EN),
15662306a36Sopenharmony_ci	regmap_reg_range(REG_DSI_LANE, REG_DSI_CLK),
15762306a36Sopenharmony_ci	regmap_reg_range(REG_LVDS_FMT, REG_LVDS_CM),
15862306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_ACTIVE_LINE_LENGTH_LOW,
15962306a36Sopenharmony_ci			 REG_VID_CHA_ACTIVE_LINE_LENGTH_HIGH),
16062306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_VERTICAL_DISPLAY_SIZE_LOW,
16162306a36Sopenharmony_ci			 REG_VID_CHA_VERTICAL_DISPLAY_SIZE_HIGH),
16262306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_SYNC_DELAY_LOW,
16362306a36Sopenharmony_ci			 REG_VID_CHA_SYNC_DELAY_HIGH),
16462306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_HSYNC_PULSE_WIDTH_LOW,
16562306a36Sopenharmony_ci			 REG_VID_CHA_HSYNC_PULSE_WIDTH_HIGH),
16662306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_VSYNC_PULSE_WIDTH_LOW,
16762306a36Sopenharmony_ci			 REG_VID_CHA_VSYNC_PULSE_WIDTH_HIGH),
16862306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_HORIZONTAL_BACK_PORCH,
16962306a36Sopenharmony_ci			 REG_VID_CHA_HORIZONTAL_BACK_PORCH),
17062306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_VERTICAL_BACK_PORCH,
17162306a36Sopenharmony_ci			 REG_VID_CHA_VERTICAL_BACK_PORCH),
17262306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_HORIZONTAL_FRONT_PORCH,
17362306a36Sopenharmony_ci			 REG_VID_CHA_HORIZONTAL_FRONT_PORCH),
17462306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_VERTICAL_FRONT_PORCH,
17562306a36Sopenharmony_ci			 REG_VID_CHA_VERTICAL_FRONT_PORCH),
17662306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_TEST_PATTERN, REG_VID_CHA_TEST_PATTERN),
17762306a36Sopenharmony_ci	regmap_reg_range(REG_IRQ_GLOBAL, REG_IRQ_EN),
17862306a36Sopenharmony_ci	regmap_reg_range(REG_IRQ_STAT, REG_IRQ_STAT),
17962306a36Sopenharmony_ci};
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic const struct regmap_access_table sn65dsi83_readable_table = {
18262306a36Sopenharmony_ci	.yes_ranges = sn65dsi83_readable_ranges,
18362306a36Sopenharmony_ci	.n_yes_ranges = ARRAY_SIZE(sn65dsi83_readable_ranges),
18462306a36Sopenharmony_ci};
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic const struct regmap_range sn65dsi83_writeable_ranges[] = {
18762306a36Sopenharmony_ci	regmap_reg_range(REG_RC_RESET, REG_RC_DSI_CLK),
18862306a36Sopenharmony_ci	regmap_reg_range(REG_RC_PLL_EN, REG_RC_PLL_EN),
18962306a36Sopenharmony_ci	regmap_reg_range(REG_DSI_LANE, REG_DSI_CLK),
19062306a36Sopenharmony_ci	regmap_reg_range(REG_LVDS_FMT, REG_LVDS_CM),
19162306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_ACTIVE_LINE_LENGTH_LOW,
19262306a36Sopenharmony_ci			 REG_VID_CHA_ACTIVE_LINE_LENGTH_HIGH),
19362306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_VERTICAL_DISPLAY_SIZE_LOW,
19462306a36Sopenharmony_ci			 REG_VID_CHA_VERTICAL_DISPLAY_SIZE_HIGH),
19562306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_SYNC_DELAY_LOW,
19662306a36Sopenharmony_ci			 REG_VID_CHA_SYNC_DELAY_HIGH),
19762306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_HSYNC_PULSE_WIDTH_LOW,
19862306a36Sopenharmony_ci			 REG_VID_CHA_HSYNC_PULSE_WIDTH_HIGH),
19962306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_VSYNC_PULSE_WIDTH_LOW,
20062306a36Sopenharmony_ci			 REG_VID_CHA_VSYNC_PULSE_WIDTH_HIGH),
20162306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_HORIZONTAL_BACK_PORCH,
20262306a36Sopenharmony_ci			 REG_VID_CHA_HORIZONTAL_BACK_PORCH),
20362306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_VERTICAL_BACK_PORCH,
20462306a36Sopenharmony_ci			 REG_VID_CHA_VERTICAL_BACK_PORCH),
20562306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_HORIZONTAL_FRONT_PORCH,
20662306a36Sopenharmony_ci			 REG_VID_CHA_HORIZONTAL_FRONT_PORCH),
20762306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_VERTICAL_FRONT_PORCH,
20862306a36Sopenharmony_ci			 REG_VID_CHA_VERTICAL_FRONT_PORCH),
20962306a36Sopenharmony_ci	regmap_reg_range(REG_VID_CHA_TEST_PATTERN, REG_VID_CHA_TEST_PATTERN),
21062306a36Sopenharmony_ci	regmap_reg_range(REG_IRQ_GLOBAL, REG_IRQ_EN),
21162306a36Sopenharmony_ci	regmap_reg_range(REG_IRQ_STAT, REG_IRQ_STAT),
21262306a36Sopenharmony_ci};
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic const struct regmap_access_table sn65dsi83_writeable_table = {
21562306a36Sopenharmony_ci	.yes_ranges = sn65dsi83_writeable_ranges,
21662306a36Sopenharmony_ci	.n_yes_ranges = ARRAY_SIZE(sn65dsi83_writeable_ranges),
21762306a36Sopenharmony_ci};
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic const struct regmap_range sn65dsi83_volatile_ranges[] = {
22062306a36Sopenharmony_ci	regmap_reg_range(REG_RC_RESET, REG_RC_RESET),
22162306a36Sopenharmony_ci	regmap_reg_range(REG_RC_LVDS_PLL, REG_RC_LVDS_PLL),
22262306a36Sopenharmony_ci	regmap_reg_range(REG_IRQ_STAT, REG_IRQ_STAT),
22362306a36Sopenharmony_ci};
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic const struct regmap_access_table sn65dsi83_volatile_table = {
22662306a36Sopenharmony_ci	.yes_ranges = sn65dsi83_volatile_ranges,
22762306a36Sopenharmony_ci	.n_yes_ranges = ARRAY_SIZE(sn65dsi83_volatile_ranges),
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic const struct regmap_config sn65dsi83_regmap_config = {
23162306a36Sopenharmony_ci	.reg_bits = 8,
23262306a36Sopenharmony_ci	.val_bits = 8,
23362306a36Sopenharmony_ci	.rd_table = &sn65dsi83_readable_table,
23462306a36Sopenharmony_ci	.wr_table = &sn65dsi83_writeable_table,
23562306a36Sopenharmony_ci	.volatile_table = &sn65dsi83_volatile_table,
23662306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
23762306a36Sopenharmony_ci	.max_register = REG_IRQ_STAT,
23862306a36Sopenharmony_ci};
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic struct sn65dsi83 *bridge_to_sn65dsi83(struct drm_bridge *bridge)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	return container_of(bridge, struct sn65dsi83, bridge);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic int sn65dsi83_attach(struct drm_bridge *bridge,
24662306a36Sopenharmony_ci			    enum drm_bridge_attach_flags flags)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	return drm_bridge_attach(bridge->encoder, ctx->panel_bridge,
25162306a36Sopenharmony_ci				 &ctx->bridge, flags);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic void sn65dsi83_detach(struct drm_bridge *bridge)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (!ctx->dsi)
25962306a36Sopenharmony_ci		return;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	ctx->dsi = NULL;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic u8 sn65dsi83_get_lvds_range(struct sn65dsi83 *ctx,
26562306a36Sopenharmony_ci				   const struct drm_display_mode *mode)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	/*
26862306a36Sopenharmony_ci	 * The encoding of the LVDS_CLK_RANGE is as follows:
26962306a36Sopenharmony_ci	 * 000 - 25 MHz <= LVDS_CLK < 37.5 MHz
27062306a36Sopenharmony_ci	 * 001 - 37.5 MHz <= LVDS_CLK < 62.5 MHz
27162306a36Sopenharmony_ci	 * 010 - 62.5 MHz <= LVDS_CLK < 87.5 MHz
27262306a36Sopenharmony_ci	 * 011 - 87.5 MHz <= LVDS_CLK < 112.5 MHz
27362306a36Sopenharmony_ci	 * 100 - 112.5 MHz <= LVDS_CLK < 137.5 MHz
27462306a36Sopenharmony_ci	 * 101 - 137.5 MHz <= LVDS_CLK <= 154 MHz
27562306a36Sopenharmony_ci	 * which is a range of 12.5MHz..162.5MHz in 50MHz steps, except that
27662306a36Sopenharmony_ci	 * the ends of the ranges are clamped to the supported range. Since
27762306a36Sopenharmony_ci	 * sn65dsi83_mode_valid() already filters the valid modes and limits
27862306a36Sopenharmony_ci	 * the clock to 25..154 MHz, the range calculation can be simplified
27962306a36Sopenharmony_ci	 * as follows:
28062306a36Sopenharmony_ci	 */
28162306a36Sopenharmony_ci	int mode_clock = mode->clock;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (ctx->lvds_dual_link)
28462306a36Sopenharmony_ci		mode_clock /= 2;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	return (mode_clock - 12500) / 25000;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic u8 sn65dsi83_get_dsi_range(struct sn65dsi83 *ctx,
29062306a36Sopenharmony_ci				  const struct drm_display_mode *mode)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	/*
29362306a36Sopenharmony_ci	 * The encoding of the CHA_DSI_CLK_RANGE is as follows:
29462306a36Sopenharmony_ci	 * 0x00 through 0x07 - Reserved
29562306a36Sopenharmony_ci	 * 0x08 - 40 <= DSI_CLK < 45 MHz
29662306a36Sopenharmony_ci	 * 0x09 - 45 <= DSI_CLK < 50 MHz
29762306a36Sopenharmony_ci	 * ...
29862306a36Sopenharmony_ci	 * 0x63 - 495 <= DSI_CLK < 500 MHz
29962306a36Sopenharmony_ci	 * 0x64 - 500 MHz
30062306a36Sopenharmony_ci	 * 0x65 through 0xFF - Reserved
30162306a36Sopenharmony_ci	 * which is DSI clock in 5 MHz steps, clamped to 40..500 MHz.
30262306a36Sopenharmony_ci	 * The DSI clock are calculated as:
30362306a36Sopenharmony_ci	 *  DSI_CLK = mode clock * bpp / dsi_data_lanes / 2
30462306a36Sopenharmony_ci	 * the 2 is there because the bus is DDR.
30562306a36Sopenharmony_ci	 */
30662306a36Sopenharmony_ci	return DIV_ROUND_UP(clamp((unsigned int)mode->clock *
30762306a36Sopenharmony_ci			    mipi_dsi_pixel_format_to_bpp(ctx->dsi->format) /
30862306a36Sopenharmony_ci			    ctx->dsi->lanes / 2, 40000U, 500000U), 5000U);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic u8 sn65dsi83_get_dsi_div(struct sn65dsi83 *ctx)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	/* The divider is (DSI_CLK / LVDS_CLK) - 1, which really is: */
31462306a36Sopenharmony_ci	unsigned int dsi_div = mipi_dsi_pixel_format_to_bpp(ctx->dsi->format);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	dsi_div /= ctx->dsi->lanes;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (!ctx->lvds_dual_link)
31962306a36Sopenharmony_ci		dsi_div /= 2;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	return dsi_div - 1;
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
32562306a36Sopenharmony_ci					struct drm_bridge_state *old_bridge_state)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
32862306a36Sopenharmony_ci	struct drm_atomic_state *state = old_bridge_state->base.state;
32962306a36Sopenharmony_ci	const struct drm_bridge_state *bridge_state;
33062306a36Sopenharmony_ci	const struct drm_crtc_state *crtc_state;
33162306a36Sopenharmony_ci	const struct drm_display_mode *mode;
33262306a36Sopenharmony_ci	struct drm_connector *connector;
33362306a36Sopenharmony_ci	struct drm_crtc *crtc;
33462306a36Sopenharmony_ci	bool lvds_format_24bpp;
33562306a36Sopenharmony_ci	bool lvds_format_jeida;
33662306a36Sopenharmony_ci	unsigned int pval;
33762306a36Sopenharmony_ci	__le16 le16val;
33862306a36Sopenharmony_ci	u16 val;
33962306a36Sopenharmony_ci	int ret;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	ret = regulator_enable(ctx->vcc);
34262306a36Sopenharmony_ci	if (ret) {
34362306a36Sopenharmony_ci		dev_err(ctx->dev, "Failed to enable vcc: %d\n", ret);
34462306a36Sopenharmony_ci		return;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	/* Deassert reset */
34862306a36Sopenharmony_ci	gpiod_set_value_cansleep(ctx->enable_gpio, 1);
34962306a36Sopenharmony_ci	usleep_range(10000, 11000);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* Get the LVDS format from the bridge state. */
35262306a36Sopenharmony_ci	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	switch (bridge_state->output_bus_cfg.format) {
35562306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
35662306a36Sopenharmony_ci		lvds_format_24bpp = false;
35762306a36Sopenharmony_ci		lvds_format_jeida = true;
35862306a36Sopenharmony_ci		break;
35962306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
36062306a36Sopenharmony_ci		lvds_format_24bpp = true;
36162306a36Sopenharmony_ci		lvds_format_jeida = true;
36262306a36Sopenharmony_ci		break;
36362306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
36462306a36Sopenharmony_ci		lvds_format_24bpp = true;
36562306a36Sopenharmony_ci		lvds_format_jeida = false;
36662306a36Sopenharmony_ci		break;
36762306a36Sopenharmony_ci	default:
36862306a36Sopenharmony_ci		/*
36962306a36Sopenharmony_ci		 * Some bridges still don't set the correct
37062306a36Sopenharmony_ci		 * LVDS bus pixel format, use SPWG24 default
37162306a36Sopenharmony_ci		 * format until those are fixed.
37262306a36Sopenharmony_ci		 */
37362306a36Sopenharmony_ci		lvds_format_24bpp = true;
37462306a36Sopenharmony_ci		lvds_format_jeida = false;
37562306a36Sopenharmony_ci		dev_warn(ctx->dev,
37662306a36Sopenharmony_ci			 "Unsupported LVDS bus format 0x%04x, please check output bridge driver. Falling back to SPWG24.\n",
37762306a36Sopenharmony_ci			 bridge_state->output_bus_cfg.format);
37862306a36Sopenharmony_ci		break;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	/*
38262306a36Sopenharmony_ci	 * Retrieve the CRTC adjusted mode. This requires a little dance to go
38362306a36Sopenharmony_ci	 * from the bridge to the encoder, to the connector and to the CRTC.
38462306a36Sopenharmony_ci	 */
38562306a36Sopenharmony_ci	connector = drm_atomic_get_new_connector_for_encoder(state,
38662306a36Sopenharmony_ci							     bridge->encoder);
38762306a36Sopenharmony_ci	crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
38862306a36Sopenharmony_ci	crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
38962306a36Sopenharmony_ci	mode = &crtc_state->adjusted_mode;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* Clear reset, disable PLL */
39262306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_RC_RESET, 0x00);
39362306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_RC_PLL_EN, 0x00);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/* Reference clock derived from DSI link clock. */
39662306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_RC_LVDS_PLL,
39762306a36Sopenharmony_ci		     REG_RC_LVDS_PLL_LVDS_CLK_RANGE(sn65dsi83_get_lvds_range(ctx, mode)) |
39862306a36Sopenharmony_ci		     REG_RC_LVDS_PLL_HS_CLK_SRC_DPHY);
39962306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_DSI_CLK,
40062306a36Sopenharmony_ci		     REG_DSI_CLK_CHA_DSI_CLK_RANGE(sn65dsi83_get_dsi_range(ctx, mode)));
40162306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_RC_DSI_CLK,
40262306a36Sopenharmony_ci		     REG_RC_DSI_CLK_DSI_CLK_DIVIDER(sn65dsi83_get_dsi_div(ctx)));
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/* Set number of DSI lanes and LVDS link config. */
40562306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_DSI_LANE,
40662306a36Sopenharmony_ci		     REG_DSI_LANE_DSI_CHANNEL_MODE_SINGLE |
40762306a36Sopenharmony_ci		     REG_DSI_LANE_CHA_DSI_LANES(~(ctx->dsi->lanes - 1)) |
40862306a36Sopenharmony_ci		     /* CHB is DSI85-only, set to default on DSI83/DSI84 */
40962306a36Sopenharmony_ci		     REG_DSI_LANE_CHB_DSI_LANES(3));
41062306a36Sopenharmony_ci	/* No equalization. */
41162306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_DSI_EQ, 0x00);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* Set up sync signal polarity. */
41462306a36Sopenharmony_ci	val = (mode->flags & DRM_MODE_FLAG_NHSYNC ?
41562306a36Sopenharmony_ci	       REG_LVDS_FMT_HS_NEG_POLARITY : 0) |
41662306a36Sopenharmony_ci	      (mode->flags & DRM_MODE_FLAG_NVSYNC ?
41762306a36Sopenharmony_ci	       REG_LVDS_FMT_VS_NEG_POLARITY : 0);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* Set up bits-per-pixel, 18bpp or 24bpp. */
42062306a36Sopenharmony_ci	if (lvds_format_24bpp) {
42162306a36Sopenharmony_ci		val |= REG_LVDS_FMT_CHA_24BPP_MODE;
42262306a36Sopenharmony_ci		if (ctx->lvds_dual_link)
42362306a36Sopenharmony_ci			val |= REG_LVDS_FMT_CHB_24BPP_MODE;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* Set up LVDS format, JEIDA/Format 1 or SPWG/Format 2 */
42762306a36Sopenharmony_ci	if (lvds_format_jeida) {
42862306a36Sopenharmony_ci		val |= REG_LVDS_FMT_CHA_24BPP_FORMAT1;
42962306a36Sopenharmony_ci		if (ctx->lvds_dual_link)
43062306a36Sopenharmony_ci			val |= REG_LVDS_FMT_CHB_24BPP_FORMAT1;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	/* Set up LVDS output config (DSI84,DSI85) */
43462306a36Sopenharmony_ci	if (!ctx->lvds_dual_link)
43562306a36Sopenharmony_ci		val |= REG_LVDS_FMT_LVDS_LINK_CFG;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_LVDS_FMT, val);
43862306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_LVDS_VCOM, 0x05);
43962306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_LVDS_LANE,
44062306a36Sopenharmony_ci		     (ctx->lvds_dual_link_even_odd_swap ?
44162306a36Sopenharmony_ci		      REG_LVDS_LANE_EVEN_ODD_SWAP : 0) |
44262306a36Sopenharmony_ci		     REG_LVDS_LANE_CHA_LVDS_TERM |
44362306a36Sopenharmony_ci		     REG_LVDS_LANE_CHB_LVDS_TERM);
44462306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_LVDS_CM, 0x00);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	le16val = cpu_to_le16(mode->hdisplay);
44762306a36Sopenharmony_ci	regmap_bulk_write(ctx->regmap, REG_VID_CHA_ACTIVE_LINE_LENGTH_LOW,
44862306a36Sopenharmony_ci			  &le16val, 2);
44962306a36Sopenharmony_ci	le16val = cpu_to_le16(mode->vdisplay);
45062306a36Sopenharmony_ci	regmap_bulk_write(ctx->regmap, REG_VID_CHA_VERTICAL_DISPLAY_SIZE_LOW,
45162306a36Sopenharmony_ci			  &le16val, 2);
45262306a36Sopenharmony_ci	/* 32 + 1 pixel clock to ensure proper operation */
45362306a36Sopenharmony_ci	le16val = cpu_to_le16(32 + 1);
45462306a36Sopenharmony_ci	regmap_bulk_write(ctx->regmap, REG_VID_CHA_SYNC_DELAY_LOW, &le16val, 2);
45562306a36Sopenharmony_ci	le16val = cpu_to_le16(mode->hsync_end - mode->hsync_start);
45662306a36Sopenharmony_ci	regmap_bulk_write(ctx->regmap, REG_VID_CHA_HSYNC_PULSE_WIDTH_LOW,
45762306a36Sopenharmony_ci			  &le16val, 2);
45862306a36Sopenharmony_ci	le16val = cpu_to_le16(mode->vsync_end - mode->vsync_start);
45962306a36Sopenharmony_ci	regmap_bulk_write(ctx->regmap, REG_VID_CHA_VSYNC_PULSE_WIDTH_LOW,
46062306a36Sopenharmony_ci			  &le16val, 2);
46162306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_VID_CHA_HORIZONTAL_BACK_PORCH,
46262306a36Sopenharmony_ci		     mode->htotal - mode->hsync_end);
46362306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_VID_CHA_VERTICAL_BACK_PORCH,
46462306a36Sopenharmony_ci		     mode->vtotal - mode->vsync_end);
46562306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_VID_CHA_HORIZONTAL_FRONT_PORCH,
46662306a36Sopenharmony_ci		     mode->hsync_start - mode->hdisplay);
46762306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_VID_CHA_VERTICAL_FRONT_PORCH,
46862306a36Sopenharmony_ci		     mode->vsync_start - mode->vdisplay);
46962306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_VID_CHA_TEST_PATTERN, 0x00);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* Enable PLL */
47262306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_RC_PLL_EN, REG_RC_PLL_EN_PLL_EN);
47362306a36Sopenharmony_ci	usleep_range(3000, 4000);
47462306a36Sopenharmony_ci	ret = regmap_read_poll_timeout(ctx->regmap, REG_RC_LVDS_PLL, pval,
47562306a36Sopenharmony_ci				       pval & REG_RC_LVDS_PLL_PLL_EN_STAT,
47662306a36Sopenharmony_ci				       1000, 100000);
47762306a36Sopenharmony_ci	if (ret) {
47862306a36Sopenharmony_ci		dev_err(ctx->dev, "failed to lock PLL, ret=%i\n", ret);
47962306a36Sopenharmony_ci		/* On failure, disable PLL again and exit. */
48062306a36Sopenharmony_ci		regmap_write(ctx->regmap, REG_RC_PLL_EN, 0x00);
48162306a36Sopenharmony_ci		regulator_disable(ctx->vcc);
48262306a36Sopenharmony_ci		return;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	/* Trigger reset after CSR register update. */
48662306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_RC_RESET, REG_RC_RESET_SOFT_RESET);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	/* Wait for 10ms after soft reset as specified in datasheet */
48962306a36Sopenharmony_ci	usleep_range(10000, 12000);
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic void sn65dsi83_atomic_enable(struct drm_bridge *bridge,
49362306a36Sopenharmony_ci				    struct drm_bridge_state *old_bridge_state)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
49662306a36Sopenharmony_ci	unsigned int pval;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	/* Clear all errors that got asserted during initialization. */
49962306a36Sopenharmony_ci	regmap_read(ctx->regmap, REG_IRQ_STAT, &pval);
50062306a36Sopenharmony_ci	regmap_write(ctx->regmap, REG_IRQ_STAT, pval);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* Wait for 1ms and check for errors in status register */
50362306a36Sopenharmony_ci	usleep_range(1000, 1100);
50462306a36Sopenharmony_ci	regmap_read(ctx->regmap, REG_IRQ_STAT, &pval);
50562306a36Sopenharmony_ci	if (pval)
50662306a36Sopenharmony_ci		dev_err(ctx->dev, "Unexpected link status 0x%02x\n", pval);
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic void sn65dsi83_atomic_disable(struct drm_bridge *bridge,
51062306a36Sopenharmony_ci				     struct drm_bridge_state *old_bridge_state)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
51362306a36Sopenharmony_ci	int ret;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */
51662306a36Sopenharmony_ci	gpiod_set_value_cansleep(ctx->enable_gpio, 0);
51762306a36Sopenharmony_ci	usleep_range(10000, 11000);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	ret = regulator_disable(ctx->vcc);
52062306a36Sopenharmony_ci	if (ret)
52162306a36Sopenharmony_ci		dev_err(ctx->dev, "Failed to disable vcc: %d\n", ret);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	regcache_mark_dirty(ctx->regmap);
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic enum drm_mode_status
52762306a36Sopenharmony_cisn65dsi83_mode_valid(struct drm_bridge *bridge,
52862306a36Sopenharmony_ci		     const struct drm_display_info *info,
52962306a36Sopenharmony_ci		     const struct drm_display_mode *mode)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	/* LVDS output clock range 25..154 MHz */
53262306a36Sopenharmony_ci	if (mode->clock < 25000)
53362306a36Sopenharmony_ci		return MODE_CLOCK_LOW;
53462306a36Sopenharmony_ci	if (mode->clock > 154000)
53562306a36Sopenharmony_ci		return MODE_CLOCK_HIGH;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	return MODE_OK;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci#define MAX_INPUT_SEL_FORMATS	1
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cistatic u32 *
54362306a36Sopenharmony_cisn65dsi83_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
54462306a36Sopenharmony_ci				    struct drm_bridge_state *bridge_state,
54562306a36Sopenharmony_ci				    struct drm_crtc_state *crtc_state,
54662306a36Sopenharmony_ci				    struct drm_connector_state *conn_state,
54762306a36Sopenharmony_ci				    u32 output_fmt,
54862306a36Sopenharmony_ci				    unsigned int *num_input_fmts)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	u32 *input_fmts;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	*num_input_fmts = 0;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
55562306a36Sopenharmony_ci			     GFP_KERNEL);
55662306a36Sopenharmony_ci	if (!input_fmts)
55762306a36Sopenharmony_ci		return NULL;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/* This is the DSI-end bus format */
56062306a36Sopenharmony_ci	input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
56162306a36Sopenharmony_ci	*num_input_fmts = 1;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	return input_fmts;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic const struct drm_bridge_funcs sn65dsi83_funcs = {
56762306a36Sopenharmony_ci	.attach			= sn65dsi83_attach,
56862306a36Sopenharmony_ci	.detach			= sn65dsi83_detach,
56962306a36Sopenharmony_ci	.atomic_enable		= sn65dsi83_atomic_enable,
57062306a36Sopenharmony_ci	.atomic_pre_enable	= sn65dsi83_atomic_pre_enable,
57162306a36Sopenharmony_ci	.atomic_disable		= sn65dsi83_atomic_disable,
57262306a36Sopenharmony_ci	.mode_valid		= sn65dsi83_mode_valid,
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
57562306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
57662306a36Sopenharmony_ci	.atomic_reset = drm_atomic_helper_bridge_reset,
57762306a36Sopenharmony_ci	.atomic_get_input_bus_fmts = sn65dsi83_atomic_get_input_bus_fmts,
57862306a36Sopenharmony_ci};
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct drm_bridge *panel_bridge;
58362306a36Sopenharmony_ci	struct device *dev = ctx->dev;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	ctx->lvds_dual_link = false;
58662306a36Sopenharmony_ci	ctx->lvds_dual_link_even_odd_swap = false;
58762306a36Sopenharmony_ci	if (model != MODEL_SN65DSI83) {
58862306a36Sopenharmony_ci		struct device_node *port2, *port3;
58962306a36Sopenharmony_ci		int dual_link;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		port2 = of_graph_get_port_by_id(dev->of_node, 2);
59262306a36Sopenharmony_ci		port3 = of_graph_get_port_by_id(dev->of_node, 3);
59362306a36Sopenharmony_ci		dual_link = drm_of_lvds_get_dual_link_pixel_order(port2, port3);
59462306a36Sopenharmony_ci		of_node_put(port2);
59562306a36Sopenharmony_ci		of_node_put(port3);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		if (dual_link == DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS) {
59862306a36Sopenharmony_ci			ctx->lvds_dual_link = true;
59962306a36Sopenharmony_ci			/* Odd pixels to LVDS Channel A, even pixels to B */
60062306a36Sopenharmony_ci			ctx->lvds_dual_link_even_odd_swap = false;
60162306a36Sopenharmony_ci		} else if (dual_link == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS) {
60262306a36Sopenharmony_ci			ctx->lvds_dual_link = true;
60362306a36Sopenharmony_ci			/* Even pixels to LVDS Channel A, odd pixels to B */
60462306a36Sopenharmony_ci			ctx->lvds_dual_link_even_odd_swap = true;
60562306a36Sopenharmony_ci		}
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 2, 0);
60962306a36Sopenharmony_ci	if (IS_ERR(panel_bridge))
61062306a36Sopenharmony_ci		return PTR_ERR(panel_bridge);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	ctx->panel_bridge = panel_bridge;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	ctx->vcc = devm_regulator_get(dev, "vcc");
61562306a36Sopenharmony_ci	if (IS_ERR(ctx->vcc))
61662306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(ctx->vcc),
61762306a36Sopenharmony_ci				     "Failed to get supply 'vcc'\n");
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	return 0;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic int sn65dsi83_host_attach(struct sn65dsi83 *ctx)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct device *dev = ctx->dev;
62562306a36Sopenharmony_ci	struct device_node *host_node;
62662306a36Sopenharmony_ci	struct device_node *endpoint;
62762306a36Sopenharmony_ci	struct mipi_dsi_device *dsi;
62862306a36Sopenharmony_ci	struct mipi_dsi_host *host;
62962306a36Sopenharmony_ci	const struct mipi_dsi_device_info info = {
63062306a36Sopenharmony_ci		.type = "sn65dsi83",
63162306a36Sopenharmony_ci		.channel = 0,
63262306a36Sopenharmony_ci		.node = NULL,
63362306a36Sopenharmony_ci	};
63462306a36Sopenharmony_ci	int dsi_lanes, ret;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
63762306a36Sopenharmony_ci	dsi_lanes = drm_of_get_data_lanes_count(endpoint, 1, 4);
63862306a36Sopenharmony_ci	host_node = of_graph_get_remote_port_parent(endpoint);
63962306a36Sopenharmony_ci	host = of_find_mipi_dsi_host_by_node(host_node);
64062306a36Sopenharmony_ci	of_node_put(host_node);
64162306a36Sopenharmony_ci	of_node_put(endpoint);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	if (!host)
64462306a36Sopenharmony_ci		return -EPROBE_DEFER;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	if (dsi_lanes < 0)
64762306a36Sopenharmony_ci		return dsi_lanes;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
65062306a36Sopenharmony_ci	if (IS_ERR(dsi))
65162306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(dsi),
65262306a36Sopenharmony_ci				     "failed to create dsi device\n");
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	ctx->dsi = dsi;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	dsi->lanes = dsi_lanes;
65762306a36Sopenharmony_ci	dsi->format = MIPI_DSI_FMT_RGB888;
65862306a36Sopenharmony_ci	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
65962306a36Sopenharmony_ci			  MIPI_DSI_MODE_VIDEO_NO_HFP | MIPI_DSI_MODE_VIDEO_NO_HBP |
66062306a36Sopenharmony_ci			  MIPI_DSI_MODE_VIDEO_NO_HSA | MIPI_DSI_MODE_NO_EOT_PACKET;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	ret = devm_mipi_dsi_attach(dev, dsi);
66362306a36Sopenharmony_ci	if (ret < 0) {
66462306a36Sopenharmony_ci		dev_err(dev, "failed to attach dsi to host: %d\n", ret);
66562306a36Sopenharmony_ci		return ret;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	return 0;
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic int sn65dsi83_probe(struct i2c_client *client)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	const struct i2c_device_id *id = i2c_client_get_device_id(client);
67462306a36Sopenharmony_ci	struct device *dev = &client->dev;
67562306a36Sopenharmony_ci	enum sn65dsi83_model model;
67662306a36Sopenharmony_ci	struct sn65dsi83 *ctx;
67762306a36Sopenharmony_ci	int ret;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
68062306a36Sopenharmony_ci	if (!ctx)
68162306a36Sopenharmony_ci		return -ENOMEM;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	ctx->dev = dev;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (dev->of_node) {
68662306a36Sopenharmony_ci		model = (enum sn65dsi83_model)(uintptr_t)
68762306a36Sopenharmony_ci			of_device_get_match_data(dev);
68862306a36Sopenharmony_ci	} else {
68962306a36Sopenharmony_ci		model = id->driver_data;
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */
69362306a36Sopenharmony_ci	ctx->enable_gpio = devm_gpiod_get_optional(ctx->dev, "enable",
69462306a36Sopenharmony_ci						   GPIOD_OUT_LOW);
69562306a36Sopenharmony_ci	if (IS_ERR(ctx->enable_gpio))
69662306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(ctx->enable_gpio), "failed to get enable GPIO\n");
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	usleep_range(10000, 11000);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	ret = sn65dsi83_parse_dt(ctx, model);
70162306a36Sopenharmony_ci	if (ret)
70262306a36Sopenharmony_ci		return ret;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	ctx->regmap = devm_regmap_init_i2c(client, &sn65dsi83_regmap_config);
70562306a36Sopenharmony_ci	if (IS_ERR(ctx->regmap))
70662306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(ctx->regmap), "failed to get regmap\n");
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	dev_set_drvdata(dev, ctx);
70962306a36Sopenharmony_ci	i2c_set_clientdata(client, ctx);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	ctx->bridge.funcs = &sn65dsi83_funcs;
71262306a36Sopenharmony_ci	ctx->bridge.of_node = dev->of_node;
71362306a36Sopenharmony_ci	ctx->bridge.pre_enable_prev_first = true;
71462306a36Sopenharmony_ci	drm_bridge_add(&ctx->bridge);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	ret = sn65dsi83_host_attach(ctx);
71762306a36Sopenharmony_ci	if (ret) {
71862306a36Sopenharmony_ci		dev_err_probe(dev, ret, "failed to attach DSI host\n");
71962306a36Sopenharmony_ci		goto err_remove_bridge;
72062306a36Sopenharmony_ci	}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	return 0;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cierr_remove_bridge:
72562306a36Sopenharmony_ci	drm_bridge_remove(&ctx->bridge);
72662306a36Sopenharmony_ci	return ret;
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_cistatic void sn65dsi83_remove(struct i2c_client *client)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	struct sn65dsi83 *ctx = i2c_get_clientdata(client);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	drm_bridge_remove(&ctx->bridge);
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic struct i2c_device_id sn65dsi83_id[] = {
73762306a36Sopenharmony_ci	{ "ti,sn65dsi83", MODEL_SN65DSI83 },
73862306a36Sopenharmony_ci	{ "ti,sn65dsi84", MODEL_SN65DSI84 },
73962306a36Sopenharmony_ci	{},
74062306a36Sopenharmony_ci};
74162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, sn65dsi83_id);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_cistatic const struct of_device_id sn65dsi83_match_table[] = {
74462306a36Sopenharmony_ci	{ .compatible = "ti,sn65dsi83", .data = (void *)MODEL_SN65DSI83 },
74562306a36Sopenharmony_ci	{ .compatible = "ti,sn65dsi84", .data = (void *)MODEL_SN65DSI84 },
74662306a36Sopenharmony_ci	{},
74762306a36Sopenharmony_ci};
74862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sn65dsi83_match_table);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic struct i2c_driver sn65dsi83_driver = {
75162306a36Sopenharmony_ci	.probe = sn65dsi83_probe,
75262306a36Sopenharmony_ci	.remove = sn65dsi83_remove,
75362306a36Sopenharmony_ci	.id_table = sn65dsi83_id,
75462306a36Sopenharmony_ci	.driver = {
75562306a36Sopenharmony_ci		.name = "sn65dsi83",
75662306a36Sopenharmony_ci		.of_match_table = sn65dsi83_match_table,
75762306a36Sopenharmony_ci	},
75862306a36Sopenharmony_ci};
75962306a36Sopenharmony_cimodule_i2c_driver(sn65dsi83_driver);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ciMODULE_AUTHOR("Marek Vasut <marex@denx.de>");
76262306a36Sopenharmony_ciMODULE_DESCRIPTION("TI SN65DSI83 DSI to LVDS bridge driver");
76362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
764