Angel Pons | e67ab18 | 2020-04-04 18:51:11 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 2 | |
Hung-Te Lin | 75e4314 | 2019-08-07 10:31:27 +0800 | [diff] [blame] | 3 | #include <assert.h> |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 4 | #include <console/console.h> |
Julius Werner | 5ff1808 | 2021-08-24 16:03:57 -0700 | [diff] [blame] | 5 | #include <mipi/panel.h> |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 6 | #include <device/mmio.h> |
| 7 | #include <delay.h> |
| 8 | #include <edid.h> |
| 9 | #include <soc/dsi.h> |
Hung-Te Lin | ff0945e | 2019-08-07 09:59:16 +0800 | [diff] [blame] | 10 | #include <string.h> |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 11 | #include <timer.h> |
| 12 | |
Paul Ma | d0ded16 | 2020-05-08 14:28:25 +0800 | [diff] [blame] | 13 | #define MIN_HFP_BYTE 2 |
| 14 | #define MIN_HBP_BYTE 2 |
| 15 | |
Hung-Te Lin | 61e3466 | 2019-08-07 08:44:30 +0800 | [diff] [blame] | 16 | static unsigned int mtk_dsi_get_bits_per_pixel(u32 format) |
| 17 | { |
| 18 | switch (format) { |
| 19 | case MIPI_DSI_FMT_RGB565: |
| 20 | return 16; |
Hung-Te Lin | 61e3466 | 2019-08-07 08:44:30 +0800 | [diff] [blame] | 21 | case MIPI_DSI_FMT_RGB666_PACKED: |
| 22 | return 18; |
Yu-Ping Wu | b4a2938 | 2020-02-12 11:51:49 +0800 | [diff] [blame] | 23 | case MIPI_DSI_FMT_RGB666: |
Hung-Te Lin | 61e3466 | 2019-08-07 08:44:30 +0800 | [diff] [blame] | 24 | case MIPI_DSI_FMT_RGB888: |
| 25 | return 24; |
| 26 | } |
| 27 | printk(BIOS_WARNING, "%s: WARN: Unknown format %d, assuming 24 bpp\n", |
| 28 | __func__, format); |
| 29 | return 24; |
| 30 | } |
| 31 | |
Yu-Ping Wu | 443fbd7 | 2020-02-11 18:33:57 +0800 | [diff] [blame] | 32 | static u32 mtk_dsi_get_data_rate(u32 bits_per_pixel, u32 lanes, |
Hung-Te Lin | 302dddf | 2019-08-08 06:28:43 +0800 | [diff] [blame] | 33 | const struct edid *edid) |
| 34 | { |
| 35 | /* data_rate = pixel_clock * bits_per_pixel * mipi_ratio / lanes |
Yu-Ping Wu | 443fbd7 | 2020-02-11 18:33:57 +0800 | [diff] [blame] | 36 | * Note pixel_clock comes in kHz and returned data_rate is in bps. |
Hung-Te Lin | 302dddf | 2019-08-08 06:28:43 +0800 | [diff] [blame] | 37 | * mipi_ratio is the clk coefficient to balance the pixel clk in MIPI |
| 38 | * for older platforms which do not have complete implementation in HFP. |
| 39 | * Newer platforms should just set that to 1.0 (100 / 100). |
| 40 | */ |
Yu-Ping Wu | 443fbd7 | 2020-02-11 18:33:57 +0800 | [diff] [blame] | 41 | u32 data_rate = DIV_ROUND_UP((u64)edid->mode.pixel_clock * |
| 42 | bits_per_pixel * 1000 * |
| 43 | MTK_DSI_MIPI_RATIO_NUMERATOR, |
| 44 | (u64)lanes * |
| 45 | MTK_DSI_MIPI_RATIO_DENOMINATOR); |
| 46 | printk(BIOS_INFO, "DSI data_rate: %u bps\n", data_rate); |
Hung-Te Lin | 302dddf | 2019-08-08 06:28:43 +0800 | [diff] [blame] | 47 | |
Yu-Ping Wu | 443fbd7 | 2020-02-11 18:33:57 +0800 | [diff] [blame] | 48 | if (data_rate < MTK_DSI_DATA_RATE_MIN_MHZ * MHz) { |
| 49 | printk(BIOS_ERR, "data rate (%ubps) must be >= %ubps. " |
| 50 | "Please check the pixel clock (%u), " |
| 51 | "bits per pixel (%u), " |
Hung-Te Lin | 302dddf | 2019-08-08 06:28:43 +0800 | [diff] [blame] | 52 | "mipi_ratio (%d%%) and number of lanes (%d)\n", |
Yu-Ping Wu | 443fbd7 | 2020-02-11 18:33:57 +0800 | [diff] [blame] | 53 | data_rate, MTK_DSI_DATA_RATE_MIN_MHZ * MHz, |
Hung-Te Lin | 302dddf | 2019-08-08 06:28:43 +0800 | [diff] [blame] | 54 | edid->mode.pixel_clock, bits_per_pixel, |
| 55 | (100 * MTK_DSI_MIPI_RATIO_NUMERATOR / |
| 56 | MTK_DSI_MIPI_RATIO_DENOMINATOR), lanes); |
Yu-Ping Wu | 443fbd7 | 2020-02-11 18:33:57 +0800 | [diff] [blame] | 57 | return 0; |
Hung-Te Lin | 302dddf | 2019-08-08 06:28:43 +0800 | [diff] [blame] | 58 | } |
| 59 | return data_rate; |
| 60 | } |
| 61 | |
Hung-Te Lin | ff0945e | 2019-08-07 09:59:16 +0800 | [diff] [blame] | 62 | __weak void mtk_dsi_override_phy_timing(struct mtk_phy_timing *timing) |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 63 | { |
Hung-Te Lin | ff0945e | 2019-08-07 09:59:16 +0800 | [diff] [blame] | 64 | /* Do nothing. */ |
| 65 | } |
| 66 | |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 67 | static void mtk_dsi_phy_timing(u32 data_rate, struct mtk_phy_timing *timing) |
Hung-Te Lin | ff0945e | 2019-08-07 09:59:16 +0800 | [diff] [blame] | 68 | { |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 69 | u32 timcon0, timcon1, timcon2, timcon3; |
Yu-Ping Wu | 443fbd7 | 2020-02-11 18:33:57 +0800 | [diff] [blame] | 70 | u32 data_rate_mhz = DIV_ROUND_UP(data_rate, MHz); |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 71 | |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 72 | memset(timing, 0, sizeof(*timing)); |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 73 | |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 74 | timing->lpx = (60 * data_rate_mhz / (8 * 1000)) + 1; |
| 75 | timing->da_hs_prepare = (80 * data_rate_mhz + 4 * 1000) / 8000; |
| 76 | timing->da_hs_zero = (170 * data_rate_mhz + 10 * 1000) / 8000 + 1 - |
| 77 | timing->da_hs_prepare; |
| 78 | timing->da_hs_trail = timing->da_hs_prepare + 1; |
Hung-Te Lin | ff0945e | 2019-08-07 09:59:16 +0800 | [diff] [blame] | 79 | |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 80 | timing->ta_go = 4 * timing->lpx - 2; |
| 81 | timing->ta_sure = timing->lpx + 2; |
| 82 | timing->ta_get = 4 * timing->lpx; |
| 83 | timing->da_hs_exit = 2 * timing->lpx + 1; |
Hung-Te Lin | ff0945e | 2019-08-07 09:59:16 +0800 | [diff] [blame] | 84 | |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 85 | timing->da_hs_sync = 1; |
Hung-Te Lin | ff0945e | 2019-08-07 09:59:16 +0800 | [diff] [blame] | 86 | |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 87 | timing->clk_hs_prepare = 70 * data_rate_mhz / (8 * 1000); |
| 88 | timing->clk_hs_post = timing->clk_hs_prepare + 8; |
| 89 | timing->clk_hs_trail = timing->clk_hs_prepare; |
| 90 | timing->clk_hs_zero = timing->clk_hs_trail * 4; |
| 91 | timing->clk_hs_exit = 2 * timing->clk_hs_trail; |
Hung-Te Lin | ff0945e | 2019-08-07 09:59:16 +0800 | [diff] [blame] | 92 | |
| 93 | /* Allow board-specific tuning. */ |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 94 | mtk_dsi_override_phy_timing(timing); |
Hung-Te Lin | ff0945e | 2019-08-07 09:59:16 +0800 | [diff] [blame] | 95 | |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 96 | timcon0 = timing->lpx | timing->da_hs_prepare << 8 | |
| 97 | timing->da_hs_zero << 16 | timing->da_hs_trail << 24; |
| 98 | timcon1 = timing->ta_go | timing->ta_sure << 8 | |
| 99 | timing->ta_get << 16 | timing->da_hs_exit << 24; |
| 100 | timcon2 = timing->da_hs_sync << 8 | timing->clk_hs_zero << 16 | |
| 101 | timing->clk_hs_trail << 24; |
| 102 | timcon3 = timing->clk_hs_prepare | timing->clk_hs_post << 8 | |
| 103 | timing->clk_hs_exit << 16; |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 104 | |
| 105 | write32(&dsi0->dsi_phy_timecon0, timcon0); |
| 106 | write32(&dsi0->dsi_phy_timecon1, timcon1); |
| 107 | write32(&dsi0->dsi_phy_timecon2, timcon2); |
| 108 | write32(&dsi0->dsi_phy_timecon3, timcon3); |
| 109 | } |
| 110 | |
| 111 | static void mtk_dsi_clk_hs_mode_enable(void) |
| 112 | { |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 113 | setbits32(&dsi0->dsi_phy_lccon, LC_HS_TX_EN); |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 114 | } |
| 115 | |
| 116 | static void mtk_dsi_clk_hs_mode_disable(void) |
| 117 | { |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 118 | clrbits32(&dsi0->dsi_phy_lccon, LC_HS_TX_EN); |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 119 | } |
| 120 | |
| 121 | static void mtk_dsi_set_mode(u32 mode_flags) |
| 122 | { |
| 123 | u32 tmp_reg1 = 0; |
| 124 | |
| 125 | if (mode_flags & MIPI_DSI_MODE_VIDEO) { |
| 126 | tmp_reg1 = SYNC_PULSE_MODE; |
| 127 | |
| 128 | if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST) |
| 129 | tmp_reg1 = BURST_MODE; |
| 130 | |
| 131 | if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) |
| 132 | tmp_reg1 = SYNC_PULSE_MODE; |
| 133 | } |
| 134 | |
| 135 | write32(&dsi0->dsi_mode_ctrl, tmp_reg1); |
| 136 | } |
| 137 | |
| 138 | static void mtk_dsi_rxtx_control(u32 mode_flags, u32 lanes) |
| 139 | { |
| 140 | u32 tmp_reg = 0; |
| 141 | |
| 142 | switch (lanes) { |
| 143 | case 1: |
| 144 | tmp_reg = 1 << 2; |
| 145 | break; |
| 146 | case 2: |
| 147 | tmp_reg = 3 << 2; |
| 148 | break; |
| 149 | case 3: |
| 150 | tmp_reg = 7 << 2; |
| 151 | break; |
| 152 | case 4: |
| 153 | default: |
| 154 | tmp_reg = 0xf << 2; |
| 155 | break; |
| 156 | } |
| 157 | |
Shaoming Chen | 9e38efc | 2020-12-23 11:45:59 +0800 | [diff] [blame] | 158 | if (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) |
| 159 | tmp_reg |= NON_CONTINUOUS_CLK; |
| 160 | |
| 161 | if (!(mode_flags & MIPI_DSI_MODE_EOT_PACKET)) |
| 162 | tmp_reg |= EOTP_DISABLE; |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 163 | |
| 164 | write32(&dsi0->dsi_txrx_ctrl, tmp_reg); |
| 165 | } |
| 166 | |
Hung-Te Lin | 3b217d5 | 2019-08-07 10:15:48 +0800 | [diff] [blame] | 167 | static void mtk_dsi_config_vdo_timing(u32 mode_flags, u32 format, u32 lanes, |
| 168 | const struct edid *edid, |
| 169 | const struct mtk_phy_timing *phy_timing) |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 170 | { |
| 171 | u32 hsync_active_byte; |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 172 | u32 hbp; |
| 173 | u32 hfp; |
Paul Ma | d0ded16 | 2020-05-08 14:28:25 +0800 | [diff] [blame] | 174 | s32 hbp_byte; |
| 175 | s32 hfp_byte; |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 176 | u32 vbp_byte; |
| 177 | u32 vfp_byte; |
Hung-Te Lin | 61e3466 | 2019-08-07 08:44:30 +0800 | [diff] [blame] | 178 | u32 bytes_per_pixel; |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 179 | u32 packet_fmt; |
| 180 | u32 hactive; |
Hung-Te Lin | 3b217d5 | 2019-08-07 10:15:48 +0800 | [diff] [blame] | 181 | u32 data_phy_cycles; |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 182 | |
Hung-Te Lin | 61e3466 | 2019-08-07 08:44:30 +0800 | [diff] [blame] | 183 | bytes_per_pixel = DIV_ROUND_UP(mtk_dsi_get_bits_per_pixel(format), 8); |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 184 | vbp_byte = edid->mode.vbl - edid->mode.vso - edid->mode.vspw - |
| 185 | edid->mode.vborder; |
| 186 | vfp_byte = edid->mode.vso - edid->mode.vborder; |
| 187 | |
| 188 | write32(&dsi0->dsi_vsa_nl, edid->mode.vspw); |
| 189 | write32(&dsi0->dsi_vbp_nl, vbp_byte); |
| 190 | write32(&dsi0->dsi_vfp_nl, vfp_byte); |
| 191 | write32(&dsi0->dsi_vact_nl, edid->mode.va); |
| 192 | |
Hung-Te Lin | 61e3466 | 2019-08-07 08:44:30 +0800 | [diff] [blame] | 193 | hsync_active_byte = edid->mode.hspw * bytes_per_pixel - 10; |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 194 | |
| 195 | hbp = edid->mode.hbl - edid->mode.hso - edid->mode.hspw - |
| 196 | edid->mode.hborder; |
| 197 | hfp = edid->mode.hso - edid->mode.hborder; |
| 198 | |
| 199 | if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) |
| 200 | hbp_byte = hbp * bytes_per_pixel - 10; |
| 201 | else |
| 202 | hbp_byte = (hbp + edid->mode.hspw) * bytes_per_pixel - 10; |
| 203 | hfp_byte = hfp * bytes_per_pixel; |
Hung-Te Lin | 3b217d5 | 2019-08-07 10:15:48 +0800 | [diff] [blame] | 204 | |
| 205 | data_phy_cycles = phy_timing->lpx + phy_timing->da_hs_prepare + |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 206 | phy_timing->da_hs_zero + phy_timing->da_hs_exit + 3; |
Hung-Te Lin | 3b217d5 | 2019-08-07 10:15:48 +0800 | [diff] [blame] | 207 | |
Jitao Shi | 104b7a9 | 2021-03-12 17:35:18 +0800 | [diff] [blame] | 208 | u32 delta = 10; |
Shaoming Chen | 9e38efc | 2020-12-23 11:45:59 +0800 | [diff] [blame] | 209 | |
| 210 | if (mode_flags & MIPI_DSI_MODE_EOT_PACKET) |
| 211 | delta += 2; |
| 212 | |
Hung-Te Lin | 3b217d5 | 2019-08-07 10:15:48 +0800 | [diff] [blame] | 213 | if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST) |
| 214 | delta += 6; |
| 215 | |
| 216 | u32 d_phy = phy_timing->d_phy; |
| 217 | if (d_phy == 0) |
| 218 | d_phy = data_phy_cycles * lanes + delta; |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 219 | |
| 220 | if ((hfp + hbp) * bytes_per_pixel > d_phy) { |
| 221 | hfp_byte -= d_phy * hfp / (hfp + hbp); |
| 222 | hbp_byte -= d_phy * hbp / (hfp + hbp); |
| 223 | } else { |
Ruihai Zhou | 7a66715 | 2023-07-13 09:24:17 +0800 | [diff] [blame^] | 224 | printk(BIOS_ERR, "HFP %u plus HBP %u is not greater than d_phy %u, " |
| 225 | "the panel may not work properly.\n", hfp * bytes_per_pixel, |
| 226 | hbp * bytes_per_pixel, d_phy); |
Jitao Shi | f68cc81 | 2020-01-14 21:23:44 +0800 | [diff] [blame] | 227 | } |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 228 | |
Jitao Shi | 927fa6d | 2021-02-01 13:21:45 +0800 | [diff] [blame] | 229 | if (mode_flags & MIPI_DSI_MODE_LINE_END) { |
| 230 | hsync_active_byte = DIV_ROUND_UP(hsync_active_byte, lanes) * lanes - 2; |
| 231 | hbp_byte = DIV_ROUND_UP(hbp_byte, lanes) * lanes - 2; |
| 232 | hfp_byte = DIV_ROUND_UP(hfp_byte, lanes) * lanes - 2; |
| 233 | hbp_byte -= (edid->mode.ha * bytes_per_pixel + 2) % lanes; |
| 234 | } |
| 235 | |
Paul Ma | d0ded16 | 2020-05-08 14:28:25 +0800 | [diff] [blame] | 236 | if (hfp_byte + hbp_byte < MIN_HFP_BYTE + MIN_HBP_BYTE) { |
Ruihai Zhou | 7a66715 | 2023-07-13 09:24:17 +0800 | [diff] [blame^] | 237 | printk(BIOS_ERR, "Calculated hfp_byte %d and hbp_byte %d are too small, " |
| 238 | "the panel may not work properly.\n", hfp_byte, hbp_byte); |
Paul Ma | d0ded16 | 2020-05-08 14:28:25 +0800 | [diff] [blame] | 239 | } else if (hfp_byte < MIN_HFP_BYTE) { |
Ruihai Zhou | 7a66715 | 2023-07-13 09:24:17 +0800 | [diff] [blame^] | 240 | printk(BIOS_NOTICE, "Calculated hfp_byte %d is too small, " |
| 241 | "adjust it to the minimum value %d.\n", hfp_byte, MIN_HFP_BYTE); |
Paul Ma | d0ded16 | 2020-05-08 14:28:25 +0800 | [diff] [blame] | 242 | hbp_byte -= MIN_HFP_BYTE - hfp_byte; |
| 243 | hfp_byte = MIN_HFP_BYTE; |
| 244 | } else if (hbp_byte < MIN_HBP_BYTE) { |
Ruihai Zhou | 7a66715 | 2023-07-13 09:24:17 +0800 | [diff] [blame^] | 245 | printk(BIOS_NOTICE, "Calculated hbp_byte %d is too small, " |
| 246 | "adjust it to the minimum value %d.\n", hbp_byte, MIN_HBP_BYTE); |
Paul Ma | d0ded16 | 2020-05-08 14:28:25 +0800 | [diff] [blame] | 247 | hfp_byte -= MIN_HBP_BYTE - hbp_byte; |
| 248 | hbp_byte = MIN_HBP_BYTE; |
| 249 | } |
| 250 | |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 251 | write32(&dsi0->dsi_hsa_wc, hsync_active_byte); |
| 252 | write32(&dsi0->dsi_hbp_wc, hbp_byte); |
| 253 | write32(&dsi0->dsi_hfp_wc, hfp_byte); |
| 254 | |
| 255 | switch (format) { |
| 256 | case MIPI_DSI_FMT_RGB888: |
| 257 | packet_fmt = PACKED_PS_24BIT_RGB888; |
| 258 | break; |
| 259 | case MIPI_DSI_FMT_RGB666: |
| 260 | packet_fmt = LOOSELY_PS_18BIT_RGB666; |
| 261 | break; |
| 262 | case MIPI_DSI_FMT_RGB666_PACKED: |
| 263 | packet_fmt = PACKED_PS_18BIT_RGB666; |
| 264 | break; |
| 265 | case MIPI_DSI_FMT_RGB565: |
| 266 | packet_fmt = PACKED_PS_16BIT_RGB565; |
| 267 | break; |
| 268 | default: |
| 269 | packet_fmt = PACKED_PS_24BIT_RGB888; |
| 270 | break; |
| 271 | } |
| 272 | |
| 273 | hactive = edid->mode.ha; |
Hung-Te Lin | 61e3466 | 2019-08-07 08:44:30 +0800 | [diff] [blame] | 274 | packet_fmt |= (hactive * bytes_per_pixel) & DSI_PS_WC; |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 275 | |
Hung-Te Lin | 3b217d5 | 2019-08-07 10:15:48 +0800 | [diff] [blame] | 276 | write32(&dsi0->dsi_psctrl, |
| 277 | PIXEL_STREAM_CUSTOM_HEADER << DSI_PSCON_CUSTOM_HEADER_SHIFT | |
| 278 | packet_fmt); |
Hung-Te Lin | 32ddc0d | 2019-08-07 10:58:36 +0800 | [diff] [blame] | 279 | |
| 280 | /* Older systems like MT8173 do not support size_con. */ |
| 281 | if (MTK_DSI_HAVE_SIZE_CON) |
| 282 | write32(&dsi0->dsi_size_con, |
| 283 | edid->mode.va << DSI_SIZE_CON_HEIGHT_SHIFT | |
| 284 | hactive << DSI_SIZE_CON_WIDTH_SHIFT); |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 285 | } |
| 286 | |
| 287 | static void mtk_dsi_start(void) |
| 288 | { |
| 289 | write32(&dsi0->dsi_start, 0); |
| 290 | /* Only start master DSI */ |
| 291 | write32(&dsi0->dsi_start, 1); |
| 292 | } |
| 293 | |
Julius Werner | 4757a7e | 2021-09-08 17:58:34 -0700 | [diff] [blame] | 294 | static bool mtk_dsi_is_read_command(enum mipi_dsi_transaction type) |
Hung-Te Lin | 75e4314 | 2019-08-07 10:31:27 +0800 | [diff] [blame] | 295 | { |
| 296 | switch (type) { |
| 297 | case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: |
| 298 | case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: |
| 299 | case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: |
| 300 | case MIPI_DSI_DCS_READ: |
| 301 | return true; |
Julius Werner | 4757a7e | 2021-09-08 17:58:34 -0700 | [diff] [blame] | 302 | default: |
| 303 | return false; |
Hung-Te Lin | 75e4314 | 2019-08-07 10:31:27 +0800 | [diff] [blame] | 304 | } |
Hung-Te Lin | 75e4314 | 2019-08-07 10:31:27 +0800 | [diff] [blame] | 305 | } |
| 306 | |
Julius Werner | 69cc557 | 2022-03-04 17:49:56 -0800 | [diff] [blame] | 307 | static enum cb_err mtk_dsi_cmdq(enum mipi_dsi_transaction type, const u8 *data, u8 len) |
Hung-Te Lin | 75e4314 | 2019-08-07 10:31:27 +0800 | [diff] [blame] | 308 | { |
| 309 | const u8 *tx_buf = data; |
| 310 | u32 config; |
| 311 | int i, j; |
| 312 | |
| 313 | if (!wait_ms(20, !(read32(&dsi0->dsi_intsta) & DSI_BUSY))) { |
| 314 | printk(BIOS_ERR, "%s: cannot get DSI ready for sending commands" |
| 315 | " after 20ms and the panel may not work properly.\n", |
| 316 | __func__); |
Julius Werner | 4757a7e | 2021-09-08 17:58:34 -0700 | [diff] [blame] | 317 | return CB_ERR; |
Hung-Te Lin | 75e4314 | 2019-08-07 10:31:27 +0800 | [diff] [blame] | 318 | } |
| 319 | write32(&dsi0->dsi_intsta, 0); |
| 320 | |
| 321 | if (mtk_dsi_is_read_command(type)) |
| 322 | config = BTA; |
| 323 | else |
| 324 | config = (len > 2) ? LONG_PACKET : SHORT_PACKET; |
| 325 | |
| 326 | if (len <= 2) { |
| 327 | uint32_t val = (type << 8) | config; |
| 328 | for (i = 0; i < len; i++) |
| 329 | val |= tx_buf[i] << (i + 2) * 8; |
| 330 | write32(&dsi0->dsi_cmdq[0], val); |
| 331 | write32(&dsi0->dsi_cmdq_size, 1); |
| 332 | } else { |
| 333 | /* TODO(hungte) Replace by buffer_to_fifo32_prefix */ |
| 334 | write32(&dsi0->dsi_cmdq[0], (len << 16) | (type << 8) | config); |
| 335 | for (i = 0; i < len; i += 4) { |
| 336 | uint32_t val = 0; |
| 337 | for (j = 0; j < MIN(len - i, 4); j++) |
| 338 | val |= tx_buf[i + j] << j * 8; |
| 339 | write32(&dsi0->dsi_cmdq[i / 4 + 1], val); |
| 340 | } |
| 341 | write32(&dsi0->dsi_cmdq_size, 1 + DIV_ROUND_UP(len, 4)); |
| 342 | } |
| 343 | |
| 344 | mtk_dsi_start(); |
| 345 | |
| 346 | if (!wait_us(400, read32(&dsi0->dsi_intsta) & CMD_DONE_INT_FLAG)) { |
| 347 | printk(BIOS_ERR, "%s: failed sending DSI command, " |
| 348 | "panel may not work.\n", __func__); |
Julius Werner | b2a1480 | 2021-08-12 16:48:12 -0700 | [diff] [blame] | 349 | return CB_ERR; |
Hung-Te Lin | 75e4314 | 2019-08-07 10:31:27 +0800 | [diff] [blame] | 350 | } |
Julius Werner | b2a1480 | 2021-08-12 16:48:12 -0700 | [diff] [blame] | 351 | |
Julius Werner | b2a1480 | 2021-08-12 16:48:12 -0700 | [diff] [blame] | 352 | return CB_SUCCESS; |
Hung-Te Lin | 75e4314 | 2019-08-07 10:31:27 +0800 | [diff] [blame] | 353 | } |
| 354 | |
Jitao Shi | b6ca938 | 2019-10-22 10:15:34 +0800 | [diff] [blame] | 355 | static void mtk_dsi_reset_dphy(void) |
| 356 | { |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 357 | setbits32(&dsi0->dsi_con_ctrl, DPHY_RESET); |
| 358 | clrbits32(&dsi0->dsi_con_ctrl, DPHY_RESET); |
Jitao Shi | b6ca938 | 2019-10-22 10:15:34 +0800 | [diff] [blame] | 359 | } |
| 360 | |
Hung-Te Lin | 75e4314 | 2019-08-07 10:31:27 +0800 | [diff] [blame] | 361 | int mtk_dsi_init(u32 mode_flags, u32 format, u32 lanes, const struct edid *edid, |
Hung-Te Lin | e366ba1 | 2019-08-07 16:28:08 +0800 | [diff] [blame] | 362 | const u8 *init_commands) |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 363 | { |
Yu-Ping Wu | 443fbd7 | 2020-02-11 18:33:57 +0800 | [diff] [blame] | 364 | u32 data_rate; |
Hung-Te Lin | 61e3466 | 2019-08-07 08:44:30 +0800 | [diff] [blame] | 365 | u32 bits_per_pixel = mtk_dsi_get_bits_per_pixel(format); |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 366 | |
Hung-Te Lin | 302dddf | 2019-08-08 06:28:43 +0800 | [diff] [blame] | 367 | data_rate = mtk_dsi_get_data_rate(bits_per_pixel, lanes, edid); |
Yu-Ping Wu | 443fbd7 | 2020-02-11 18:33:57 +0800 | [diff] [blame] | 368 | if (!data_rate) |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 369 | return -1; |
| 370 | |
Hung-Te Lin | 302dddf | 2019-08-08 06:28:43 +0800 | [diff] [blame] | 371 | mtk_dsi_configure_mipi_tx(data_rate, lanes); |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 372 | mtk_dsi_reset(); |
Hung-Te Lin | ff0945e | 2019-08-07 09:59:16 +0800 | [diff] [blame] | 373 | struct mtk_phy_timing phy_timing; |
| 374 | mtk_dsi_phy_timing(data_rate, &phy_timing); |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 375 | mtk_dsi_rxtx_control(mode_flags, lanes); |
Jitao Shi | b6ca938 | 2019-10-22 10:15:34 +0800 | [diff] [blame] | 376 | mdelay(1); |
| 377 | mtk_dsi_reset_dphy(); |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 378 | mtk_dsi_clk_hs_mode_disable(); |
Hung-Te Lin | 3b217d5 | 2019-08-07 10:15:48 +0800 | [diff] [blame] | 379 | mtk_dsi_config_vdo_timing(mode_flags, format, lanes, edid, &phy_timing); |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 380 | mtk_dsi_clk_hs_mode_enable(); |
Yu-Ping Wu | f8f5062 | 2021-12-27 13:46:35 +0800 | [diff] [blame] | 381 | if (init_commands) |
| 382 | mipi_panel_parse_init_commands(init_commands, mtk_dsi_cmdq); |
Hung-Te Lin | 75e4314 | 2019-08-07 10:31:27 +0800 | [diff] [blame] | 383 | mtk_dsi_set_mode(mode_flags); |
Hung-Te Lin | c59fbf2 | 2019-08-07 08:00:58 +0800 | [diff] [blame] | 384 | mtk_dsi_start(); |
| 385 | |
| 386 | return 0; |
| 387 | } |