Angel Pons | a2ee761 | 2020-04-04 18:51:15 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Elyes HAOUAS | f765d4f | 2018-10-27 20:14:59 +0200 | [diff] [blame] | 2 | |
| 3 | #include <types.h> |
Patrick Georgi | 40a3e32 | 2015-06-22 19:41:29 +0200 | [diff] [blame] | 4 | #include <console/console.h> |
Kyösti Mälkki | 13f6650 | 2019-03-03 08:01:05 +0200 | [diff] [blame] | 5 | #include <device/mmio.h> |
Patrick Georgi | 40a3e32 | 2015-06-22 19:41:29 +0200 | [diff] [blame] | 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> |
Elyes HAOUAS | 3081855 | 2019-06-23 07:03:59 +0200 | [diff] [blame] | 14 | |
Patrick Georgi | 40a3e32 | 2015-06-22 19:41:29 +0200 | [diff] [blame] | 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | } |