Angel Pons | bbc99cf | 2020-04-04 18:51:23 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 2 | |
Kyösti Mälkki | 13f6650 | 2019-03-03 08:01:05 +0200 | [diff] [blame] | 3 | #include <device/mmio.h> |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 4 | #include <console/console.h> |
| 5 | #include <delay.h> |
| 6 | #include <device/device.h> |
| 7 | #include <edid.h> |
| 8 | #include <gpio.h> |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 9 | #include <string.h> |
| 10 | #include <soc/addressmap.h> |
| 11 | #include <soc/clock.h> |
| 12 | #include <soc/display.h> |
| 13 | #include <soc/mipi.h> |
| 14 | #include <soc/soc.h> |
Elyes HAOUAS | 27d02d8 | 2019-05-15 21:11:39 +0200 | [diff] [blame] | 15 | #include <types.h> |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 16 | #include <timer.h> |
| 17 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 18 | static struct rk_mipi_dsi rk_mipi[2] = { |
| 19 | { .mipi_regs = (void *)MIPI0_BASE}, |
| 20 | { .mipi_regs = (void *)MIPI1_BASE} |
| 21 | }; |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 22 | |
| 23 | /* |
| 24 | * The controller should generate 2 frames before |
| 25 | * preparing the peripheral. |
| 26 | */ |
| 27 | static void rk_mipi_dsi_wait_for_two_frames(struct rk_mipi_dsi *dsi, |
| 28 | const struct edid *edid) |
| 29 | { |
| 30 | int two_frames; |
| 31 | unsigned int refresh = edid->mode.refresh; |
| 32 | |
Elyes HAOUAS | 6df3b64 | 2018-11-26 22:53:49 +0100 | [diff] [blame] | 33 | two_frames = DIV_ROUND_UP(MSECS_PER_SEC * 2, refresh); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 34 | mdelay(two_frames); |
| 35 | } |
| 36 | |
Lin Huang | 2e3ebb6 | 2017-10-30 17:40:19 +0800 | [diff] [blame] | 37 | static const struct dphy_pll_parameter_map dppa_map[] = { |
| 38 | { 89, 0x00, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM}, |
| 39 | { 99, 0x10, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM}, |
| 40 | { 109, 0x20, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM}, |
| 41 | { 129, 0x01, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM}, |
| 42 | { 139, 0x11, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM}, |
| 43 | { 149, 0x21, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM}, |
| 44 | { 169, 0x02, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM}, |
| 45 | { 179, 0x12, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM}, |
| 46 | { 199, 0x22, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM}, |
| 47 | { 219, 0x03, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM}, |
| 48 | { 239, 0x13, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM}, |
| 49 | { 249, 0x23, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM}, |
| 50 | { 269, 0x04, CP_CURRENT_6UA, LPF_RESISTORS_11_5KOHM}, |
| 51 | { 299, 0x14, CP_CURRENT_6UA, LPF_RESISTORS_11_5KOHM}, |
| 52 | { 329, 0x05, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM}, |
| 53 | { 359, 0x15, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM}, |
| 54 | { 399, 0x25, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM}, |
| 55 | { 449, 0x06, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM}, |
| 56 | { 499, 0x16, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM}, |
| 57 | { 549, 0x07, CP_CURRENT_7_5UA, LPF_RESISTORS_10_5KOHM}, |
| 58 | { 599, 0x17, CP_CURRENT_7_5UA, LPF_RESISTORS_10_5KOHM}, |
| 59 | { 649, 0x08, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM}, |
| 60 | { 699, 0x18, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM}, |
| 61 | { 749, 0x09, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM}, |
| 62 | { 799, 0x19, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM}, |
| 63 | { 849, 0x29, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM}, |
| 64 | { 899, 0x39, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM}, |
| 65 | { 949, 0x0a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM}, |
| 66 | { 999, 0x1a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM}, |
| 67 | {1049, 0x2a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM}, |
| 68 | {1099, 0x3a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM}, |
| 69 | {1149, 0x0b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM}, |
| 70 | {1199, 0x1b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM}, |
| 71 | {1249, 0x2b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM}, |
| 72 | {1299, 0x3b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM}, |
| 73 | {1349, 0x0c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM}, |
| 74 | {1399, 0x1c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM}, |
| 75 | {1449, 0x2c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM}, |
| 76 | {1500, 0x3c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM} |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 77 | }; |
| 78 | |
Lin Huang | 2e3ebb6 | 2017-10-30 17:40:19 +0800 | [diff] [blame] | 79 | static int max_mbps_to_parameter(unsigned int max_mbps) |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 80 | { |
| 81 | int i; |
| 82 | |
Lin Huang | 2e3ebb6 | 2017-10-30 17:40:19 +0800 | [diff] [blame] | 83 | for (i = 0; i < ARRAY_SIZE(dppa_map); i++) { |
| 84 | if (dppa_map[i].max_mbps >= max_mbps) |
| 85 | return i; |
| 86 | } |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 87 | |
| 88 | return -1; |
| 89 | } |
| 90 | |
| 91 | static void rk_mipi_dsi_phy_write(struct rk_mipi_dsi *dsi, |
| 92 | u8 test_code, |
| 93 | u8 test_data) |
| 94 | { |
| 95 | /* |
| 96 | * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content |
| 97 | * is latched internally as the current test code. Test data is |
| 98 | * programmed internally by rising edge on TESTCLK. |
| 99 | */ |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 100 | write32(&dsi->mipi_regs->dsi_phy_tst_ctrl0, |
| 101 | PHY_TESTCLK | PHY_UNTESTCLR); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 102 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 103 | write32(&dsi->mipi_regs->dsi_phy_tst_ctrl1, |
| 104 | PHY_TESTEN | PHY_TESTDOUT(0) | PHY_TESTDIN(test_code)); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 105 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 106 | write32(&dsi->mipi_regs->dsi_phy_tst_ctrl0, |
| 107 | PHY_UNTESTCLK | PHY_UNTESTCLR); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 108 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 109 | write32(&dsi->mipi_regs->dsi_phy_tst_ctrl1, |
| 110 | PHY_UNTESTEN | PHY_TESTDOUT(0) | PHY_TESTDIN(test_data)); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 111 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 112 | write32(&dsi->mipi_regs->dsi_phy_tst_ctrl0, |
| 113 | PHY_TESTCLK | PHY_UNTESTCLR); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 114 | } |
| 115 | |
Lin Huang | 45f1b01 | 2017-11-02 17:53:30 +0800 | [diff] [blame] | 116 | /* bytes_per_ns - Nanoseconds to byte clock cycles */ |
| 117 | static inline unsigned int bytes_per_ns(struct rk_mipi_dsi *dsi, int ns) |
| 118 | { |
| 119 | return DIV_ROUND_UP((u64)ns * dsi->lane_bps, (u64)8 * NSECS_PER_SEC); |
| 120 | } |
| 121 | |
| 122 | /* bits_per_ns - Nanoseconds to bit time periods */ |
| 123 | static inline unsigned int bits_per_ns(struct rk_mipi_dsi *dsi, int ns) |
| 124 | { |
| 125 | return DIV_ROUND_UP((u64)ns * dsi->lane_bps, NSECS_PER_SEC); |
| 126 | } |
| 127 | |
| 128 | static int rk_mipi_dsi_wait_phy_lock(struct rk_mipi_dsi *dsi) |
| 129 | { |
| 130 | struct stopwatch sw; |
| 131 | int val; |
| 132 | |
| 133 | stopwatch_init_msecs_expire(&sw, 20); |
| 134 | do { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 135 | val = read32(&dsi->mipi_regs->dsi_phy_status); |
Lin Huang | 45f1b01 | 2017-11-02 17:53:30 +0800 | [diff] [blame] | 136 | if (val & LOCK) |
| 137 | return 0; |
| 138 | } while (!stopwatch_expired(&sw)); |
| 139 | |
| 140 | return -1; |
| 141 | } |
| 142 | |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 143 | static int rk_mipi_dsi_phy_init(struct rk_mipi_dsi *dsi) |
| 144 | { |
Lin Huang | 45f1b01 | 2017-11-02 17:53:30 +0800 | [diff] [blame] | 145 | int i, vco, val; |
Elyes HAOUAS | 6df3b64 | 2018-11-26 22:53:49 +0100 | [diff] [blame] | 146 | int lane_mbps = DIV_ROUND_UP(dsi->lane_bps, USECS_PER_SEC); |
Lin Huang | 45f1b01 | 2017-11-02 17:53:30 +0800 | [diff] [blame] | 147 | struct stopwatch sw; |
Lin Huang | 2e3ebb6 | 2017-10-30 17:40:19 +0800 | [diff] [blame] | 148 | |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 149 | vco = (lane_mbps < 200) ? 0 : (lane_mbps + 100) / 200; |
| 150 | |
Lin Huang | 2e3ebb6 | 2017-10-30 17:40:19 +0800 | [diff] [blame] | 151 | i = max_mbps_to_parameter(lane_mbps); |
| 152 | if (i < 0) { |
| 153 | printk(BIOS_DEBUG, |
| 154 | "failed to get parameter for %dmbps clock\n", lane_mbps); |
| 155 | return i; |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 156 | } |
| 157 | |
| 158 | /* Start by clearing PHY state */ |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 159 | write32(&dsi->mipi_regs->dsi_phy_tst_ctrl0, PHY_UNTESTCLR); |
| 160 | write32(&dsi->mipi_regs->dsi_phy_tst_ctrl0, PHY_TESTCLR); |
| 161 | write32(&dsi->mipi_regs->dsi_phy_tst_ctrl0, PHY_UNTESTCLR); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 162 | |
| 163 | rk_mipi_dsi_phy_write(dsi, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL, |
| 164 | BYPASS_VCO_RANGE | |
| 165 | VCO_RANGE_CON_SEL(vco) | |
| 166 | VCO_IN_CAP_CON_LOW | |
| 167 | REF_BIAS_CUR_SEL); |
| 168 | |
| 169 | rk_mipi_dsi_phy_write(dsi, PLL_CP_CONTROL_PLL_LOCK_BYPASS, |
Lin Huang | 2e3ebb6 | 2017-10-30 17:40:19 +0800 | [diff] [blame] | 170 | CP_CURRENT_SEL(dppa_map[i].icpctrl)); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 171 | rk_mipi_dsi_phy_write(dsi, PLL_LPF_AND_CP_CONTROL, |
| 172 | CP_PROGRAM_EN | |
| 173 | LPF_PROGRAM_EN | |
Lin Huang | 2e3ebb6 | 2017-10-30 17:40:19 +0800 | [diff] [blame] | 174 | LPF_RESISTORS_SEL(dppa_map[i].lpfctrl)); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 175 | rk_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0, |
Lin Huang | 2e3ebb6 | 2017-10-30 17:40:19 +0800 | [diff] [blame] | 176 | HSFREQRANGE_SEL(dppa_map[i].hsfreqrange)); |
| 177 | |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 178 | rk_mipi_dsi_phy_write(dsi, PLL_INPUT_DIVIDER_RATIO, |
| 179 | INPUT_DIVIDER(dsi->input_div)); |
| 180 | rk_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO, |
| 181 | LOOP_DIV_LOW_SEL(dsi->feedback_div) | |
| 182 | LOW_PROGRAM_EN); |
Lin Huang | f4acb92 | 2017-11-16 10:03:47 +0800 | [diff] [blame] | 183 | |
| 184 | /* |
| 185 | * we need set divider control register immediately to make |
| 186 | * the configured LSB effective according to IP simulation |
| 187 | * and lab test results. Only in this way can we get correct |
| 188 | * mipi phy pll frequency. |
| 189 | */ |
| 190 | rk_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, |
| 191 | PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 192 | rk_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO, |
| 193 | LOOP_DIV_HIGH_SEL(dsi->feedback_div) | |
| 194 | HIGH_PROGRAM_EN); |
| 195 | rk_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, |
| 196 | PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN); |
| 197 | rk_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, |
| 198 | LOW_PROGRAM_EN | |
| 199 | BIASEXTR_SEL(BIASEXTR_127_7)); |
| 200 | rk_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, |
| 201 | HIGH_PROGRAM_EN | |
| 202 | BANDGAP_SEL(BANDGAP_96_10)); |
| 203 | rk_mipi_dsi_phy_write(dsi, BANDGAP_AND_BIAS_CONTROL, |
| 204 | POWER_CONTROL | INTERNAL_REG_CURRENT | |
| 205 | BIAS_BLOCK_ON | BANDGAP_ON); |
| 206 | rk_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL, |
| 207 | TER_RESISTOR_LOW | TER_CAL_DONE | |
| 208 | SETRD_MAX | TER_RESISTORS_ON); |
| 209 | rk_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL, |
| 210 | TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON | |
| 211 | SETRD_MAX | POWER_MANAGE | |
| 212 | TER_RESISTORS_ON); |
Lin Huang | 45f1b01 | 2017-11-02 17:53:30 +0800 | [diff] [blame] | 213 | rk_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL, |
| 214 | TLP_PROGRAM_EN | bytes_per_ns(dsi, 500)); |
| 215 | rk_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL, |
| 216 | THS_PRE_PROGRAM_EN | bits_per_ns(dsi, 40)); |
| 217 | rk_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL, |
| 218 | THS_ZERO_PROGRAM_EN | bytes_per_ns(dsi, 300)); |
| 219 | rk_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL, |
| 220 | THS_PRE_PROGRAM_EN | bits_per_ns(dsi, 100)); |
| 221 | rk_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL, |
| 222 | BIT(5) | bytes_per_ns(dsi, 100)); |
| 223 | rk_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_POST_TIME_CONTROL, |
| 224 | BIT(5) | (bytes_per_ns(dsi, 60) + 7)); |
| 225 | rk_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL, |
| 226 | TLP_PROGRAM_EN | bytes_per_ns(dsi, 500)); |
| 227 | rk_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL, |
| 228 | THS_PRE_PROGRAM_EN | (bits_per_ns(dsi, 50) + 5)); |
| 229 | rk_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL, |
| 230 | THS_ZERO_PROGRAM_EN | |
| 231 | (bytes_per_ns(dsi, 140) + 2)); |
| 232 | rk_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL, |
| 233 | THS_PRE_PROGRAM_EN | (bits_per_ns(dsi, 60) + 8)); |
| 234 | rk_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL, |
| 235 | BIT(5) | bytes_per_ns(dsi, 100)); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 236 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 237 | write32(&dsi->mipi_regs->dsi_phy_rstz, |
| 238 | PHY_ENFORCEPLL | PHY_ENABLECLK | |
| 239 | PHY_UNRSTZ | PHY_UNSHUTDOWNZ); |
Lin Huang | 45f1b01 | 2017-11-02 17:53:30 +0800 | [diff] [blame] | 240 | |
| 241 | if (rk_mipi_dsi_wait_phy_lock(dsi)) { |
| 242 | printk(BIOS_ERR, "failed to wait for phy lock state\n"); |
| 243 | return -1; |
| 244 | } |
| 245 | |
| 246 | stopwatch_init_msecs_expire(&sw, 20); |
| 247 | do { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 248 | val = read32(&dsi->mipi_regs->dsi_phy_status); |
Lin Huang | 45f1b01 | 2017-11-02 17:53:30 +0800 | [diff] [blame] | 249 | if (val & STOP_STATE_CLK_LANE) |
| 250 | return 0; |
| 251 | } while (!stopwatch_expired(&sw)); |
| 252 | |
| 253 | printk(BIOS_ERR, "failed to wait for phy clk lane stop state"); |
| 254 | return -1; |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 255 | } |
| 256 | |
| 257 | static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt) |
| 258 | { |
| 259 | switch (fmt) { |
| 260 | case MIPI_DSI_FMT_RGB888: |
| 261 | case MIPI_DSI_FMT_RGB666: |
| 262 | return 24; |
| 263 | |
| 264 | case MIPI_DSI_FMT_RGB666_PACKED: |
| 265 | return 18; |
| 266 | |
| 267 | case MIPI_DSI_FMT_RGB565: |
| 268 | return 16; |
| 269 | } |
| 270 | |
| 271 | return -1; |
| 272 | } |
| 273 | |
| 274 | static int rk_mipi_dsi_get_lane_bps(struct rk_mipi_dsi *dsi, |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 275 | const struct edid *edid, |
| 276 | const struct mipi_panel_data *panel_data) |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 277 | { |
Lin Huang | f4acb92 | 2017-11-16 10:03:47 +0800 | [diff] [blame] | 278 | u64 pclk, target_bps; |
Lin Huang | 2e3ebb6 | 2017-10-30 17:40:19 +0800 | [diff] [blame] | 279 | u32 max_bps = dppa_map[ARRAY_SIZE(dppa_map) - 1].max_mbps * MHz; |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 280 | int bpp; |
Lin Huang | f4acb92 | 2017-11-16 10:03:47 +0800 | [diff] [blame] | 281 | u64 best_freq = 0; |
| 282 | u64 fvco_min, fvco_max, fref; |
| 283 | u32 min_prediv, max_prediv; |
| 284 | u32 prediv, best_prediv; |
| 285 | u64 fbdiv, best_fbdiv; |
| 286 | u32 min_delta; |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 287 | |
| 288 | bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); |
| 289 | if (bpp < 0) { |
| 290 | printk(BIOS_DEBUG, "failed to get bpp for pixel format %d\n", |
| 291 | dsi->format); |
| 292 | return bpp; |
| 293 | } |
Jacob Garber | e24585c | 2019-07-23 11:55:04 -0600 | [diff] [blame] | 294 | pclk = (u64)edid->mode.pixel_clock * MSECS_PER_SEC; |
Lin Huang | f4acb92 | 2017-11-16 10:03:47 +0800 | [diff] [blame] | 295 | |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 296 | /* take 1 / 0.8, since mbps must bigger than bandwidth of RGB */ |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 297 | target_bps = pclk / panel_data->lanes * bpp / 8 * 10; |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 298 | if (target_bps >= max_bps) { |
| 299 | printk(BIOS_DEBUG, "DPHY clock frequency is out of range\n"); |
| 300 | return -1; |
| 301 | } |
Lin Huang | f4acb92 | 2017-11-16 10:03:47 +0800 | [diff] [blame] | 302 | |
| 303 | fref = OSC_HZ; |
| 304 | |
| 305 | /* constraint: 5Mhz <= Fref / N <= 40MHz */ |
Elyes HAOUAS | 6df3b64 | 2018-11-26 22:53:49 +0100 | [diff] [blame] | 306 | min_prediv = DIV_ROUND_UP(fref, 40 * MHz); |
Lin Huang | f4acb92 | 2017-11-16 10:03:47 +0800 | [diff] [blame] | 307 | max_prediv = fref / (5 * MHz); |
| 308 | |
Elyes HAOUAS | 8d1b0f1 | 2020-02-20 18:20:57 +0100 | [diff] [blame] | 309 | /* constraint: 80MHz <= Fvco <= 1500MHz */ |
Lin Huang | f4acb92 | 2017-11-16 10:03:47 +0800 | [diff] [blame] | 310 | fvco_min = 80 * MHz; |
| 311 | fvco_max = 1500 * MHz; |
| 312 | min_delta = 1500 * MHz; |
| 313 | |
| 314 | for (prediv = min_prediv; prediv <= max_prediv; prediv++) { |
| 315 | u64 freq; |
| 316 | int delta; |
| 317 | |
| 318 | /* Fvco = Fref * M / N */ |
| 319 | fbdiv = target_bps * prediv / fref; |
| 320 | |
| 321 | /* |
| 322 | * Due to the use of a "by 2 pre-scaler", the range of the |
| 323 | * feedback multiplication value M is limited to even division |
| 324 | * numbers, and m must be in 6 <= m <= 512. |
| 325 | */ |
| 326 | fbdiv += fbdiv % 2; |
| 327 | if (fbdiv < 6 || fbdiv > 512) |
| 328 | continue; |
| 329 | |
| 330 | freq = (u64)fbdiv * fref / prediv; |
| 331 | if (freq < fvco_min || freq > fvco_max) |
| 332 | continue; |
| 333 | |
| 334 | delta = target_bps - freq; |
| 335 | delta = ABS(delta); |
| 336 | if (delta >= min_delta) |
| 337 | continue; |
| 338 | |
| 339 | best_prediv = prediv; |
| 340 | best_fbdiv = fbdiv; |
| 341 | min_delta = delta; |
| 342 | best_freq = freq; |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 343 | } |
Lin Huang | f4acb92 | 2017-11-16 10:03:47 +0800 | [diff] [blame] | 344 | |
| 345 | if (best_freq) { |
| 346 | dsi->lane_bps = best_freq; |
| 347 | dsi->input_div = best_prediv; |
| 348 | dsi->feedback_div = best_fbdiv; |
| 349 | } else { |
| 350 | printk(BIOS_ERR, "Can not find best_freq for DPHY\n"); |
| 351 | return -1; |
| 352 | } |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 353 | |
| 354 | return 0; |
| 355 | } |
| 356 | |
| 357 | static void rk_mipi_dsi_dpi_config(struct rk_mipi_dsi *dsi) |
| 358 | { |
| 359 | u32 color = 0; |
| 360 | |
| 361 | switch (dsi->format) { |
| 362 | case MIPI_DSI_FMT_RGB888: |
| 363 | color = DPI_COLOR_CODING_24BIT; |
| 364 | break; |
| 365 | case MIPI_DSI_FMT_RGB666: |
| 366 | color = DPI_COLOR_CODING_18BIT_2 | EN18_LOOSELY; |
| 367 | break; |
| 368 | case MIPI_DSI_FMT_RGB666_PACKED: |
| 369 | color = DPI_COLOR_CODING_18BIT_1; |
| 370 | break; |
| 371 | case MIPI_DSI_FMT_RGB565: |
| 372 | color = DPI_COLOR_CODING_16BIT_1; |
| 373 | break; |
| 374 | } |
| 375 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 376 | write32(&dsi->mipi_regs->dsi_dpi_vcid, 0); |
| 377 | write32(&dsi->mipi_regs->dsi_dpi_color_coding, color); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 378 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 379 | write32(&dsi->mipi_regs->dsi_dpi_cfg_pol, 0); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 380 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 381 | write32(&dsi->mipi_regs->dsi_dpi_lp_cmd_tim, |
| 382 | OUTVACT_LPCMD_TIME(4) | INVACT_LPCMD_TIME(4)); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 383 | } |
| 384 | |
| 385 | static void rk_mipi_dsi_packet_handler_config(struct rk_mipi_dsi *dsi) |
| 386 | { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 387 | write32(&dsi->mipi_regs->dsi_pckhdl_cfg, |
| 388 | EN_CRC_RX | EN_ECC_RX | EN_BTA); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 389 | } |
| 390 | |
| 391 | static void rk_mipi_dsi_video_mode_config(struct rk_mipi_dsi *dsi) |
| 392 | { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 393 | write32(&dsi->mipi_regs->dsi_vid_mode_cfg, |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 394 | VID_MODE_TYPE_BURST_SYNC_PULSES | ENABLE_LOW_POWER); |
| 395 | } |
| 396 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 397 | static void rk_mipi_dsi_video_packet_config(struct rk_mipi_dsi *dsi, |
| 398 | const struct edid *edid, |
| 399 | const struct mipi_panel_data *panel_data) |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 400 | { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 401 | int pkt_size; |
| 402 | |
| 403 | if (panel_data->mipi_num > 1) |
| 404 | pkt_size = VID_PKT_SIZE(edid->mode.ha / 2 + 4); |
| 405 | else |
| 406 | pkt_size = VID_PKT_SIZE(edid->mode.ha); |
| 407 | |
| 408 | write32(&dsi->mipi_regs->dsi_vid_pkt_size, pkt_size); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 409 | } |
| 410 | |
| 411 | static void rk_mipi_dsi_command_mode_config(struct rk_mipi_dsi *dsi) |
| 412 | { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 413 | write32(&dsi->mipi_regs->dsi_to_cnt_cfg, |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 414 | HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000)); |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 415 | write32(&dsi->mipi_regs->dsi_bta_to_cnt, 0xd00); |
| 416 | write32(&dsi->mipi_regs->dsi_cmd_mode_cfg, CMD_MODE_ALL_LP); |
| 417 | write32(&dsi->mipi_regs->dsi_mode_cfg, ENABLE_CMD_MODE); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 418 | } |
| 419 | |
| 420 | /* Get lane byte clock cycles. */ |
| 421 | static u32 rk_mipi_dsi_get_hcomponent_lbcc(struct rk_mipi_dsi *dsi, |
| 422 | u32 hcomponent, |
| 423 | const struct edid *edid) |
| 424 | { |
| 425 | u32 lbcc; |
| 426 | u64 lbcc_tmp; |
| 427 | |
| 428 | lbcc_tmp = hcomponent * dsi->lane_bps / (8 * MSECS_PER_SEC); |
Elyes HAOUAS | 6df3b64 | 2018-11-26 22:53:49 +0100 | [diff] [blame] | 429 | lbcc = DIV_ROUND_UP(lbcc_tmp, edid->mode.pixel_clock); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 430 | |
| 431 | return lbcc; |
| 432 | } |
| 433 | |
| 434 | static void rk_mipi_dsi_line_timer_config(struct rk_mipi_dsi *dsi, |
| 435 | const struct edid *edid) |
| 436 | { |
| 437 | u32 htotal, hsa, hbp, lbcc; |
| 438 | |
| 439 | htotal = edid->mode.ha + edid->mode.hbl; |
| 440 | hsa = edid->mode.hspw; |
| 441 | hbp = edid->mode.hbl - edid->mode.hso - edid->mode.hspw; |
| 442 | |
| 443 | lbcc = rk_mipi_dsi_get_hcomponent_lbcc(dsi, htotal, edid); |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 444 | write32(&dsi->mipi_regs->dsi_vid_hline_time, lbcc); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 445 | |
| 446 | lbcc = rk_mipi_dsi_get_hcomponent_lbcc(dsi, hsa, edid); |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 447 | write32(&dsi->mipi_regs->dsi_vid_hsa_time, lbcc); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 448 | lbcc = rk_mipi_dsi_get_hcomponent_lbcc(dsi, hbp, edid); |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 449 | write32(&dsi->mipi_regs->dsi_vid_hbp_time, lbcc); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 450 | } |
| 451 | |
| 452 | static void rk_mipi_dsi_vertical_timing_config(struct rk_mipi_dsi *dsi, |
| 453 | const struct edid *edid) |
| 454 | { |
| 455 | u32 vactive, vsa, vfp, vbp; |
| 456 | |
| 457 | vactive = edid->mode.va; |
| 458 | vsa = edid->mode.vspw; |
| 459 | vfp = edid->mode.vso; |
| 460 | vbp = edid->mode.vbl - edid->mode.vso - edid->mode.vspw; |
| 461 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 462 | write32(&dsi->mipi_regs->dsi_vid_vactive_lines, vactive); |
| 463 | write32(&dsi->mipi_regs->dsi_vid_vsa_lines, vsa); |
| 464 | write32(&dsi->mipi_regs->dsi_vid_vfp_lines, vfp); |
| 465 | write32(&dsi->mipi_regs->dsi_vid_vbp_lines, vbp); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 466 | } |
| 467 | |
| 468 | static void rk_mipi_dsi_dphy_timing_config(struct rk_mipi_dsi *dsi) |
| 469 | { |
| 470 | /* |
| 471 | * HS-PREPARE: 40ns + 4 * UI ~ 85ns + 6 * UI |
| 472 | * HS-EXIT: 100ns |
| 473 | */ |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 474 | write32(&dsi->mipi_regs->dsi_phy_tmr_cfg, PHY_HS2LP_TIME(0x40) | |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 475 | PHY_LP2HS_TIME(0x40) | |
| 476 | MAX_RD_TIME(10000)); |
| 477 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 478 | write32(&dsi->mipi_regs->dsi_phy_tmr_lpclk_cfg, |
| 479 | PHY_CLKHS2LP_TIME(0x40) | PHY_CLKLP2HS_TIME(0x40)); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 480 | } |
| 481 | |
| 482 | static void rk_mipi_dsi_clear_err(struct rk_mipi_dsi *dsi) |
| 483 | { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 484 | read32(&dsi->mipi_regs->dsi_int_st0); |
| 485 | read32(&dsi->mipi_regs->dsi_int_st1); |
| 486 | write32(&dsi->mipi_regs->dsi_int_msk0, 0); |
| 487 | write32(&dsi->mipi_regs->dsi_int_msk1, 0); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 488 | } |
| 489 | |
| 490 | static void rk_mipi_dsi_dphy_interface_config(struct rk_mipi_dsi *dsi) |
| 491 | { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 492 | write32(&dsi->mipi_regs->dsi_phy_if_cfg, PHY_STOP_WAIT_TIME(0x20) | |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 493 | N_LANES(dsi->lanes)); |
| 494 | } |
| 495 | |
| 496 | static void rk_mipi_dsi_set_mode(struct rk_mipi_dsi *dsi, |
| 497 | enum rk_mipi_dsi_mode mode) |
| 498 | { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 499 | write32(&dsi->mipi_regs->dsi_pwr_up, RESET); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 500 | if (mode == MIPI_DSI_CMD_MODE) { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 501 | write32(&dsi->mipi_regs->dsi_mode_cfg, ENABLE_CMD_MODE); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 502 | } else { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 503 | write32(&dsi->mipi_regs->dsi_mode_cfg, ENABLE_VIDEO_MODE); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 504 | rk_mipi_dsi_video_mode_config(dsi); |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 505 | write32(&dsi->mipi_regs->dsi_lpclk_ctrl, PHY_TXREQUESTCLKHS); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 506 | } |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 507 | write32(&dsi->mipi_regs->dsi_pwr_up, POWERUP); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 508 | } |
| 509 | |
| 510 | static void rk_mipi_dsi_init(struct rk_mipi_dsi *dsi) |
| 511 | { |
| 512 | /* |
| 513 | * The maximum permitted escape clock is 20MHz and it is derived from |
| 514 | * lanebyteclk, which is running at "lane_mbps / 8". Thus we want: |
| 515 | * |
| 516 | * (lane_mbps >> 3) / esc_clk_division < 20 |
| 517 | * which is: |
| 518 | * (lane_mbps >> 3) / 20 > esc_clk_division |
| 519 | */ |
Elyes HAOUAS | 6df3b64 | 2018-11-26 22:53:49 +0100 | [diff] [blame] | 520 | u32 esc_clk_division = DIV_ROUND_UP(dsi->lane_bps, |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 521 | 8 * 20 * USECS_PER_SEC); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 522 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 523 | write32(&dsi->mipi_regs->dsi_pwr_up, RESET); |
| 524 | write32(&dsi->mipi_regs->dsi_phy_rstz, |
| 525 | PHY_DISFORCEPLL | PHY_DISABLECLK | PHY_RSTZ | PHY_SHUTDOWNZ); |
| 526 | write32(&dsi->mipi_regs->dsi_clk_cfg, |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 527 | TO_CLK_DIVIDSION(10) | |
| 528 | TX_ESC_CLK_DIVIDSION(esc_clk_division)); |
| 529 | } |
| 530 | |
Lin Huang | 538b9ef | 2017-11-01 10:22:49 +0800 | [diff] [blame] | 531 | static void rk_mipi_message_config(struct rk_mipi_dsi *dsi) |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 532 | { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 533 | write32(&dsi->mipi_regs->dsi_lpclk_ctrl, 0); |
| 534 | write32(&dsi->mipi_regs->dsi_cmd_mode_cfg, CMD_MODE_ALL_LP); |
Lin Huang | 538b9ef | 2017-11-01 10:22:49 +0800 | [diff] [blame] | 535 | } |
| 536 | |
Lin Huang | 0499ce9 | 2018-01-17 14:24:14 +0800 | [diff] [blame] | 537 | static int rk_mipi_dsi_check_fifo(struct rk_mipi_dsi *dsi, u32 flag) |
Lin Huang | 538b9ef | 2017-11-01 10:22:49 +0800 | [diff] [blame] | 538 | { |
| 539 | struct stopwatch sw; |
| 540 | int val; |
| 541 | |
| 542 | stopwatch_init_msecs_expire(&sw, 20); |
| 543 | do { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 544 | val = read32(&dsi->mipi_regs->dsi_cmd_pkt_status); |
Lin Huang | 0499ce9 | 2018-01-17 14:24:14 +0800 | [diff] [blame] | 545 | if (!(val & flag)) |
Elyes Haouas | 4b6d368 | 2023-08-13 12:49:16 +0200 | [diff] [blame] | 546 | return 0; |
Lin Huang | 538b9ef | 2017-11-01 10:22:49 +0800 | [diff] [blame] | 547 | } while (!stopwatch_expired(&sw)); |
| 548 | |
| 549 | return -1; |
| 550 | } |
| 551 | |
| 552 | static int rk_mipi_dsi_gen_pkt_hdr_write(struct rk_mipi_dsi *dsi, u32 hdr_val) |
| 553 | { |
| 554 | int val; |
| 555 | struct stopwatch sw; |
| 556 | u32 mask; |
| 557 | |
Lin Huang | 0499ce9 | 2018-01-17 14:24:14 +0800 | [diff] [blame] | 558 | if (rk_mipi_dsi_check_fifo(dsi, GEN_CMD_FULL)) { |
Lin Huang | 538b9ef | 2017-11-01 10:22:49 +0800 | [diff] [blame] | 559 | printk(BIOS_ERR, "failed to get available command FIFO\n"); |
| 560 | return -1; |
| 561 | } |
| 562 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 563 | write32(&dsi->mipi_regs->dsi_gen_hdr, hdr_val); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 564 | |
Lin Huang | 538b9ef | 2017-11-01 10:22:49 +0800 | [diff] [blame] | 565 | mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY; |
| 566 | stopwatch_init_msecs_expire(&sw, 20); |
| 567 | do { |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 568 | val = read32(&dsi->mipi_regs->dsi_cmd_pkt_status); |
Lin Huang | 538b9ef | 2017-11-01 10:22:49 +0800 | [diff] [blame] | 569 | if ((val & mask) == mask) |
Elyes Haouas | 4b6d368 | 2023-08-13 12:49:16 +0200 | [diff] [blame] | 570 | return 0; |
Lin Huang | 538b9ef | 2017-11-01 10:22:49 +0800 | [diff] [blame] | 571 | } while (!stopwatch_expired(&sw)); |
| 572 | printk(BIOS_ERR, "failed to write command FIFO\n"); |
| 573 | |
| 574 | return -1; |
| 575 | } |
| 576 | |
| 577 | static int rk_mipi_dsi_dcs_cmd(struct rk_mipi_dsi *dsi, u8 cmd) |
| 578 | { |
| 579 | u32 val; |
| 580 | |
| 581 | rk_mipi_message_config(dsi); |
| 582 | |
| 583 | val = GEN_HDATA(cmd) | GEN_HTYPE(MIPI_DSI_DCS_SHORT_WRITE); |
| 584 | |
| 585 | return rk_mipi_dsi_gen_pkt_hdr_write(dsi, val); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 586 | } |
| 587 | |
Lin Huang | 0499ce9 | 2018-01-17 14:24:14 +0800 | [diff] [blame] | 588 | static int rk_mipi_dsi_dci_long_write(struct rk_mipi_dsi *dsi, |
| 589 | char *data, u32 len) |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 590 | { |
Lin Huang | 0499ce9 | 2018-01-17 14:24:14 +0800 | [diff] [blame] | 591 | u32 remainder; |
| 592 | int ret = 0; |
| 593 | |
| 594 | while (len) { |
| 595 | if (len < 4) { |
| 596 | remainder = 0; |
| 597 | memcpy(&remainder, data, len); |
| 598 | write32(&dsi->mipi_regs->dsi_gen_pld_data, remainder); |
| 599 | len = 0; |
| 600 | } else { |
| 601 | remainder = *(u32 *)data; |
| 602 | write32(&dsi->mipi_regs->dsi_gen_pld_data, remainder); |
| 603 | data += 4; |
| 604 | len -= 4; |
| 605 | } |
| 606 | |
| 607 | ret = rk_mipi_dsi_check_fifo(dsi, GEN_PLD_W_FULL); |
| 608 | if (ret) { |
| 609 | printk(BIOS_ERR, "Failed to write fifo\n"); |
| 610 | return ret; |
| 611 | } |
| 612 | } |
| 613 | |
| 614 | return ret; |
| 615 | } |
| 616 | |
| 617 | static int rk_mipi_dsi_write(struct rk_mipi_dsi *dsi, char *data, int len) |
| 618 | { |
| 619 | u16 buf = 0; |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 620 | u32 val; |
Lin Huang | 0499ce9 | 2018-01-17 14:24:14 +0800 | [diff] [blame] | 621 | int ret = 0; |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 622 | |
| 623 | rk_mipi_message_config(dsi); |
| 624 | |
Lin Huang | 0499ce9 | 2018-01-17 14:24:14 +0800 | [diff] [blame] | 625 | switch (len) { |
| 626 | case 0: |
| 627 | die("not data!"); |
| 628 | case 1: |
| 629 | val = GEN_HDATA(*data) | |
| 630 | GEN_HTYPE(MIPI_DSI_DCS_SHORT_WRITE); |
| 631 | break; |
| 632 | case 2: |
| 633 | buf = *data++; |
| 634 | buf |= *data << 8; |
| 635 | val = GEN_HDATA(buf) | |
| 636 | GEN_HTYPE(MIPI_DSI_DCS_SHORT_WRITE_PARAM); |
| 637 | break; |
| 638 | default: |
| 639 | ret = rk_mipi_dsi_dci_long_write(dsi, data, len); |
| 640 | if (ret) { |
| 641 | printk(BIOS_ERR, "error happened during long write\n"); |
| 642 | return ret; |
| 643 | } |
| 644 | val = GEN_HDATA(len) | GEN_HTYPE(MIPI_DSI_DCS_LONG_WRITE); |
| 645 | break; |
| 646 | } |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 647 | |
| 648 | return rk_mipi_dsi_gen_pkt_hdr_write(dsi, val); |
| 649 | } |
| 650 | |
| 651 | static void rk_mipi_enable(struct rk_mipi_dsi *dsi, |
| 652 | const struct edid *edid, |
| 653 | const struct mipi_panel_data *panel_data) |
| 654 | { |
| 655 | if (rk_mipi_dsi_get_lane_bps(dsi, edid, panel_data) < 0) |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 656 | return; |
| 657 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 658 | rk_mipi_dsi_init(dsi); |
| 659 | rk_mipi_dsi_dpi_config(dsi); |
| 660 | rk_mipi_dsi_packet_handler_config(dsi); |
| 661 | rk_mipi_dsi_video_mode_config(dsi); |
| 662 | rk_mipi_dsi_video_packet_config(dsi, edid, panel_data); |
| 663 | rk_mipi_dsi_command_mode_config(dsi); |
| 664 | rk_mipi_dsi_line_timer_config(dsi, edid); |
| 665 | rk_mipi_dsi_vertical_timing_config(dsi, edid); |
| 666 | rk_mipi_dsi_dphy_timing_config(dsi); |
| 667 | rk_mipi_dsi_dphy_interface_config(dsi); |
| 668 | rk_mipi_dsi_clear_err(dsi); |
| 669 | if (rk_mipi_dsi_phy_init(dsi) < 0) |
Nickey Yang | 5be0b2e | 2017-05-25 11:23:23 +0800 | [diff] [blame] | 670 | return; |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 671 | rk_mipi_dsi_wait_for_two_frames(dsi, edid); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 672 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 673 | rk_mipi_dsi_set_mode(dsi, MIPI_DSI_CMD_MODE); |
| 674 | } |
Lin Huang | 538b9ef | 2017-11-01 10:22:49 +0800 | [diff] [blame] | 675 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 676 | void rk_mipi_prepare(const struct edid *edid, |
| 677 | const struct mipi_panel_data *panel_data) |
| 678 | { |
| 679 | int i, num; |
Lin Huang | 0499ce9 | 2018-01-17 14:24:14 +0800 | [diff] [blame] | 680 | struct panel_init_command *cmds; |
Lin Huang | 538b9ef | 2017-11-01 10:22:49 +0800 | [diff] [blame] | 681 | |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 682 | for (i = 0; i < panel_data->mipi_num; i++) { |
| 683 | rk_mipi[i].lanes = panel_data->lanes / panel_data->mipi_num; |
| 684 | rk_mipi[i].format = panel_data->format; |
| 685 | rk_mipi_enable(&rk_mipi[i], edid, panel_data); |
| 686 | } |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 687 | |
Lin Huang | 0499ce9 | 2018-01-17 14:24:14 +0800 | [diff] [blame] | 688 | if (panel_data->init_cmd) { |
| 689 | cmds = panel_data->init_cmd; |
| 690 | for (num = 0; cmds[num].len != 0; num++) { |
| 691 | struct panel_init_command *cmd = &cmds[num]; |
| 692 | for (i = 0; i < panel_data->mipi_num; i++) { |
| 693 | if (rk_mipi_dsi_write(&rk_mipi[i], cmd->data, |
| 694 | cmd->len)) |
| 695 | return; |
| 696 | |
| 697 | /* make sure panel picks up the command */ |
| 698 | if (rk_mipi_dsi_dcs_cmd(&rk_mipi[i], |
| 699 | MIPI_DCS_NOP)) |
| 700 | return; |
| 701 | } |
| 702 | } |
Lin Huang | 25fb09b | 2017-11-22 09:40:50 +0800 | [diff] [blame] | 703 | } |
| 704 | |
| 705 | for (i = 0; i < panel_data->mipi_num; i++) { |
| 706 | if (rk_mipi_dsi_dcs_cmd(&rk_mipi[i], |
| 707 | MIPI_DCS_EXIT_SLEEP_MODE) < 0) |
| 708 | return; |
| 709 | } |
| 710 | udelay(panel_data->display_on_udelay); |
| 711 | for (i = 0; i < panel_data->mipi_num; i++) { |
| 712 | if (rk_mipi_dsi_dcs_cmd(&rk_mipi[i], |
| 713 | MIPI_DCS_SET_DISPLAY_ON) < 0) |
| 714 | return; |
| 715 | } |
| 716 | udelay(panel_data->video_mode_udelay); |
| 717 | for (i = 0; i < panel_data->mipi_num; i++) |
| 718 | rk_mipi_dsi_set_mode(&rk_mipi[i], MIPI_DSI_VID_MODE); |
Nickey Yang | fe122d4 | 2017-04-27 09:38:06 +0800 | [diff] [blame] | 719 | } |