162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2013 NVIDIA Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/clk.h>
762306a36Sopenharmony_ci#include <linux/debugfs.h>
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/host1x.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/of_platform.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1562306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1662306a36Sopenharmony_ci#include <linux/reset.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <video/mipi_display.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
2162306a36Sopenharmony_ci#include <drm/drm_debugfs.h>
2262306a36Sopenharmony_ci#include <drm/drm_file.h>
2362306a36Sopenharmony_ci#include <drm/drm_mipi_dsi.h>
2462306a36Sopenharmony_ci#include <drm/drm_panel.h>
2562306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "dc.h"
2862306a36Sopenharmony_ci#include "drm.h"
2962306a36Sopenharmony_ci#include "dsi.h"
3062306a36Sopenharmony_ci#include "mipi-phy.h"
3162306a36Sopenharmony_ci#include "trace.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct tegra_dsi_state {
3462306a36Sopenharmony_ci	struct drm_connector_state base;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	struct mipi_dphy_timing timing;
3762306a36Sopenharmony_ci	unsigned long period;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	unsigned int vrefresh;
4062306a36Sopenharmony_ci	unsigned int lanes;
4162306a36Sopenharmony_ci	unsigned long pclk;
4262306a36Sopenharmony_ci	unsigned long bclk;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	enum tegra_dsi_format format;
4562306a36Sopenharmony_ci	unsigned int mul;
4662306a36Sopenharmony_ci	unsigned int div;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic inline struct tegra_dsi_state *
5062306a36Sopenharmony_cito_dsi_state(struct drm_connector_state *state)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	return container_of(state, struct tegra_dsi_state, base);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistruct tegra_dsi {
5662306a36Sopenharmony_ci	struct host1x_client client;
5762306a36Sopenharmony_ci	struct tegra_output output;
5862306a36Sopenharmony_ci	struct device *dev;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	void __iomem *regs;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	struct reset_control *rst;
6362306a36Sopenharmony_ci	struct clk *clk_parent;
6462306a36Sopenharmony_ci	struct clk *clk_lp;
6562306a36Sopenharmony_ci	struct clk *clk;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	struct drm_info_list *debugfs_files;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	unsigned long flags;
7062306a36Sopenharmony_ci	enum mipi_dsi_pixel_format format;
7162306a36Sopenharmony_ci	unsigned int lanes;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	struct tegra_mipi_device *mipi;
7462306a36Sopenharmony_ci	struct mipi_dsi_host host;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	struct regulator *vdd;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	unsigned int video_fifo_depth;
7962306a36Sopenharmony_ci	unsigned int host_fifo_depth;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	/* for ganged-mode support */
8262306a36Sopenharmony_ci	struct tegra_dsi *master;
8362306a36Sopenharmony_ci	struct tegra_dsi *slave;
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic inline struct tegra_dsi *
8762306a36Sopenharmony_cihost1x_client_to_dsi(struct host1x_client *client)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	return container_of(client, struct tegra_dsi, client);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic inline struct tegra_dsi *host_to_tegra(struct mipi_dsi_host *host)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	return container_of(host, struct tegra_dsi, host);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic inline struct tegra_dsi *to_dsi(struct tegra_output *output)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	return container_of(output, struct tegra_dsi, output);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic struct tegra_dsi_state *tegra_dsi_get_state(struct tegra_dsi *dsi)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	return to_dsi_state(dsi->output.connector.state);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic inline u32 tegra_dsi_readl(struct tegra_dsi *dsi, unsigned int offset)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	u32 value = readl(dsi->regs + (offset << 2));
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	trace_dsi_readl(dsi->dev, offset, value);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return value;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic inline void tegra_dsi_writel(struct tegra_dsi *dsi, u32 value,
11762306a36Sopenharmony_ci				    unsigned int offset)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	trace_dsi_writel(dsi->dev, offset, value);
12062306a36Sopenharmony_ci	writel(value, dsi->regs + (offset << 2));
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name }
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic const struct debugfs_reg32 tegra_dsi_regs[] = {
12662306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INCR_SYNCPT),
12762306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INCR_SYNCPT_CONTROL),
12862306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INCR_SYNCPT_ERROR),
12962306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_CTXSW),
13062306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_RD_DATA),
13162306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_WR_DATA),
13262306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_POWER_CONTROL),
13362306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INT_ENABLE),
13462306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INT_STATUS),
13562306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INT_MASK),
13662306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_HOST_CONTROL),
13762306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_CONTROL),
13862306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_SOL_DELAY),
13962306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_MAX_THRESHOLD),
14062306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_TRIGGER),
14162306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_TX_CRC),
14262306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_STATUS),
14362306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_CONTROL),
14462306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_0),
14562306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_1),
14662306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_2),
14762306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_3),
14862306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_4),
14962306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_5),
15062306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_6),
15162306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_7),
15262306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_SEQ_0_LO),
15362306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_SEQ_0_HI),
15462306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_SEQ_1_LO),
15562306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_SEQ_1_HI),
15662306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_SEQ_2_LO),
15762306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_SEQ_2_HI),
15862306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_SEQ_3_LO),
15962306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_SEQ_3_HI),
16062306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_SEQ_4_LO),
16162306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_SEQ_4_HI),
16262306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_SEQ_5_LO),
16362306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_SEQ_5_HI),
16462306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_DCS_CMDS),
16562306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_LEN_0_1),
16662306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_LEN_2_3),
16762306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_LEN_4_5),
16862306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PKT_LEN_6_7),
16962306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PHY_TIMING_0),
17062306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PHY_TIMING_1),
17162306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PHY_TIMING_2),
17262306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_BTA_TIMING),
17362306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_TIMEOUT_0),
17462306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_TIMEOUT_1),
17562306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_TO_TALLY),
17662306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PAD_CONTROL_0),
17762306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PAD_CONTROL_CD),
17862306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PAD_CD_STATUS),
17962306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_VIDEO_MODE_CONTROL),
18062306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PAD_CONTROL_1),
18162306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PAD_CONTROL_2),
18262306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PAD_CONTROL_3),
18362306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_PAD_CONTROL_4),
18462306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_GANGED_MODE_CONTROL),
18562306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_GANGED_MODE_START),
18662306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_GANGED_MODE_SIZE),
18762306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_RAW_DATA_BYTE_COUNT),
18862306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_ULTRA_LOW_POWER_CONTROL),
18962306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_8),
19062306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_9),
19162306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_10),
19262306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_11),
19362306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_12),
19462306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_13),
19562306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_14),
19662306a36Sopenharmony_ci	DEBUGFS_REG32(DSI_INIT_SEQ_DATA_15),
19762306a36Sopenharmony_ci};
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int tegra_dsi_show_regs(struct seq_file *s, void *data)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct drm_info_node *node = s->private;
20262306a36Sopenharmony_ci	struct tegra_dsi *dsi = node->info_ent->data;
20362306a36Sopenharmony_ci	struct drm_crtc *crtc = dsi->output.encoder.crtc;
20462306a36Sopenharmony_ci	struct drm_device *drm = node->minor->dev;
20562306a36Sopenharmony_ci	unsigned int i;
20662306a36Sopenharmony_ci	int err = 0;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	drm_modeset_lock_all(drm);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (!crtc || !crtc->state->active) {
21162306a36Sopenharmony_ci		err = -EBUSY;
21262306a36Sopenharmony_ci		goto unlock;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tegra_dsi_regs); i++) {
21662306a36Sopenharmony_ci		unsigned int offset = tegra_dsi_regs[i].offset;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		seq_printf(s, "%-32s %#05x %08x\n", tegra_dsi_regs[i].name,
21962306a36Sopenharmony_ci			   offset, tegra_dsi_readl(dsi, offset));
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ciunlock:
22362306a36Sopenharmony_ci	drm_modeset_unlock_all(drm);
22462306a36Sopenharmony_ci	return err;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic struct drm_info_list debugfs_files[] = {
22862306a36Sopenharmony_ci	{ "regs", tegra_dsi_show_regs, 0, NULL },
22962306a36Sopenharmony_ci};
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic int tegra_dsi_late_register(struct drm_connector *connector)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct tegra_output *output = connector_to_output(connector);
23462306a36Sopenharmony_ci	unsigned int i, count = ARRAY_SIZE(debugfs_files);
23562306a36Sopenharmony_ci	struct drm_minor *minor = connector->dev->primary;
23662306a36Sopenharmony_ci	struct dentry *root = connector->debugfs_entry;
23762306a36Sopenharmony_ci	struct tegra_dsi *dsi = to_dsi(output);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	dsi->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
24062306a36Sopenharmony_ci				     GFP_KERNEL);
24162306a36Sopenharmony_ci	if (!dsi->debugfs_files)
24262306a36Sopenharmony_ci		return -ENOMEM;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	for (i = 0; i < count; i++)
24562306a36Sopenharmony_ci		dsi->debugfs_files[i].data = dsi;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	drm_debugfs_create_files(dsi->debugfs_files, count, root, minor);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return 0;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic void tegra_dsi_early_unregister(struct drm_connector *connector)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct tegra_output *output = connector_to_output(connector);
25562306a36Sopenharmony_ci	unsigned int count = ARRAY_SIZE(debugfs_files);
25662306a36Sopenharmony_ci	struct tegra_dsi *dsi = to_dsi(output);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	drm_debugfs_remove_files(dsi->debugfs_files, count,
25962306a36Sopenharmony_ci				 connector->dev->primary);
26062306a36Sopenharmony_ci	kfree(dsi->debugfs_files);
26162306a36Sopenharmony_ci	dsi->debugfs_files = NULL;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci#define PKT_ID0(id)	((((id) & 0x3f) <<  3) | (1 <<  9))
26562306a36Sopenharmony_ci#define PKT_LEN0(len)	(((len) & 0x07) <<  0)
26662306a36Sopenharmony_ci#define PKT_ID1(id)	((((id) & 0x3f) << 13) | (1 << 19))
26762306a36Sopenharmony_ci#define PKT_LEN1(len)	(((len) & 0x07) << 10)
26862306a36Sopenharmony_ci#define PKT_ID2(id)	((((id) & 0x3f) << 23) | (1 << 29))
26962306a36Sopenharmony_ci#define PKT_LEN2(len)	(((len) & 0x07) << 20)
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci#define PKT_LP		(1 << 30)
27262306a36Sopenharmony_ci#define NUM_PKT_SEQ	12
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/*
27562306a36Sopenharmony_ci * non-burst mode with sync pulses
27662306a36Sopenharmony_ci */
27762306a36Sopenharmony_cistatic const u32 pkt_seq_video_non_burst_sync_pulses[NUM_PKT_SEQ] = {
27862306a36Sopenharmony_ci	[ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
27962306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
28062306a36Sopenharmony_ci	       PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
28162306a36Sopenharmony_ci	       PKT_LP,
28262306a36Sopenharmony_ci	[ 1] = 0,
28362306a36Sopenharmony_ci	[ 2] = PKT_ID0(MIPI_DSI_V_SYNC_END) | PKT_LEN0(0) |
28462306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
28562306a36Sopenharmony_ci	       PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
28662306a36Sopenharmony_ci	       PKT_LP,
28762306a36Sopenharmony_ci	[ 3] = 0,
28862306a36Sopenharmony_ci	[ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
28962306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
29062306a36Sopenharmony_ci	       PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
29162306a36Sopenharmony_ci	       PKT_LP,
29262306a36Sopenharmony_ci	[ 5] = 0,
29362306a36Sopenharmony_ci	[ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
29462306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
29562306a36Sopenharmony_ci	       PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0),
29662306a36Sopenharmony_ci	[ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) |
29762306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) |
29862306a36Sopenharmony_ci	       PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4),
29962306a36Sopenharmony_ci	[ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
30062306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
30162306a36Sopenharmony_ci	       PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
30262306a36Sopenharmony_ci	       PKT_LP,
30362306a36Sopenharmony_ci	[ 9] = 0,
30462306a36Sopenharmony_ci	[10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
30562306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
30662306a36Sopenharmony_ci	       PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0),
30762306a36Sopenharmony_ci	[11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) |
30862306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) |
30962306a36Sopenharmony_ci	       PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4),
31062306a36Sopenharmony_ci};
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci/*
31362306a36Sopenharmony_ci * non-burst mode with sync events
31462306a36Sopenharmony_ci */
31562306a36Sopenharmony_cistatic const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = {
31662306a36Sopenharmony_ci	[ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
31762306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
31862306a36Sopenharmony_ci	       PKT_LP,
31962306a36Sopenharmony_ci	[ 1] = 0,
32062306a36Sopenharmony_ci	[ 2] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
32162306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
32262306a36Sopenharmony_ci	       PKT_LP,
32362306a36Sopenharmony_ci	[ 3] = 0,
32462306a36Sopenharmony_ci	[ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
32562306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
32662306a36Sopenharmony_ci	       PKT_LP,
32762306a36Sopenharmony_ci	[ 5] = 0,
32862306a36Sopenharmony_ci	[ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
32962306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
33062306a36Sopenharmony_ci	       PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
33162306a36Sopenharmony_ci	[ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
33262306a36Sopenharmony_ci	[ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
33362306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
33462306a36Sopenharmony_ci	       PKT_LP,
33562306a36Sopenharmony_ci	[ 9] = 0,
33662306a36Sopenharmony_ci	[10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
33762306a36Sopenharmony_ci	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
33862306a36Sopenharmony_ci	       PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
33962306a36Sopenharmony_ci	[11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
34062306a36Sopenharmony_ci};
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = {
34362306a36Sopenharmony_ci	[ 0] = 0,
34462306a36Sopenharmony_ci	[ 1] = 0,
34562306a36Sopenharmony_ci	[ 2] = 0,
34662306a36Sopenharmony_ci	[ 3] = 0,
34762306a36Sopenharmony_ci	[ 4] = 0,
34862306a36Sopenharmony_ci	[ 5] = 0,
34962306a36Sopenharmony_ci	[ 6] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(3) | PKT_LP,
35062306a36Sopenharmony_ci	[ 7] = 0,
35162306a36Sopenharmony_ci	[ 8] = 0,
35262306a36Sopenharmony_ci	[ 9] = 0,
35362306a36Sopenharmony_ci	[10] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(5) | PKT_LP,
35462306a36Sopenharmony_ci	[11] = 0,
35562306a36Sopenharmony_ci};
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic void tegra_dsi_set_phy_timing(struct tegra_dsi *dsi,
35862306a36Sopenharmony_ci				     unsigned long period,
35962306a36Sopenharmony_ci				     const struct mipi_dphy_timing *timing)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	u32 value;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	value = DSI_TIMING_FIELD(timing->hsexit, period, 1) << 24 |
36462306a36Sopenharmony_ci		DSI_TIMING_FIELD(timing->hstrail, period, 0) << 16 |
36562306a36Sopenharmony_ci		DSI_TIMING_FIELD(timing->hszero, period, 3) << 8 |
36662306a36Sopenharmony_ci		DSI_TIMING_FIELD(timing->hsprepare, period, 1);
36762306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_0);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	value = DSI_TIMING_FIELD(timing->clktrail, period, 1) << 24 |
37062306a36Sopenharmony_ci		DSI_TIMING_FIELD(timing->clkpost, period, 1) << 16 |
37162306a36Sopenharmony_ci		DSI_TIMING_FIELD(timing->clkzero, period, 1) << 8 |
37262306a36Sopenharmony_ci		DSI_TIMING_FIELD(timing->lpx, period, 1);
37362306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_1);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	value = DSI_TIMING_FIELD(timing->clkprepare, period, 1) << 16 |
37662306a36Sopenharmony_ci		DSI_TIMING_FIELD(timing->clkpre, period, 1) << 8 |
37762306a36Sopenharmony_ci		DSI_TIMING_FIELD(0xff * period, period, 0) << 0;
37862306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_2);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	value = DSI_TIMING_FIELD(timing->taget, period, 1) << 16 |
38162306a36Sopenharmony_ci		DSI_TIMING_FIELD(timing->tasure, period, 1) << 8 |
38262306a36Sopenharmony_ci		DSI_TIMING_FIELD(timing->tago, period, 1);
38362306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_BTA_TIMING);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (dsi->slave)
38662306a36Sopenharmony_ci		tegra_dsi_set_phy_timing(dsi->slave, period, timing);
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format,
39062306a36Sopenharmony_ci				unsigned int *mulp, unsigned int *divp)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	switch (format) {
39362306a36Sopenharmony_ci	case MIPI_DSI_FMT_RGB666_PACKED:
39462306a36Sopenharmony_ci	case MIPI_DSI_FMT_RGB888:
39562306a36Sopenharmony_ci		*mulp = 3;
39662306a36Sopenharmony_ci		*divp = 1;
39762306a36Sopenharmony_ci		break;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	case MIPI_DSI_FMT_RGB565:
40062306a36Sopenharmony_ci		*mulp = 2;
40162306a36Sopenharmony_ci		*divp = 1;
40262306a36Sopenharmony_ci		break;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	case MIPI_DSI_FMT_RGB666:
40562306a36Sopenharmony_ci		*mulp = 9;
40662306a36Sopenharmony_ci		*divp = 4;
40762306a36Sopenharmony_ci		break;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	default:
41062306a36Sopenharmony_ci		return -EINVAL;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	return 0;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic int tegra_dsi_get_format(enum mipi_dsi_pixel_format format,
41762306a36Sopenharmony_ci				enum tegra_dsi_format *fmt)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	switch (format) {
42062306a36Sopenharmony_ci	case MIPI_DSI_FMT_RGB888:
42162306a36Sopenharmony_ci		*fmt = TEGRA_DSI_FORMAT_24P;
42262306a36Sopenharmony_ci		break;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	case MIPI_DSI_FMT_RGB666:
42562306a36Sopenharmony_ci		*fmt = TEGRA_DSI_FORMAT_18NP;
42662306a36Sopenharmony_ci		break;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	case MIPI_DSI_FMT_RGB666_PACKED:
42962306a36Sopenharmony_ci		*fmt = TEGRA_DSI_FORMAT_18P;
43062306a36Sopenharmony_ci		break;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	case MIPI_DSI_FMT_RGB565:
43362306a36Sopenharmony_ci		*fmt = TEGRA_DSI_FORMAT_16P;
43462306a36Sopenharmony_ci		break;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	default:
43762306a36Sopenharmony_ci		return -EINVAL;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return 0;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic void tegra_dsi_ganged_enable(struct tegra_dsi *dsi, unsigned int start,
44462306a36Sopenharmony_ci				    unsigned int size)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	u32 value;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	tegra_dsi_writel(dsi, start, DSI_GANGED_MODE_START);
44962306a36Sopenharmony_ci	tegra_dsi_writel(dsi, size << 16 | size, DSI_GANGED_MODE_SIZE);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	value = DSI_GANGED_MODE_CONTROL_ENABLE;
45262306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_GANGED_MODE_CONTROL);
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic void tegra_dsi_enable(struct tegra_dsi *dsi)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	u32 value;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
46062306a36Sopenharmony_ci	value |= DSI_POWER_CONTROL_ENABLE;
46162306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (dsi->slave)
46462306a36Sopenharmony_ci		tegra_dsi_enable(dsi->slave);
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic unsigned int tegra_dsi_get_lanes(struct tegra_dsi *dsi)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	if (dsi->master)
47062306a36Sopenharmony_ci		return dsi->master->lanes + dsi->lanes;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (dsi->slave)
47362306a36Sopenharmony_ci		return dsi->lanes + dsi->slave->lanes;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	return dsi->lanes;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic void tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
47962306a36Sopenharmony_ci				const struct drm_display_mode *mode)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	unsigned int hact, hsw, hbp, hfp, i, mul, div;
48262306a36Sopenharmony_ci	struct tegra_dsi_state *state;
48362306a36Sopenharmony_ci	const u32 *pkt_seq;
48462306a36Sopenharmony_ci	u32 value;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	/* XXX: pass in state into this function? */
48762306a36Sopenharmony_ci	if (dsi->master)
48862306a36Sopenharmony_ci		state = tegra_dsi_get_state(dsi->master);
48962306a36Sopenharmony_ci	else
49062306a36Sopenharmony_ci		state = tegra_dsi_get_state(dsi);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	mul = state->mul;
49362306a36Sopenharmony_ci	div = state->div;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
49662306a36Sopenharmony_ci		DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n");
49762306a36Sopenharmony_ci		pkt_seq = pkt_seq_video_non_burst_sync_pulses;
49862306a36Sopenharmony_ci	} else if (dsi->flags & MIPI_DSI_MODE_VIDEO) {
49962306a36Sopenharmony_ci		DRM_DEBUG_KMS("Non-burst video mode with sync events\n");
50062306a36Sopenharmony_ci		pkt_seq = pkt_seq_video_non_burst_sync_events;
50162306a36Sopenharmony_ci	} else {
50262306a36Sopenharmony_ci		DRM_DEBUG_KMS("Command mode\n");
50362306a36Sopenharmony_ci		pkt_seq = pkt_seq_command_mode;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	value = DSI_CONTROL_CHANNEL(0) |
50762306a36Sopenharmony_ci		DSI_CONTROL_FORMAT(state->format) |
50862306a36Sopenharmony_ci		DSI_CONTROL_LANES(dsi->lanes - 1) |
50962306a36Sopenharmony_ci		DSI_CONTROL_SOURCE(pipe);
51062306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_CONTROL);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	tegra_dsi_writel(dsi, dsi->video_fifo_depth, DSI_MAX_THRESHOLD);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	value = DSI_HOST_CONTROL_HS;
51562306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	value = tegra_dsi_readl(dsi, DSI_CONTROL);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if (dsi->flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
52062306a36Sopenharmony_ci		value |= DSI_CONTROL_HS_CLK_CTRL;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	value &= ~DSI_CONTROL_TX_TRIG(3);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	/* enable DCS commands for command mode */
52562306a36Sopenharmony_ci	if (dsi->flags & MIPI_DSI_MODE_VIDEO)
52662306a36Sopenharmony_ci		value &= ~DSI_CONTROL_DCS_ENABLE;
52762306a36Sopenharmony_ci	else
52862306a36Sopenharmony_ci		value |= DSI_CONTROL_DCS_ENABLE;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	value |= DSI_CONTROL_VIDEO_ENABLE;
53162306a36Sopenharmony_ci	value &= ~DSI_CONTROL_HOST_ENABLE;
53262306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_CONTROL);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	for (i = 0; i < NUM_PKT_SEQ; i++)
53562306a36Sopenharmony_ci		tegra_dsi_writel(dsi, pkt_seq[i], DSI_PKT_SEQ_0_LO + i);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (dsi->flags & MIPI_DSI_MODE_VIDEO) {
53862306a36Sopenharmony_ci		/* horizontal active pixels */
53962306a36Sopenharmony_ci		hact = mode->hdisplay * mul / div;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		/* horizontal sync width */
54262306a36Sopenharmony_ci		hsw = (mode->hsync_end - mode->hsync_start) * mul / div;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci		/* horizontal back porch */
54562306a36Sopenharmony_ci		hbp = (mode->htotal - mode->hsync_end) * mul / div;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		if ((dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0)
54862306a36Sopenharmony_ci			hbp += hsw;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		/* horizontal front porch */
55162306a36Sopenharmony_ci		hfp = (mode->hsync_start - mode->hdisplay) * mul / div;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		/* subtract packet overhead */
55462306a36Sopenharmony_ci		hsw -= 10;
55562306a36Sopenharmony_ci		hbp -= 14;
55662306a36Sopenharmony_ci		hfp -= 8;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci		tegra_dsi_writel(dsi, hsw << 16 | 0, DSI_PKT_LEN_0_1);
55962306a36Sopenharmony_ci		tegra_dsi_writel(dsi, hact << 16 | hbp, DSI_PKT_LEN_2_3);
56062306a36Sopenharmony_ci		tegra_dsi_writel(dsi, hfp, DSI_PKT_LEN_4_5);
56162306a36Sopenharmony_ci		tegra_dsi_writel(dsi, 0x0f0f << 16, DSI_PKT_LEN_6_7);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		/* set SOL delay (for non-burst mode only) */
56462306a36Sopenharmony_ci		tegra_dsi_writel(dsi, 8 * mul / div, DSI_SOL_DELAY);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci		/* TODO: implement ganged mode */
56762306a36Sopenharmony_ci	} else {
56862306a36Sopenharmony_ci		u16 bytes;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci		if (dsi->master || dsi->slave) {
57162306a36Sopenharmony_ci			/*
57262306a36Sopenharmony_ci			 * For ganged mode, assume symmetric left-right mode.
57362306a36Sopenharmony_ci			 */
57462306a36Sopenharmony_ci			bytes = 1 + (mode->hdisplay / 2) * mul / div;
57562306a36Sopenharmony_ci		} else {
57662306a36Sopenharmony_ci			/* 1 byte (DCS command) + pixel data */
57762306a36Sopenharmony_ci			bytes = 1 + mode->hdisplay * mul / div;
57862306a36Sopenharmony_ci		}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_0_1);
58162306a36Sopenharmony_ci		tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_2_3);
58262306a36Sopenharmony_ci		tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_4_5);
58362306a36Sopenharmony_ci		tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_6_7);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		value = MIPI_DCS_WRITE_MEMORY_START << 8 |
58662306a36Sopenharmony_ci			MIPI_DCS_WRITE_MEMORY_CONTINUE;
58762306a36Sopenharmony_ci		tegra_dsi_writel(dsi, value, DSI_DCS_CMDS);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		/* set SOL delay */
59062306a36Sopenharmony_ci		if (dsi->master || dsi->slave) {
59162306a36Sopenharmony_ci			unsigned long delay, bclk, bclk_ganged;
59262306a36Sopenharmony_ci			unsigned int lanes = state->lanes;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci			/* SOL to valid, valid to FIFO and FIFO write delay */
59562306a36Sopenharmony_ci			delay = 4 + 4 + 2;
59662306a36Sopenharmony_ci			delay = DIV_ROUND_UP(delay * mul, div * lanes);
59762306a36Sopenharmony_ci			/* FIFO read delay */
59862306a36Sopenharmony_ci			delay = delay + 6;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci			bclk = DIV_ROUND_UP(mode->htotal * mul, div * lanes);
60162306a36Sopenharmony_ci			bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes);
60262306a36Sopenharmony_ci			value = bclk - bclk_ganged + delay + 20;
60362306a36Sopenharmony_ci		} else {
60462306a36Sopenharmony_ci			/* TODO: revisit for non-ganged mode */
60562306a36Sopenharmony_ci			value = 8 * mul / div;
60662306a36Sopenharmony_ci		}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		tegra_dsi_writel(dsi, value, DSI_SOL_DELAY);
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (dsi->slave) {
61262306a36Sopenharmony_ci		tegra_dsi_configure(dsi->slave, pipe, mode);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci		/*
61562306a36Sopenharmony_ci		 * TODO: Support modes other than symmetrical left-right
61662306a36Sopenharmony_ci		 * split.
61762306a36Sopenharmony_ci		 */
61862306a36Sopenharmony_ci		tegra_dsi_ganged_enable(dsi, 0, mode->hdisplay / 2);
61962306a36Sopenharmony_ci		tegra_dsi_ganged_enable(dsi->slave, mode->hdisplay / 2,
62062306a36Sopenharmony_ci					mode->hdisplay / 2);
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic int tegra_dsi_wait_idle(struct tegra_dsi *dsi, unsigned long timeout)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	u32 value;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(timeout);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
63162306a36Sopenharmony_ci		value = tegra_dsi_readl(dsi, DSI_STATUS);
63262306a36Sopenharmony_ci		if (value & DSI_STATUS_IDLE)
63362306a36Sopenharmony_ci			return 0;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci		usleep_range(1000, 2000);
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	return -ETIMEDOUT;
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic void tegra_dsi_video_disable(struct tegra_dsi *dsi)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	u32 value;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	value = tegra_dsi_readl(dsi, DSI_CONTROL);
64662306a36Sopenharmony_ci	value &= ~DSI_CONTROL_VIDEO_ENABLE;
64762306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_CONTROL);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (dsi->slave)
65062306a36Sopenharmony_ci		tegra_dsi_video_disable(dsi->slave);
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic void tegra_dsi_ganged_disable(struct tegra_dsi *dsi)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_START);
65662306a36Sopenharmony_ci	tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_SIZE);
65762306a36Sopenharmony_ci	tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL);
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic int tegra_dsi_pad_enable(struct tegra_dsi *dsi)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	u32 value;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0);
66562306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	return 0;
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_cistatic int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	u32 value;
67362306a36Sopenharmony_ci	int err;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	/*
67662306a36Sopenharmony_ci	 * XXX Is this still needed? The module reset is deasserted right
67762306a36Sopenharmony_ci	 * before this function is called.
67862306a36Sopenharmony_ci	 */
67962306a36Sopenharmony_ci	tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0);
68062306a36Sopenharmony_ci	tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1);
68162306a36Sopenharmony_ci	tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2);
68262306a36Sopenharmony_ci	tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3);
68362306a36Sopenharmony_ci	tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* start calibration */
68662306a36Sopenharmony_ci	tegra_dsi_pad_enable(dsi);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) |
68962306a36Sopenharmony_ci		DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) |
69062306a36Sopenharmony_ci		DSI_PAD_OUT_CLK(0x0);
69162306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) |
69462306a36Sopenharmony_ci		DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3);
69562306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_3);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	err = tegra_mipi_start_calibration(dsi->mipi);
69862306a36Sopenharmony_ci	if (err < 0)
69962306a36Sopenharmony_ci		return err;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	return tegra_mipi_finish_calibration(dsi->mipi);
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk,
70562306a36Sopenharmony_ci				  unsigned int vrefresh)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	unsigned int timeout;
70862306a36Sopenharmony_ci	u32 value;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/* one frame high-speed transmission timeout */
71162306a36Sopenharmony_ci	timeout = (bclk / vrefresh) / 512;
71262306a36Sopenharmony_ci	value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout);
71362306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	/* 2 ms peripheral timeout for panel */
71662306a36Sopenharmony_ci	timeout = 2 * bclk / 512 * 1000;
71762306a36Sopenharmony_ci	value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000);
71862306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0);
72162306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_TO_TALLY);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	if (dsi->slave)
72462306a36Sopenharmony_ci		tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh);
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cistatic void tegra_dsi_disable(struct tegra_dsi *dsi)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	u32 value;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (dsi->slave) {
73262306a36Sopenharmony_ci		tegra_dsi_ganged_disable(dsi->slave);
73362306a36Sopenharmony_ci		tegra_dsi_ganged_disable(dsi);
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
73762306a36Sopenharmony_ci	value &= ~DSI_POWER_CONTROL_ENABLE;
73862306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	if (dsi->slave)
74162306a36Sopenharmony_ci		tegra_dsi_disable(dsi->slave);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	usleep_range(5000, 10000);
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_cistatic void tegra_dsi_soft_reset(struct tegra_dsi *dsi)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	u32 value;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
75162306a36Sopenharmony_ci	value &= ~DSI_POWER_CONTROL_ENABLE;
75262306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	usleep_range(300, 1000);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
75762306a36Sopenharmony_ci	value |= DSI_POWER_CONTROL_ENABLE;
75862306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	usleep_range(300, 1000);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	value = tegra_dsi_readl(dsi, DSI_TRIGGER);
76362306a36Sopenharmony_ci	if (value)
76462306a36Sopenharmony_ci		tegra_dsi_writel(dsi, 0, DSI_TRIGGER);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (dsi->slave)
76762306a36Sopenharmony_ci		tegra_dsi_soft_reset(dsi->slave);
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic void tegra_dsi_connector_reset(struct drm_connector *connector)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	struct tegra_dsi_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (!state)
77562306a36Sopenharmony_ci		return;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	if (connector->state) {
77862306a36Sopenharmony_ci		__drm_atomic_helper_connector_destroy_state(connector->state);
77962306a36Sopenharmony_ci		kfree(connector->state);
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	__drm_atomic_helper_connector_reset(connector, &state->base);
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic struct drm_connector_state *
78662306a36Sopenharmony_citegra_dsi_connector_duplicate_state(struct drm_connector *connector)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	struct tegra_dsi_state *state = to_dsi_state(connector->state);
78962306a36Sopenharmony_ci	struct tegra_dsi_state *copy;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
79262306a36Sopenharmony_ci	if (!copy)
79362306a36Sopenharmony_ci		return NULL;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	__drm_atomic_helper_connector_duplicate_state(connector,
79662306a36Sopenharmony_ci						      &copy->base);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	return &copy->base;
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_cistatic const struct drm_connector_funcs tegra_dsi_connector_funcs = {
80262306a36Sopenharmony_ci	.reset = tegra_dsi_connector_reset,
80362306a36Sopenharmony_ci	.detect = tegra_output_connector_detect,
80462306a36Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
80562306a36Sopenharmony_ci	.destroy = tegra_output_connector_destroy,
80662306a36Sopenharmony_ci	.atomic_duplicate_state = tegra_dsi_connector_duplicate_state,
80762306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
80862306a36Sopenharmony_ci	.late_register = tegra_dsi_late_register,
80962306a36Sopenharmony_ci	.early_unregister = tegra_dsi_early_unregister,
81062306a36Sopenharmony_ci};
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic enum drm_mode_status
81362306a36Sopenharmony_citegra_dsi_connector_mode_valid(struct drm_connector *connector,
81462306a36Sopenharmony_ci			       struct drm_display_mode *mode)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	return MODE_OK;
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs tegra_dsi_connector_helper_funcs = {
82062306a36Sopenharmony_ci	.get_modes = tegra_output_connector_get_modes,
82162306a36Sopenharmony_ci	.mode_valid = tegra_dsi_connector_mode_valid,
82262306a36Sopenharmony_ci};
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic void tegra_dsi_unprepare(struct tegra_dsi *dsi)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	int err;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	if (dsi->slave)
82962306a36Sopenharmony_ci		tegra_dsi_unprepare(dsi->slave);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	err = tegra_mipi_disable(dsi->mipi);
83262306a36Sopenharmony_ci	if (err < 0)
83362306a36Sopenharmony_ci		dev_err(dsi->dev, "failed to disable MIPI calibration: %d\n",
83462306a36Sopenharmony_ci			err);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	err = host1x_client_suspend(&dsi->client);
83762306a36Sopenharmony_ci	if (err < 0)
83862306a36Sopenharmony_ci		dev_err(dsi->dev, "failed to suspend: %d\n", err);
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic void tegra_dsi_encoder_disable(struct drm_encoder *encoder)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	struct tegra_output *output = encoder_to_output(encoder);
84462306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
84562306a36Sopenharmony_ci	struct tegra_dsi *dsi = to_dsi(output);
84662306a36Sopenharmony_ci	u32 value;
84762306a36Sopenharmony_ci	int err;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	if (output->panel)
85062306a36Sopenharmony_ci		drm_panel_disable(output->panel);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	tegra_dsi_video_disable(dsi);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	/*
85562306a36Sopenharmony_ci	 * The following accesses registers of the display controller, so make
85662306a36Sopenharmony_ci	 * sure it's only executed when the output is attached to one.
85762306a36Sopenharmony_ci	 */
85862306a36Sopenharmony_ci	if (dc) {
85962306a36Sopenharmony_ci		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
86062306a36Sopenharmony_ci		value &= ~DSI_ENABLE;
86162306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci		tegra_dc_commit(dc);
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	err = tegra_dsi_wait_idle(dsi, 100);
86762306a36Sopenharmony_ci	if (err < 0)
86862306a36Sopenharmony_ci		dev_dbg(dsi->dev, "failed to idle DSI: %d\n", err);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	tegra_dsi_soft_reset(dsi);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	if (output->panel)
87362306a36Sopenharmony_ci		drm_panel_unprepare(output->panel);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	tegra_dsi_disable(dsi);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	tegra_dsi_unprepare(dsi);
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic int tegra_dsi_prepare(struct tegra_dsi *dsi)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	int err;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	err = host1x_client_resume(&dsi->client);
88562306a36Sopenharmony_ci	if (err < 0) {
88662306a36Sopenharmony_ci		dev_err(dsi->dev, "failed to resume: %d\n", err);
88762306a36Sopenharmony_ci		return err;
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	err = tegra_mipi_enable(dsi->mipi);
89162306a36Sopenharmony_ci	if (err < 0)
89262306a36Sopenharmony_ci		dev_err(dsi->dev, "failed to enable MIPI calibration: %d\n",
89362306a36Sopenharmony_ci			err);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	err = tegra_dsi_pad_calibrate(dsi);
89662306a36Sopenharmony_ci	if (err < 0)
89762306a36Sopenharmony_ci		dev_err(dsi->dev, "MIPI calibration failed: %d\n", err);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	if (dsi->slave)
90062306a36Sopenharmony_ci		tegra_dsi_prepare(dsi->slave);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	return 0;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_cistatic void tegra_dsi_encoder_enable(struct drm_encoder *encoder)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
90862306a36Sopenharmony_ci	struct tegra_output *output = encoder_to_output(encoder);
90962306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
91062306a36Sopenharmony_ci	struct tegra_dsi *dsi = to_dsi(output);
91162306a36Sopenharmony_ci	struct tegra_dsi_state *state;
91262306a36Sopenharmony_ci	u32 value;
91362306a36Sopenharmony_ci	int err;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	/* If the bootloader enabled DSI it needs to be disabled
91662306a36Sopenharmony_ci	 * in order for the panel initialization commands to be
91762306a36Sopenharmony_ci	 * properly sent.
91862306a36Sopenharmony_ci	 */
91962306a36Sopenharmony_ci	value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	if (value & DSI_POWER_CONTROL_ENABLE)
92262306a36Sopenharmony_ci		tegra_dsi_disable(dsi);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	err = tegra_dsi_prepare(dsi);
92562306a36Sopenharmony_ci	if (err < 0) {
92662306a36Sopenharmony_ci		dev_err(dsi->dev, "failed to prepare: %d\n", err);
92762306a36Sopenharmony_ci		return;
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	state = tegra_dsi_get_state(dsi);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	tegra_dsi_set_timeout(dsi, state->bclk, state->vrefresh);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	/*
93562306a36Sopenharmony_ci	 * The D-PHY timing fields are expressed in byte-clock cycles, so
93662306a36Sopenharmony_ci	 * multiply the period by 8.
93762306a36Sopenharmony_ci	 */
93862306a36Sopenharmony_ci	tegra_dsi_set_phy_timing(dsi, state->period * 8, &state->timing);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (output->panel)
94162306a36Sopenharmony_ci		drm_panel_prepare(output->panel);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	tegra_dsi_configure(dsi, dc->pipe, mode);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	/* enable display controller */
94662306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
94762306a36Sopenharmony_ci	value |= DSI_ENABLE;
94862306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	tegra_dc_commit(dc);
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	/* enable DSI controller */
95362306a36Sopenharmony_ci	tegra_dsi_enable(dsi);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	if (output->panel)
95662306a36Sopenharmony_ci		drm_panel_enable(output->panel);
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic int
96062306a36Sopenharmony_citegra_dsi_encoder_atomic_check(struct drm_encoder *encoder,
96162306a36Sopenharmony_ci			       struct drm_crtc_state *crtc_state,
96262306a36Sopenharmony_ci			       struct drm_connector_state *conn_state)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	struct tegra_output *output = encoder_to_output(encoder);
96562306a36Sopenharmony_ci	struct tegra_dsi_state *state = to_dsi_state(conn_state);
96662306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
96762306a36Sopenharmony_ci	struct tegra_dsi *dsi = to_dsi(output);
96862306a36Sopenharmony_ci	unsigned int scdiv;
96962306a36Sopenharmony_ci	unsigned long plld;
97062306a36Sopenharmony_ci	int err;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	state->pclk = crtc_state->mode.clock * 1000;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	err = tegra_dsi_get_muldiv(dsi->format, &state->mul, &state->div);
97562306a36Sopenharmony_ci	if (err < 0)
97662306a36Sopenharmony_ci		return err;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	state->lanes = tegra_dsi_get_lanes(dsi);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	err = tegra_dsi_get_format(dsi->format, &state->format);
98162306a36Sopenharmony_ci	if (err < 0)
98262306a36Sopenharmony_ci		return err;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	state->vrefresh = drm_mode_vrefresh(&crtc_state->mode);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	/* compute byte clock */
98762306a36Sopenharmony_ci	state->bclk = (state->pclk * state->mul) / (state->div * state->lanes);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", state->mul, state->div,
99062306a36Sopenharmony_ci		      state->lanes);
99162306a36Sopenharmony_ci	DRM_DEBUG_KMS("format: %u, vrefresh: %u\n", state->format,
99262306a36Sopenharmony_ci		      state->vrefresh);
99362306a36Sopenharmony_ci	DRM_DEBUG_KMS("bclk: %lu\n", state->bclk);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	/*
99662306a36Sopenharmony_ci	 * Compute bit clock and round up to the next MHz.
99762306a36Sopenharmony_ci	 */
99862306a36Sopenharmony_ci	plld = DIV_ROUND_UP(state->bclk * 8, USEC_PER_SEC) * USEC_PER_SEC;
99962306a36Sopenharmony_ci	state->period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	err = mipi_dphy_timing_get_default(&state->timing, state->period);
100262306a36Sopenharmony_ci	if (err < 0)
100362306a36Sopenharmony_ci		return err;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	err = mipi_dphy_timing_validate(&state->timing, state->period);
100662306a36Sopenharmony_ci	if (err < 0) {
100762306a36Sopenharmony_ci		dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err);
100862306a36Sopenharmony_ci		return err;
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	/*
101262306a36Sopenharmony_ci	 * We divide the frequency by two here, but we make up for that by
101362306a36Sopenharmony_ci	 * setting the shift clock divider (further below) to half of the
101462306a36Sopenharmony_ci	 * correct value.
101562306a36Sopenharmony_ci	 */
101662306a36Sopenharmony_ci	plld /= 2;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	/*
101962306a36Sopenharmony_ci	 * Derive pixel clock from bit clock using the shift clock divider.
102062306a36Sopenharmony_ci	 * Note that this is only half of what we would expect, but we need
102162306a36Sopenharmony_ci	 * that to make up for the fact that we divided the bit clock by a
102262306a36Sopenharmony_ci	 * factor of two above.
102362306a36Sopenharmony_ci	 *
102462306a36Sopenharmony_ci	 * It's not clear exactly why this is necessary, but the display is
102562306a36Sopenharmony_ci	 * not working properly otherwise. Perhaps the PLLs cannot generate
102662306a36Sopenharmony_ci	 * frequencies sufficiently high.
102762306a36Sopenharmony_ci	 */
102862306a36Sopenharmony_ci	scdiv = ((8 * state->mul) / (state->div * state->lanes)) - 2;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	err = tegra_dc_state_setup_clock(dc, crtc_state, dsi->clk_parent,
103162306a36Sopenharmony_ci					 plld, scdiv);
103262306a36Sopenharmony_ci	if (err < 0) {
103362306a36Sopenharmony_ci		dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
103462306a36Sopenharmony_ci		return err;
103562306a36Sopenharmony_ci	}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	return err;
103862306a36Sopenharmony_ci}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
104162306a36Sopenharmony_ci	.disable = tegra_dsi_encoder_disable,
104262306a36Sopenharmony_ci	.enable = tegra_dsi_encoder_enable,
104362306a36Sopenharmony_ci	.atomic_check = tegra_dsi_encoder_atomic_check,
104462306a36Sopenharmony_ci};
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_cistatic int tegra_dsi_init(struct host1x_client *client)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(client->host);
104962306a36Sopenharmony_ci	struct tegra_dsi *dsi = host1x_client_to_dsi(client);
105062306a36Sopenharmony_ci	int err;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	/* Gangsters must not register their own outputs. */
105362306a36Sopenharmony_ci	if (!dsi->master) {
105462306a36Sopenharmony_ci		dsi->output.dev = client->dev;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci		drm_connector_init(drm, &dsi->output.connector,
105762306a36Sopenharmony_ci				   &tegra_dsi_connector_funcs,
105862306a36Sopenharmony_ci				   DRM_MODE_CONNECTOR_DSI);
105962306a36Sopenharmony_ci		drm_connector_helper_add(&dsi->output.connector,
106062306a36Sopenharmony_ci					 &tegra_dsi_connector_helper_funcs);
106162306a36Sopenharmony_ci		dsi->output.connector.dpms = DRM_MODE_DPMS_OFF;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci		drm_simple_encoder_init(drm, &dsi->output.encoder,
106462306a36Sopenharmony_ci					DRM_MODE_ENCODER_DSI);
106562306a36Sopenharmony_ci		drm_encoder_helper_add(&dsi->output.encoder,
106662306a36Sopenharmony_ci				       &tegra_dsi_encoder_helper_funcs);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci		drm_connector_attach_encoder(&dsi->output.connector,
106962306a36Sopenharmony_ci						  &dsi->output.encoder);
107062306a36Sopenharmony_ci		drm_connector_register(&dsi->output.connector);
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci		err = tegra_output_init(drm, &dsi->output);
107362306a36Sopenharmony_ci		if (err < 0)
107462306a36Sopenharmony_ci			dev_err(dsi->dev, "failed to initialize output: %d\n",
107562306a36Sopenharmony_ci				err);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci		dsi->output.encoder.possible_crtcs = 0x3;
107862306a36Sopenharmony_ci	}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	return 0;
108162306a36Sopenharmony_ci}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cistatic int tegra_dsi_exit(struct host1x_client *client)
108462306a36Sopenharmony_ci{
108562306a36Sopenharmony_ci	struct tegra_dsi *dsi = host1x_client_to_dsi(client);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	tegra_output_exit(&dsi->output);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	return 0;
109062306a36Sopenharmony_ci}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_cistatic int tegra_dsi_runtime_suspend(struct host1x_client *client)
109362306a36Sopenharmony_ci{
109462306a36Sopenharmony_ci	struct tegra_dsi *dsi = host1x_client_to_dsi(client);
109562306a36Sopenharmony_ci	struct device *dev = client->dev;
109662306a36Sopenharmony_ci	int err;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (dsi->rst) {
109962306a36Sopenharmony_ci		err = reset_control_assert(dsi->rst);
110062306a36Sopenharmony_ci		if (err < 0) {
110162306a36Sopenharmony_ci			dev_err(dev, "failed to assert reset: %d\n", err);
110262306a36Sopenharmony_ci			return err;
110362306a36Sopenharmony_ci		}
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	usleep_range(1000, 2000);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	clk_disable_unprepare(dsi->clk_lp);
110962306a36Sopenharmony_ci	clk_disable_unprepare(dsi->clk);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	regulator_disable(dsi->vdd);
111262306a36Sopenharmony_ci	pm_runtime_put_sync(dev);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	return 0;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_cistatic int tegra_dsi_runtime_resume(struct host1x_client *client)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	struct tegra_dsi *dsi = host1x_client_to_dsi(client);
112062306a36Sopenharmony_ci	struct device *dev = client->dev;
112162306a36Sopenharmony_ci	int err;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	err = pm_runtime_resume_and_get(dev);
112462306a36Sopenharmony_ci	if (err < 0) {
112562306a36Sopenharmony_ci		dev_err(dev, "failed to get runtime PM: %d\n", err);
112662306a36Sopenharmony_ci		return err;
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	err = regulator_enable(dsi->vdd);
113062306a36Sopenharmony_ci	if (err < 0) {
113162306a36Sopenharmony_ci		dev_err(dev, "failed to enable VDD supply: %d\n", err);
113262306a36Sopenharmony_ci		goto put_rpm;
113362306a36Sopenharmony_ci	}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	err = clk_prepare_enable(dsi->clk);
113662306a36Sopenharmony_ci	if (err < 0) {
113762306a36Sopenharmony_ci		dev_err(dev, "cannot enable DSI clock: %d\n", err);
113862306a36Sopenharmony_ci		goto disable_vdd;
113962306a36Sopenharmony_ci	}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	err = clk_prepare_enable(dsi->clk_lp);
114262306a36Sopenharmony_ci	if (err < 0) {
114362306a36Sopenharmony_ci		dev_err(dev, "cannot enable low-power clock: %d\n", err);
114462306a36Sopenharmony_ci		goto disable_clk;
114562306a36Sopenharmony_ci	}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	usleep_range(1000, 2000);
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	if (dsi->rst) {
115062306a36Sopenharmony_ci		err = reset_control_deassert(dsi->rst);
115162306a36Sopenharmony_ci		if (err < 0) {
115262306a36Sopenharmony_ci			dev_err(dev, "cannot assert reset: %d\n", err);
115362306a36Sopenharmony_ci			goto disable_clk_lp;
115462306a36Sopenharmony_ci		}
115562306a36Sopenharmony_ci	}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	return 0;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_cidisable_clk_lp:
116062306a36Sopenharmony_ci	clk_disable_unprepare(dsi->clk_lp);
116162306a36Sopenharmony_cidisable_clk:
116262306a36Sopenharmony_ci	clk_disable_unprepare(dsi->clk);
116362306a36Sopenharmony_cidisable_vdd:
116462306a36Sopenharmony_ci	regulator_disable(dsi->vdd);
116562306a36Sopenharmony_ciput_rpm:
116662306a36Sopenharmony_ci	pm_runtime_put_sync(dev);
116762306a36Sopenharmony_ci	return err;
116862306a36Sopenharmony_ci}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_cistatic const struct host1x_client_ops dsi_client_ops = {
117162306a36Sopenharmony_ci	.init = tegra_dsi_init,
117262306a36Sopenharmony_ci	.exit = tegra_dsi_exit,
117362306a36Sopenharmony_ci	.suspend = tegra_dsi_runtime_suspend,
117462306a36Sopenharmony_ci	.resume = tegra_dsi_runtime_resume,
117562306a36Sopenharmony_ci};
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_cistatic int tegra_dsi_setup_clocks(struct tegra_dsi *dsi)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	struct clk *parent;
118062306a36Sopenharmony_ci	int err;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	parent = clk_get_parent(dsi->clk);
118362306a36Sopenharmony_ci	if (!parent)
118462306a36Sopenharmony_ci		return -EINVAL;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	err = clk_set_parent(parent, dsi->clk_parent);
118762306a36Sopenharmony_ci	if (err < 0)
118862306a36Sopenharmony_ci		return err;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	return 0;
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic const char * const error_report[16] = {
119462306a36Sopenharmony_ci	"SoT Error",
119562306a36Sopenharmony_ci	"SoT Sync Error",
119662306a36Sopenharmony_ci	"EoT Sync Error",
119762306a36Sopenharmony_ci	"Escape Mode Entry Command Error",
119862306a36Sopenharmony_ci	"Low-Power Transmit Sync Error",
119962306a36Sopenharmony_ci	"Peripheral Timeout Error",
120062306a36Sopenharmony_ci	"False Control Error",
120162306a36Sopenharmony_ci	"Contention Detected",
120262306a36Sopenharmony_ci	"ECC Error, single-bit",
120362306a36Sopenharmony_ci	"ECC Error, multi-bit",
120462306a36Sopenharmony_ci	"Checksum Error",
120562306a36Sopenharmony_ci	"DSI Data Type Not Recognized",
120662306a36Sopenharmony_ci	"DSI VC ID Invalid",
120762306a36Sopenharmony_ci	"Invalid Transmission Length",
120862306a36Sopenharmony_ci	"Reserved",
120962306a36Sopenharmony_ci	"DSI Protocol Violation",
121062306a36Sopenharmony_ci};
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_cistatic ssize_t tegra_dsi_read_response(struct tegra_dsi *dsi,
121362306a36Sopenharmony_ci				       const struct mipi_dsi_msg *msg,
121462306a36Sopenharmony_ci				       size_t count)
121562306a36Sopenharmony_ci{
121662306a36Sopenharmony_ci	u8 *rx = msg->rx_buf;
121762306a36Sopenharmony_ci	unsigned int i, j, k;
121862306a36Sopenharmony_ci	size_t size = 0;
121962306a36Sopenharmony_ci	u16 errors;
122062306a36Sopenharmony_ci	u32 value;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	/* read and parse packet header */
122362306a36Sopenharmony_ci	value = tegra_dsi_readl(dsi, DSI_RD_DATA);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	switch (value & 0x3f) {
122662306a36Sopenharmony_ci	case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
122762306a36Sopenharmony_ci		errors = (value >> 8) & 0xffff;
122862306a36Sopenharmony_ci		dev_dbg(dsi->dev, "Acknowledge and error report: %04x\n",
122962306a36Sopenharmony_ci			errors);
123062306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(error_report); i++)
123162306a36Sopenharmony_ci			if (errors & BIT(i))
123262306a36Sopenharmony_ci				dev_dbg(dsi->dev, "  %2u: %s\n", i,
123362306a36Sopenharmony_ci					error_report[i]);
123462306a36Sopenharmony_ci		break;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
123762306a36Sopenharmony_ci		rx[0] = (value >> 8) & 0xff;
123862306a36Sopenharmony_ci		size = 1;
123962306a36Sopenharmony_ci		break;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
124262306a36Sopenharmony_ci		rx[0] = (value >>  8) & 0xff;
124362306a36Sopenharmony_ci		rx[1] = (value >> 16) & 0xff;
124462306a36Sopenharmony_ci		size = 2;
124562306a36Sopenharmony_ci		break;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE:
124862306a36Sopenharmony_ci		size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff);
124962306a36Sopenharmony_ci		break;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE:
125262306a36Sopenharmony_ci		size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff);
125362306a36Sopenharmony_ci		break;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	default:
125662306a36Sopenharmony_ci		dev_err(dsi->dev, "unhandled response type: %02x\n",
125762306a36Sopenharmony_ci			value & 0x3f);
125862306a36Sopenharmony_ci		return -EPROTO;
125962306a36Sopenharmony_ci	}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	size = min(size, msg->rx_len);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	if (msg->rx_buf && size > 0) {
126462306a36Sopenharmony_ci		for (i = 0, j = 0; i < count - 1; i++, j += 4) {
126562306a36Sopenharmony_ci			u8 *rx = msg->rx_buf + j;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci			value = tegra_dsi_readl(dsi, DSI_RD_DATA);
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci			for (k = 0; k < 4 && (j + k) < msg->rx_len; k++)
127062306a36Sopenharmony_ci				rx[j + k] = (value >> (k << 3)) & 0xff;
127162306a36Sopenharmony_ci		}
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	return size;
127562306a36Sopenharmony_ci}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_cistatic int tegra_dsi_transmit(struct tegra_dsi *dsi, unsigned long timeout)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	tegra_dsi_writel(dsi, DSI_TRIGGER_HOST, DSI_TRIGGER);
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(timeout);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
128462306a36Sopenharmony_ci		u32 value = tegra_dsi_readl(dsi, DSI_TRIGGER);
128562306a36Sopenharmony_ci		if ((value & DSI_TRIGGER_HOST) == 0)
128662306a36Sopenharmony_ci			return 0;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci		usleep_range(1000, 2000);
128962306a36Sopenharmony_ci	}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	DRM_DEBUG_KMS("timeout waiting for transmission to complete\n");
129262306a36Sopenharmony_ci	return -ETIMEDOUT;
129362306a36Sopenharmony_ci}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_cistatic int tegra_dsi_wait_for_response(struct tegra_dsi *dsi,
129662306a36Sopenharmony_ci				       unsigned long timeout)
129762306a36Sopenharmony_ci{
129862306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
130162306a36Sopenharmony_ci		u32 value = tegra_dsi_readl(dsi, DSI_STATUS);
130262306a36Sopenharmony_ci		u8 count = value & 0x1f;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci		if (count > 0)
130562306a36Sopenharmony_ci			return count;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci		usleep_range(1000, 2000);
130862306a36Sopenharmony_ci	}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	DRM_DEBUG_KMS("peripheral returned no data\n");
131162306a36Sopenharmony_ci	return -ETIMEDOUT;
131262306a36Sopenharmony_ci}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_cistatic void tegra_dsi_writesl(struct tegra_dsi *dsi, unsigned long offset,
131562306a36Sopenharmony_ci			      const void *buffer, size_t size)
131662306a36Sopenharmony_ci{
131762306a36Sopenharmony_ci	const u8 *buf = buffer;
131862306a36Sopenharmony_ci	size_t i, j;
131962306a36Sopenharmony_ci	u32 value;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	for (j = 0; j < size; j += 4) {
132262306a36Sopenharmony_ci		value = 0;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci		for (i = 0; i < 4 && j + i < size; i++)
132562306a36Sopenharmony_ci			value |= buf[j + i] << (i << 3);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci		tegra_dsi_writel(dsi, value, DSI_WR_DATA);
132862306a36Sopenharmony_ci	}
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_cistatic ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host,
133262306a36Sopenharmony_ci				       const struct mipi_dsi_msg *msg)
133362306a36Sopenharmony_ci{
133462306a36Sopenharmony_ci	struct tegra_dsi *dsi = host_to_tegra(host);
133562306a36Sopenharmony_ci	struct mipi_dsi_packet packet;
133662306a36Sopenharmony_ci	const u8 *header;
133762306a36Sopenharmony_ci	size_t count;
133862306a36Sopenharmony_ci	ssize_t err;
133962306a36Sopenharmony_ci	u32 value;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	err = mipi_dsi_create_packet(&packet, msg);
134262306a36Sopenharmony_ci	if (err < 0)
134362306a36Sopenharmony_ci		return err;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	header = packet.header;
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	/* maximum FIFO depth is 1920 words */
134862306a36Sopenharmony_ci	if (packet.size > dsi->video_fifo_depth * 4)
134962306a36Sopenharmony_ci		return -ENOSPC;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	/* reset underflow/overflow flags */
135262306a36Sopenharmony_ci	value = tegra_dsi_readl(dsi, DSI_STATUS);
135362306a36Sopenharmony_ci	if (value & (DSI_STATUS_UNDERFLOW | DSI_STATUS_OVERFLOW)) {
135462306a36Sopenharmony_ci		value = DSI_HOST_CONTROL_FIFO_RESET;
135562306a36Sopenharmony_ci		tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
135662306a36Sopenharmony_ci		usleep_range(10, 20);
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
136062306a36Sopenharmony_ci	value |= DSI_POWER_CONTROL_ENABLE;
136162306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	usleep_range(5000, 10000);
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	value = DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST |
136662306a36Sopenharmony_ci		DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	if ((msg->flags & MIPI_DSI_MSG_USE_LPM) == 0)
136962306a36Sopenharmony_ci		value |= DSI_HOST_CONTROL_HS;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	/*
137262306a36Sopenharmony_ci	 * The host FIFO has a maximum of 64 words, so larger transmissions
137362306a36Sopenharmony_ci	 * need to use the video FIFO.
137462306a36Sopenharmony_ci	 */
137562306a36Sopenharmony_ci	if (packet.size > dsi->host_fifo_depth * 4)
137662306a36Sopenharmony_ci		value |= DSI_HOST_CONTROL_FIFO_SEL;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	/*
138162306a36Sopenharmony_ci	 * For reads and messages with explicitly requested ACK, generate a
138262306a36Sopenharmony_ci	 * BTA sequence after the transmission of the packet.
138362306a36Sopenharmony_ci	 */
138462306a36Sopenharmony_ci	if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) ||
138562306a36Sopenharmony_ci	    (msg->rx_buf && msg->rx_len > 0)) {
138662306a36Sopenharmony_ci		value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL);
138762306a36Sopenharmony_ci		value |= DSI_HOST_CONTROL_PKT_BTA;
138862306a36Sopenharmony_ci		tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
138962306a36Sopenharmony_ci	}
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	value = DSI_CONTROL_LANES(0) | DSI_CONTROL_HOST_ENABLE;
139262306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_CONTROL);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	/* write packet header, ECC is generated by hardware */
139562306a36Sopenharmony_ci	value = header[2] << 16 | header[1] << 8 | header[0];
139662306a36Sopenharmony_ci	tegra_dsi_writel(dsi, value, DSI_WR_DATA);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	/* write payload (if any) */
139962306a36Sopenharmony_ci	if (packet.payload_length > 0)
140062306a36Sopenharmony_ci		tegra_dsi_writesl(dsi, DSI_WR_DATA, packet.payload,
140162306a36Sopenharmony_ci				  packet.payload_length);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	err = tegra_dsi_transmit(dsi, 250);
140462306a36Sopenharmony_ci	if (err < 0)
140562306a36Sopenharmony_ci		return err;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) ||
140862306a36Sopenharmony_ci	    (msg->rx_buf && msg->rx_len > 0)) {
140962306a36Sopenharmony_ci		err = tegra_dsi_wait_for_response(dsi, 250);
141062306a36Sopenharmony_ci		if (err < 0)
141162306a36Sopenharmony_ci			return err;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci		count = err;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci		value = tegra_dsi_readl(dsi, DSI_RD_DATA);
141662306a36Sopenharmony_ci		switch (value) {
141762306a36Sopenharmony_ci		case 0x84:
141862306a36Sopenharmony_ci			/*
141962306a36Sopenharmony_ci			dev_dbg(dsi->dev, "ACK\n");
142062306a36Sopenharmony_ci			*/
142162306a36Sopenharmony_ci			break;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci		case 0x87:
142462306a36Sopenharmony_ci			/*
142562306a36Sopenharmony_ci			dev_dbg(dsi->dev, "ESCAPE\n");
142662306a36Sopenharmony_ci			*/
142762306a36Sopenharmony_ci			break;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci		default:
143062306a36Sopenharmony_ci			dev_err(dsi->dev, "unknown status: %08x\n", value);
143162306a36Sopenharmony_ci			break;
143262306a36Sopenharmony_ci		}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci		if (count > 1) {
143562306a36Sopenharmony_ci			err = tegra_dsi_read_response(dsi, msg, count);
143662306a36Sopenharmony_ci			if (err < 0)
143762306a36Sopenharmony_ci				dev_err(dsi->dev,
143862306a36Sopenharmony_ci					"failed to parse response: %zd\n",
143962306a36Sopenharmony_ci					err);
144062306a36Sopenharmony_ci			else {
144162306a36Sopenharmony_ci				/*
144262306a36Sopenharmony_ci				 * For read commands, return the number of
144362306a36Sopenharmony_ci				 * bytes returned by the peripheral.
144462306a36Sopenharmony_ci				 */
144562306a36Sopenharmony_ci				count = err;
144662306a36Sopenharmony_ci			}
144762306a36Sopenharmony_ci		}
144862306a36Sopenharmony_ci	} else {
144962306a36Sopenharmony_ci		/*
145062306a36Sopenharmony_ci		 * For write commands, we have transmitted the 4-byte header
145162306a36Sopenharmony_ci		 * plus the variable-length payload.
145262306a36Sopenharmony_ci		 */
145362306a36Sopenharmony_ci		count = 4 + packet.payload_length;
145462306a36Sopenharmony_ci	}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	return count;
145762306a36Sopenharmony_ci}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_cistatic int tegra_dsi_ganged_setup(struct tegra_dsi *dsi)
146062306a36Sopenharmony_ci{
146162306a36Sopenharmony_ci	struct clk *parent;
146262306a36Sopenharmony_ci	int err;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	/* make sure both DSI controllers share the same PLL */
146562306a36Sopenharmony_ci	parent = clk_get_parent(dsi->slave->clk);
146662306a36Sopenharmony_ci	if (!parent)
146762306a36Sopenharmony_ci		return -EINVAL;
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	err = clk_set_parent(parent, dsi->clk_parent);
147062306a36Sopenharmony_ci	if (err < 0)
147162306a36Sopenharmony_ci		return err;
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	return 0;
147462306a36Sopenharmony_ci}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_cistatic int tegra_dsi_host_attach(struct mipi_dsi_host *host,
147762306a36Sopenharmony_ci				 struct mipi_dsi_device *device)
147862306a36Sopenharmony_ci{
147962306a36Sopenharmony_ci	struct tegra_dsi *dsi = host_to_tegra(host);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	dsi->flags = device->mode_flags;
148262306a36Sopenharmony_ci	dsi->format = device->format;
148362306a36Sopenharmony_ci	dsi->lanes = device->lanes;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	if (dsi->slave) {
148662306a36Sopenharmony_ci		int err;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci		dev_dbg(dsi->dev, "attaching dual-channel device %s\n",
148962306a36Sopenharmony_ci			dev_name(&device->dev));
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci		err = tegra_dsi_ganged_setup(dsi);
149262306a36Sopenharmony_ci		if (err < 0) {
149362306a36Sopenharmony_ci			dev_err(dsi->dev, "failed to set up ganged mode: %d\n",
149462306a36Sopenharmony_ci				err);
149562306a36Sopenharmony_ci			return err;
149662306a36Sopenharmony_ci		}
149762306a36Sopenharmony_ci	}
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	/*
150062306a36Sopenharmony_ci	 * Slaves don't have a panel associated with them, so they provide
150162306a36Sopenharmony_ci	 * merely the second channel.
150262306a36Sopenharmony_ci	 */
150362306a36Sopenharmony_ci	if (!dsi->master) {
150462306a36Sopenharmony_ci		struct tegra_output *output = &dsi->output;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci		output->panel = of_drm_find_panel(device->dev.of_node);
150762306a36Sopenharmony_ci		if (IS_ERR(output->panel))
150862306a36Sopenharmony_ci			output->panel = NULL;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci		if (output->panel && output->connector.dev)
151162306a36Sopenharmony_ci			drm_helper_hpd_irq_event(output->connector.dev);
151262306a36Sopenharmony_ci	}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	return 0;
151562306a36Sopenharmony_ci}
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_cistatic int tegra_dsi_host_detach(struct mipi_dsi_host *host,
151862306a36Sopenharmony_ci				 struct mipi_dsi_device *device)
151962306a36Sopenharmony_ci{
152062306a36Sopenharmony_ci	struct tegra_dsi *dsi = host_to_tegra(host);
152162306a36Sopenharmony_ci	struct tegra_output *output = &dsi->output;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	if (output->panel && &device->dev == output->panel->dev) {
152462306a36Sopenharmony_ci		output->panel = NULL;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci		if (output->connector.dev)
152762306a36Sopenharmony_ci			drm_helper_hpd_irq_event(output->connector.dev);
152862306a36Sopenharmony_ci	}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	return 0;
153162306a36Sopenharmony_ci}
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_cistatic const struct mipi_dsi_host_ops tegra_dsi_host_ops = {
153462306a36Sopenharmony_ci	.attach = tegra_dsi_host_attach,
153562306a36Sopenharmony_ci	.detach = tegra_dsi_host_detach,
153662306a36Sopenharmony_ci	.transfer = tegra_dsi_host_transfer,
153762306a36Sopenharmony_ci};
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_cistatic int tegra_dsi_ganged_probe(struct tegra_dsi *dsi)
154062306a36Sopenharmony_ci{
154162306a36Sopenharmony_ci	struct device_node *np;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	np = of_parse_phandle(dsi->dev->of_node, "nvidia,ganged-mode", 0);
154462306a36Sopenharmony_ci	if (np) {
154562306a36Sopenharmony_ci		struct platform_device *gangster = of_find_device_by_node(np);
154662306a36Sopenharmony_ci		of_node_put(np);
154762306a36Sopenharmony_ci		if (!gangster)
154862306a36Sopenharmony_ci			return -EPROBE_DEFER;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci		dsi->slave = platform_get_drvdata(gangster);
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci		if (!dsi->slave) {
155362306a36Sopenharmony_ci			put_device(&gangster->dev);
155462306a36Sopenharmony_ci			return -EPROBE_DEFER;
155562306a36Sopenharmony_ci		}
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci		dsi->slave->master = dsi;
155862306a36Sopenharmony_ci	}
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	return 0;
156162306a36Sopenharmony_ci}
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_cistatic int tegra_dsi_probe(struct platform_device *pdev)
156462306a36Sopenharmony_ci{
156562306a36Sopenharmony_ci	struct tegra_dsi *dsi;
156662306a36Sopenharmony_ci	struct resource *regs;
156762306a36Sopenharmony_ci	int err;
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
157062306a36Sopenharmony_ci	if (!dsi)
157162306a36Sopenharmony_ci		return -ENOMEM;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	dsi->output.dev = dsi->dev = &pdev->dev;
157462306a36Sopenharmony_ci	dsi->video_fifo_depth = 1920;
157562306a36Sopenharmony_ci	dsi->host_fifo_depth = 64;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	err = tegra_dsi_ganged_probe(dsi);
157862306a36Sopenharmony_ci	if (err < 0)
157962306a36Sopenharmony_ci		return err;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	err = tegra_output_probe(&dsi->output);
158262306a36Sopenharmony_ci	if (err < 0)
158362306a36Sopenharmony_ci		return err;
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	dsi->output.connector.polled = DRM_CONNECTOR_POLL_HPD;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	/*
158862306a36Sopenharmony_ci	 * Assume these values by default. When a DSI peripheral driver
158962306a36Sopenharmony_ci	 * attaches to the DSI host, the parameters will be taken from
159062306a36Sopenharmony_ci	 * the attached device.
159162306a36Sopenharmony_ci	 */
159262306a36Sopenharmony_ci	dsi->flags = MIPI_DSI_MODE_VIDEO;
159362306a36Sopenharmony_ci	dsi->format = MIPI_DSI_FMT_RGB888;
159462306a36Sopenharmony_ci	dsi->lanes = 4;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	if (!pdev->dev.pm_domain) {
159762306a36Sopenharmony_ci		dsi->rst = devm_reset_control_get(&pdev->dev, "dsi");
159862306a36Sopenharmony_ci		if (IS_ERR(dsi->rst)) {
159962306a36Sopenharmony_ci			err = PTR_ERR(dsi->rst);
160062306a36Sopenharmony_ci			goto remove;
160162306a36Sopenharmony_ci		}
160262306a36Sopenharmony_ci	}
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	dsi->clk = devm_clk_get(&pdev->dev, NULL);
160562306a36Sopenharmony_ci	if (IS_ERR(dsi->clk)) {
160662306a36Sopenharmony_ci		err = dev_err_probe(&pdev->dev, PTR_ERR(dsi->clk),
160762306a36Sopenharmony_ci				    "cannot get DSI clock\n");
160862306a36Sopenharmony_ci		goto remove;
160962306a36Sopenharmony_ci	}
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	dsi->clk_lp = devm_clk_get(&pdev->dev, "lp");
161262306a36Sopenharmony_ci	if (IS_ERR(dsi->clk_lp)) {
161362306a36Sopenharmony_ci		err = dev_err_probe(&pdev->dev, PTR_ERR(dsi->clk_lp),
161462306a36Sopenharmony_ci				    "cannot get low-power clock\n");
161562306a36Sopenharmony_ci		goto remove;
161662306a36Sopenharmony_ci	}
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	dsi->clk_parent = devm_clk_get(&pdev->dev, "parent");
161962306a36Sopenharmony_ci	if (IS_ERR(dsi->clk_parent)) {
162062306a36Sopenharmony_ci		err = dev_err_probe(&pdev->dev, PTR_ERR(dsi->clk_parent),
162162306a36Sopenharmony_ci				    "cannot get parent clock\n");
162262306a36Sopenharmony_ci		goto remove;
162362306a36Sopenharmony_ci	}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	dsi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi");
162662306a36Sopenharmony_ci	if (IS_ERR(dsi->vdd)) {
162762306a36Sopenharmony_ci		err = dev_err_probe(&pdev->dev, PTR_ERR(dsi->vdd),
162862306a36Sopenharmony_ci				    "cannot get VDD supply\n");
162962306a36Sopenharmony_ci		goto remove;
163062306a36Sopenharmony_ci	}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	err = tegra_dsi_setup_clocks(dsi);
163362306a36Sopenharmony_ci	if (err < 0) {
163462306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot setup clocks\n");
163562306a36Sopenharmony_ci		goto remove;
163662306a36Sopenharmony_ci	}
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
163962306a36Sopenharmony_ci	dsi->regs = devm_ioremap_resource(&pdev->dev, regs);
164062306a36Sopenharmony_ci	if (IS_ERR(dsi->regs)) {
164162306a36Sopenharmony_ci		err = PTR_ERR(dsi->regs);
164262306a36Sopenharmony_ci		goto remove;
164362306a36Sopenharmony_ci	}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	dsi->mipi = tegra_mipi_request(&pdev->dev, pdev->dev.of_node);
164662306a36Sopenharmony_ci	if (IS_ERR(dsi->mipi)) {
164762306a36Sopenharmony_ci		err = PTR_ERR(dsi->mipi);
164862306a36Sopenharmony_ci		goto remove;
164962306a36Sopenharmony_ci	}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	dsi->host.ops = &tegra_dsi_host_ops;
165262306a36Sopenharmony_ci	dsi->host.dev = &pdev->dev;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	err = mipi_dsi_host_register(&dsi->host);
165562306a36Sopenharmony_ci	if (err < 0) {
165662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register DSI host: %d\n", err);
165762306a36Sopenharmony_ci		goto mipi_free;
165862306a36Sopenharmony_ci	}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	platform_set_drvdata(pdev, dsi);
166162306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	INIT_LIST_HEAD(&dsi->client.list);
166462306a36Sopenharmony_ci	dsi->client.ops = &dsi_client_ops;
166562306a36Sopenharmony_ci	dsi->client.dev = &pdev->dev;
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	err = host1x_client_register(&dsi->client);
166862306a36Sopenharmony_ci	if (err < 0) {
166962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
167062306a36Sopenharmony_ci			err);
167162306a36Sopenharmony_ci		goto unregister;
167262306a36Sopenharmony_ci	}
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	return 0;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ciunregister:
167762306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
167862306a36Sopenharmony_ci	mipi_dsi_host_unregister(&dsi->host);
167962306a36Sopenharmony_cimipi_free:
168062306a36Sopenharmony_ci	tegra_mipi_free(dsi->mipi);
168162306a36Sopenharmony_ciremove:
168262306a36Sopenharmony_ci	tegra_output_remove(&dsi->output);
168362306a36Sopenharmony_ci	return err;
168462306a36Sopenharmony_ci}
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_cistatic void tegra_dsi_remove(struct platform_device *pdev)
168762306a36Sopenharmony_ci{
168862306a36Sopenharmony_ci	struct tegra_dsi *dsi = platform_get_drvdata(pdev);
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	host1x_client_unregister(&dsi->client);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	tegra_output_remove(&dsi->output);
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	mipi_dsi_host_unregister(&dsi->host);
169762306a36Sopenharmony_ci	tegra_mipi_free(dsi->mipi);
169862306a36Sopenharmony_ci}
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_cistatic const struct of_device_id tegra_dsi_of_match[] = {
170162306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra210-dsi", },
170262306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra132-dsi", },
170362306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra124-dsi", },
170462306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra114-dsi", },
170562306a36Sopenharmony_ci	{ },
170662306a36Sopenharmony_ci};
170762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_dsi_of_match);
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_cistruct platform_driver tegra_dsi_driver = {
171062306a36Sopenharmony_ci	.driver = {
171162306a36Sopenharmony_ci		.name = "tegra-dsi",
171262306a36Sopenharmony_ci		.of_match_table = tegra_dsi_of_match,
171362306a36Sopenharmony_ci	},
171462306a36Sopenharmony_ci	.probe = tegra_dsi_probe,
171562306a36Sopenharmony_ci	.remove_new = tegra_dsi_remove,
171662306a36Sopenharmony_ci};
1717