• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <types.h>
4 #include <console/console.h>
5 #include <device/mmio.h>
6 #include <delay.h>
7 #include <soc/addressmap.h>
8 #include <soc/clock.h>
9 #include <device/device.h>
10 #include <soc/nvidia/tegra/types.h>
11 #include <soc/display.h>
12 #include <soc/mipi_dsi.h>
13 #include <soc/tegra_dsi.h>
14 
15 #include "jdi_25x18_display/panel-jdi-lpm102a188a.h"
16 
17 static unsigned long dsi_pads[] = {
18 	0x0c0, /* DSIA channel A & B pads */
19 	0x300, /* DSIB channel A & B pads */
20 };
21 
22 static struct tegra_mipi mipi_data = {
23 	.regs = (void *)TEGRA_MIPI_CAL_BASE,
24 };
25 
tegra_mipi_readl(struct tegra_mipi * mipi,unsigned long reg)26 static inline unsigned long tegra_mipi_readl(struct tegra_mipi *mipi,
27 					     unsigned long reg)
28 {
29 	return read32(mipi->regs + (reg << 2));
30 }
31 
tegra_mipi_writel(struct tegra_mipi * mipi,unsigned long value,unsigned long reg)32 static inline void tegra_mipi_writel(struct tegra_mipi *mipi,
33 				     unsigned long value, unsigned long reg)
34 {
35 	write32(mipi->regs + (reg << 2), value);
36 }
37 
38 static const struct tegra_mipi_pad tegra210_mipi_pads[] = {
39 	{ .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
40 	{ .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
41 	{ .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
42 	{ .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
43 	{ .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
44 	{ .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
45 	{ .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
46 	{ .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
47 	{ .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
48 	{ .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
49 };
50 
51 static const struct tegra_mipi_soc tegra210_mipi_soc = {
52 	.has_clk_lane = 1,
53 	.pads = tegra210_mipi_pads,
54 	.num_pads = ARRAY_SIZE(tegra210_mipi_pads),
55 	.clock_enable_override = 1,
56 	.needs_vclamp_ref = 0,
57 	.pad_drive_down_ref = 0x0,
58 	.pad_drive_up_ref = 0x3,
59 	.pad_vclamp_level = 0x1,
60 	.pad_vauxp_level = 0x1,
61 	.hspdos = 0x0,
62 	.hspuos = 0x2,
63 	.termos = 0x0,
64 	.hsclkpdos = 0x0,
65 	.hsclkpuos = 0x2,
66 };
67 
tegra_mipi_request(struct tegra_mipi_device * device,int device_index)68 struct tegra_mipi_device *tegra_mipi_request(struct tegra_mipi_device *device,
69 						int device_index)
70 {
71 	device->mipi = &mipi_data;
72 	device->mipi->soc = &tegra210_mipi_soc;
73 	device->pads = dsi_pads[device_index];
74 
75 	return device;
76 }
77 
tegra_mipi_wait(struct tegra_mipi * mipi)78 static int tegra_mipi_wait(struct tegra_mipi *mipi)
79 {
80 	u32 poll_interval_us = 1000;
81 	u32 timeout_us = 250 * 1000;
82 	unsigned long value;
83 
84 	do {
85 		value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
86 		if ((value & MIPI_CAL_STATUS_ACTIVE) == 0 &&
87 		    (value & MIPI_CAL_STATUS_DONE) != 0)
88 			return 0;
89 
90 		if (timeout_us > poll_interval_us)
91 			timeout_us -= poll_interval_us;
92 		else
93 			break;
94 
95 		udelay(poll_interval_us);
96 	} while (1);
97 
98 	printk(BIOS_ERR, "%s: ERROR: timeout\n", __func__);
99 	return -ETIMEDOUT;
100 }
101 
tegra_mipi_calibrate(struct tegra_mipi_device * device)102 int tegra_mipi_calibrate(struct tegra_mipi_device *device)
103 {
104 	const struct tegra_mipi_soc *soc = device->mipi->soc;
105 	unsigned int i;
106 	u32 value;
107 	int err;
108 
109 	value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG0);
110 	value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
111 
112 	if (soc->needs_vclamp_ref)
113 		value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
114 
115 	tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
116 
117 	value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
118 		MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
119 	tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
120 
121 	value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
122 	value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
123 	tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
124 
125 	value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
126 	value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
127 	value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
128 	value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
129 	value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
130 	tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
131 
132 	for (i = 0; i < soc->num_pads; i++) {
133 		u32 clk = 0, data = 0;
134 
135 		if (device->pads & BIT(i)) {
136 			data = MIPI_CAL_CONFIG_SELECT |
137 			       MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
138 			       MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
139 			       MIPI_CAL_CONFIG_TERMOS(soc->termos);
140 			clk = MIPI_CAL_CONFIG_SELECT |
141 			      MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
142 			      MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
143 		}
144 
145 		tegra_mipi_writel(device->mipi, data, soc->pads[i].data);
146 
147 		if (soc->has_clk_lane && soc->pads[i].clk != 0)
148 			tegra_mipi_writel(device->mipi, clk, soc->pads[i].clk);
149 	}
150 
151 	value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
152 	value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
153 	value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
154 	value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
155 	value |= MIPI_CAL_CTRL_PRESCALE(0x2);
156 
157 	if (!soc->clock_enable_override)
158 		value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
159 	else
160 		value |= MIPI_CAL_CTRL_CLKEN_OVR;
161 
162 	tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
163 
164 	/* clear any pending status bits */
165 	value = tegra_mipi_readl(device->mipi, MIPI_CAL_STATUS);
166 	tegra_mipi_writel(device->mipi, value, MIPI_CAL_STATUS);
167 
168 	value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
169 	value |= MIPI_CAL_CTRL_START;
170 	tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
171 
172 	err = tegra_mipi_wait(device->mipi);
173 	if (err < 0)
174 		printk(BIOS_ERR, "failed to calibrate MIPI pads: %d\n", err);
175 	else
176 		printk(BIOS_INFO, "MIPI calibration done\n");
177 
178 	value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG0);
179 
180 	if (soc->needs_vclamp_ref)
181 		value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
182 
183 	value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
184 	tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
185 
186 	return err;
187 }
188