Angel Pons | 1ddb894 | 2020-04-04 18:51:26 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 2 | |
Stefan Reinauer | 08dc357 | 2013-05-14 16:57:50 -0700 | [diff] [blame] | 3 | /* LCD driver for Exynos */ |
| 4 | |
Kyösti Mälkki | 13f6650 | 2019-03-03 08:01:05 +0200 | [diff] [blame] | 5 | #include <device/mmio.h> |
Julius Werner | 1ed0c8c | 2014-10-20 13:16:29 -0700 | [diff] [blame] | 6 | #include <console/console.h> |
Julius Werner | fa938c7 | 2013-08-29 14:17:36 -0700 | [diff] [blame] | 7 | #include <delay.h> |
Julius Werner | 1ed0c8c | 2014-10-20 13:16:29 -0700 | [diff] [blame] | 8 | #include <soc/dp.h> |
| 9 | #include <soc/dp-core.h> |
| 10 | #include <soc/fimd.h> |
| 11 | #include <soc/i2c.h> |
| 12 | #include <soc/power.h> |
| 13 | #include <soc/sysreg.h> |
David Hendricks | 0bb875b | 2013-05-02 14:23:51 -0700 | [diff] [blame] | 14 | #include <timer.h> |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 15 | |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 16 | /* |
| 17 | * Here is the rough outline of how we bring up the display: |
| 18 | * 1. Upon power-on Sink generates a hot plug detection pulse thru HPD |
| 19 | * 2. Source determines video mode by reading DPCD receiver capability field |
| 20 | * (DPCD 00000h to 0000Dh) including eDP CP capability register (DPCD |
| 21 | * 0000Dh). |
| 22 | * 3. Sink replies DPCD receiver capability field. |
| 23 | * 4. Source starts EDID read thru I2C-over-AUX. |
| 24 | * 5. Sink replies EDID thru I2C-over-AUX. |
| 25 | * 6. Source determines link configuration, such as MAX_LINK_RATE and |
| 26 | * MAX_LANE_COUNT. Source also determines which type of eDP Authentication |
| 27 | * method to use and writes DPCD link configuration field (DPCD 00100h to |
| 28 | * 0010Ah) including eDP configuration set (DPCD 0010Ah). |
| 29 | * 7. Source starts link training. Sink does clock recovery and equalization. |
| 30 | * 8. Source reads DPCD link status field (DPCD 00200h to 0020Bh). |
| 31 | * 9. Sink replies DPCD link status field. If main link is not stable, Source |
| 32 | * repeats Step 7. |
| 33 | * 10. Source sends MSA (Main Stream Attribute) data. Sink extracts video |
| 34 | * parameters and recovers stream clock. |
| 35 | * 11. Source sends video data. |
| 36 | */ |
| 37 | |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 38 | /* To help debug any init errors here, define a list of possible errors */ |
| 39 | enum { |
| 40 | ERR_PLL_NOT_UNLOCKED = 2, |
| 41 | ERR_VIDEO_CLOCK_BAD, |
| 42 | ERR_VIDEO_STREAM_BAD, |
| 43 | ERR_DPCD_READ_ERROR1, /* 5 */ |
| 44 | |
| 45 | ERR_DPCD_WRITE_ERROR1, |
| 46 | ERR_DPCD_READ_ERROR2, |
| 47 | ERR_DPCD_WRITE_ERROR2, |
| 48 | ERR_INVALID_LANE, |
| 49 | ERR_PLL_NOT_LOCKED, /* 10 */ |
| 50 | |
| 51 | ERR_PRE_EMPHASIS_LEVELS, |
| 52 | ERR_LINK_RATE_ABNORMAL, |
| 53 | ERR_MAX_LANE_COUNT_ABNORMAL, |
| 54 | ERR_LINK_TRAINING_FAILURE, |
| 55 | ERR_MISSING_DP_BASE, /* 15 */ |
| 56 | |
| 57 | ERR_NO_FDT_NODE, |
| 58 | }; |
Stefan Reinauer | b98dec0 | 2013-05-14 13:32:33 -0700 | [diff] [blame] | 59 | /* ok, this is stupid, but we're going to leave the variables in here until we |
| 60 | * know it works. One cleanup task at a time. |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 61 | */ |
| 62 | enum stage_t { |
| 63 | STAGE_START = 0, |
| 64 | STAGE_LCD_VDD, |
| 65 | STAGE_BRIDGE_SETUP, |
| 66 | STAGE_BRIDGE_INIT, |
| 67 | STAGE_BRIDGE_RESET, |
| 68 | STAGE_HOTPLUG, |
| 69 | STAGE_DP_CONTROLLER, |
| 70 | STAGE_BACKLIGHT_VDD, |
| 71 | STAGE_BACKLIGHT_PWM, |
| 72 | STAGE_BACKLIGHT_EN, |
| 73 | STAGE_DONE, |
| 74 | }; |
| 75 | |
| 76 | int lcd_line_length; |
| 77 | int lcd_color_fg; |
| 78 | int lcd_color_bg; |
| 79 | |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 80 | void *lcd_console_address; /* Start of console buffer */ |
| 81 | |
| 82 | short console_col; |
| 83 | short console_row; |
| 84 | |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 85 | /* Bypass FIMD of DISP1_BLK */ |
| 86 | static void fimd_bypass(void) |
| 87 | { |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 88 | setbits32(&exynos_sysreg->disp1blk_cfg, FIMDBYPASS_DISP1); |
Julius Werner | fa938c7 | 2013-08-29 14:17:36 -0700 | [diff] [blame] | 89 | exynos_sysreg->disp1blk_cfg &= ~FIMDBYPASS_DISP1; |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 90 | } |
| 91 | |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 92 | /* |
| 93 | * Initialize display controller. |
| 94 | * |
| 95 | * @param lcdbase pointer to the base address of framebuffer. |
Martin Roth | 5f066b2 | 2015-01-04 16:47:39 -0700 | [diff] [blame] | 96 | * @param pd pointer to the main panel_data structure |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 97 | */ |
Gabe Black | 1e797bd | 2013-05-18 15:58:46 -0700 | [diff] [blame] | 98 | void fb_init(unsigned long int fb_size, void *lcdbase, |
| 99 | struct exynos5_fimd_panel *pd) |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 100 | { |
| 101 | unsigned int val; |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 102 | |
Elyes Haouas | d6b6b22 | 2022-10-10 12:34:21 +0200 | [diff] [blame^] | 103 | fb_size = ALIGN_UP(fb_size, 4096); |
Gabe Black | 1e797bd | 2013-05-18 15:58:46 -0700 | [diff] [blame] | 104 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 105 | write32(&exynos_disp_ctrl->vidcon1, pd->ivclk | pd->fixvclk); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 106 | val = ENVID_ON | ENVID_F_ON | (pd->clkval_f << CLKVAL_F_OFFSET); |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 107 | write32(&exynos_fimd->vidcon0, val); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 108 | |
| 109 | val = (pd->vsync << VSYNC_PULSE_WIDTH_OFFSET) | |
| 110 | (pd->lower_margin << V_FRONT_PORCH_OFFSET) | |
| 111 | (pd->upper_margin << V_BACK_PORCH_OFFSET); |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 112 | write32(&exynos_disp_ctrl->vidtcon0, val); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 113 | |
| 114 | val = (pd->hsync << HSYNC_PULSE_WIDTH_OFFSET) | |
| 115 | (pd->right_margin << H_FRONT_PORCH_OFFSET) | |
| 116 | (pd->left_margin << H_BACK_PORCH_OFFSET); |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 117 | write32(&exynos_disp_ctrl->vidtcon1, val); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 118 | |
| 119 | val = ((pd->xres - 1) << HOZVAL_OFFSET) | |
| 120 | ((pd->yres - 1) << LINEVAL_OFFSET); |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 121 | write32(&exynos_disp_ctrl->vidtcon2, val); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 122 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 123 | write32(&exynos_fimd->vidw00add0b0, (unsigned int)lcdbase); |
| 124 | write32(&exynos_fimd->vidw00add1b0, (unsigned int)lcdbase + fb_size); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 125 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 126 | write32(&exynos_fimd->vidw00add2, pd->xres * 2); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 127 | |
| 128 | val = ((pd->xres - 1) << OSD_RIGHTBOTX_F_OFFSET); |
| 129 | val |= ((pd->yres - 1) << OSD_RIGHTBOTY_F_OFFSET); |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 130 | write32(&exynos_fimd->vidosd0b, val); |
| 131 | write32(&exynos_fimd->vidosd0c, pd->xres * pd->yres); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 132 | |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 133 | setbits32(&exynos_fimd->shadowcon, CHANNEL0_EN); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 134 | |
| 135 | val = BPPMODE_F_RGB_16BIT_565 << BPPMODE_F_OFFSET; |
| 136 | val |= ENWIN_F_ENABLE | HALF_WORD_SWAP_EN; |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 137 | write32(&exynos_fimd->wincon0, val); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 138 | |
| 139 | /* DPCLKCON_ENABLE */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 140 | write32(&exynos_fimd->dpclkcon, 1 << 1); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 141 | } |
| 142 | |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 143 | /* |
| 144 | * Configure DP in slave mode and wait for video stream. |
| 145 | * |
| 146 | * param dp pointer to main s5p-dp structure |
| 147 | * param video_info pointer to main video_info structure. |
| 148 | * return status |
| 149 | */ |
| 150 | static int s5p_dp_config_video(struct s5p_dp_device *dp, |
| 151 | struct video_info *video_info) |
| 152 | { |
| 153 | int timeout = 0; |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 154 | struct exynos5_dp *base = dp->base; |
Aaron Durbin | 4393346 | 2014-09-24 10:27:29 -0500 | [diff] [blame] | 155 | struct stopwatch sw; |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 156 | s5p_dp_config_video_slave_mode(dp, video_info); |
| 157 | |
| 158 | s5p_dp_set_video_color_format(dp, video_info->color_depth, |
| 159 | video_info->color_space, |
| 160 | video_info->dynamic_range, |
| 161 | video_info->ycbcr_coeff); |
| 162 | |
| 163 | if (s5p_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { |
Ronald G. Minnich | 7576f25 | 2013-04-09 14:39:34 -0700 | [diff] [blame] | 164 | printk(BIOS_DEBUG, "PLL is not locked yet.\n"); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 165 | return -ERR_PLL_NOT_UNLOCKED; |
| 166 | } |
| 167 | |
Aaron Durbin | 4393346 | 2014-09-24 10:27:29 -0500 | [diff] [blame] | 168 | stopwatch_init_msecs_expire(&sw, STREAM_ON_TIMEOUT); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 169 | do { |
| 170 | if (s5p_dp_is_slave_video_stream_clock_on(dp) == 0) { |
| 171 | timeout++; |
| 172 | break; |
| 173 | } |
Aaron Durbin | 4393346 | 2014-09-24 10:27:29 -0500 | [diff] [blame] | 174 | } while (!stopwatch_expired(&sw)); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 175 | |
| 176 | if (!timeout) { |
Rob Barnes | d522f38 | 2022-09-12 06:31:47 -0600 | [diff] [blame] | 177 | printk(BIOS_ERR, "Video Clock Not ok after %lldus.\n", |
Aaron Durbin | 4393346 | 2014-09-24 10:27:29 -0500 | [diff] [blame] | 178 | stopwatch_duration_usecs(&sw)); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 179 | return -ERR_VIDEO_CLOCK_BAD; |
| 180 | } |
| 181 | |
| 182 | /* Set to use the register calculated M/N video */ |
| 183 | s5p_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0); |
| 184 | |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 185 | clrbits32(&base->video_ctl_10, FORMAT_SEL); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 186 | |
| 187 | /* Disable video mute */ |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 188 | clrbits32(&base->video_ctl_1, HDCP_VIDEO_MUTE); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 189 | |
| 190 | /* Configure video slave mode */ |
| 191 | s5p_dp_enable_video_master(dp); |
| 192 | |
| 193 | /* Enable video */ |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 194 | setbits32(&base->video_ctl_1, VIDEO_EN); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 195 | timeout = s5p_dp_is_video_stream_on(dp); |
| 196 | |
| 197 | if (timeout) { |
Ronald G. Minnich | 7576f25 | 2013-04-09 14:39:34 -0700 | [diff] [blame] | 198 | printk(BIOS_DEBUG, "Video Stream Not on\n"); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 199 | return -ERR_VIDEO_STREAM_BAD; |
| 200 | } |
| 201 | |
| 202 | return 0; |
| 203 | } |
| 204 | |
| 205 | /* |
| 206 | * Set DP to enhanced mode. We use this for EVT1 |
| 207 | * param dp pointer to main s5p-dp structure |
| 208 | * return status |
| 209 | */ |
| 210 | static int s5p_dp_enable_rx_to_enhanced_mode(struct s5p_dp_device *dp) |
| 211 | { |
| 212 | u8 data; |
| 213 | |
| 214 | if (s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, &data)) { |
Ronald G. Minnich | 7576f25 | 2013-04-09 14:39:34 -0700 | [diff] [blame] | 215 | printk(BIOS_DEBUG, "DPCD read error\n"); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 216 | return -ERR_DPCD_READ_ERROR1; |
| 217 | } |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 218 | if (s5p_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, |
| 219 | DPCD_ENHANCED_FRAME_EN | |
| 220 | (data & DPCD_LANE_COUNT_SET_MASK))) { |
Ronald G. Minnich | 7576f25 | 2013-04-09 14:39:34 -0700 | [diff] [blame] | 221 | printk(BIOS_DEBUG, "DPCD write error\n"); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 222 | return -ERR_DPCD_WRITE_ERROR1; |
| 223 | } |
| 224 | |
| 225 | return 0; |
| 226 | } |
| 227 | |
| 228 | /* |
| 229 | * Enable scrambles mode. We use this for EVT1 |
| 230 | * param dp pointer to main s5p-dp structure |
| 231 | * return status |
| 232 | */ |
| 233 | static int s5p_dp_enable_scramble(struct s5p_dp_device *dp) |
| 234 | { |
| 235 | u8 data; |
| 236 | struct exynos5_dp *base = dp->base; |
| 237 | |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 238 | clrbits32(&base->dp_training_ptn_set, SCRAMBLING_DISABLE); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 239 | |
| 240 | if (s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_TRAINING_PATTERN_SET, |
| 241 | &data)) { |
Ronald G. Minnich | 7576f25 | 2013-04-09 14:39:34 -0700 | [diff] [blame] | 242 | printk(BIOS_DEBUG, "DPCD read error\n"); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 243 | return -ERR_DPCD_READ_ERROR2; |
| 244 | } |
| 245 | |
| 246 | if (s5p_dp_write_byte_to_dpcd(dp, DPCD_ADDR_TRAINING_PATTERN_SET, |
Ronald G. Minnich | 798f664 | 2013-04-09 14:29:42 -0700 | [diff] [blame] | 247 | (u8)(data & ~DPCD_SCRAMBLING_DISABLED))) { |
Ronald G. Minnich | 7576f25 | 2013-04-09 14:39:34 -0700 | [diff] [blame] | 248 | printk(BIOS_DEBUG, "DPCD write error\n"); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 249 | return -ERR_DPCD_WRITE_ERROR2; |
| 250 | } |
| 251 | |
| 252 | return 0; |
| 253 | } |
| 254 | |
| 255 | /* |
| 256 | * Reset DP and prepare DP for init training |
| 257 | * param dp pointer to main s5p-dp structure |
| 258 | */ |
| 259 | static int s5p_dp_init_dp(struct s5p_dp_device *dp) |
| 260 | { |
| 261 | int ret, i; |
| 262 | struct exynos5_dp *base = dp->base; |
| 263 | |
| 264 | for (i = 0; i < DP_INIT_TRIES; i++) { |
| 265 | s5p_dp_reset(dp); |
| 266 | |
| 267 | /* SW defined function Normal operation */ |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 268 | clrbits32(&base->func_en_1, SW_FUNC_EN_N); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 269 | |
| 270 | ret = s5p_dp_init_analog_func(dp); |
| 271 | if (!ret) |
| 272 | break; |
| 273 | |
| 274 | udelay(5000); |
Ronald G. Minnich | 7576f25 | 2013-04-09 14:39:34 -0700 | [diff] [blame] | 275 | printk(BIOS_DEBUG, "LCD retry init, attempt=%d ret=%d\n", i, ret); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 276 | } |
| 277 | if (i == DP_INIT_TRIES) { |
Ronald G. Minnich | 7576f25 | 2013-04-09 14:39:34 -0700 | [diff] [blame] | 278 | printk(BIOS_DEBUG, "LCD initialization failed, ret=%d\n", ret); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 279 | return ret; |
| 280 | } |
| 281 | |
| 282 | s5p_dp_init_aux(dp); |
| 283 | |
| 284 | return ret; |
| 285 | } |
| 286 | |
| 287 | /* |
| 288 | * Set pre-emphasis level |
| 289 | * param dp pointer to main s5p-dp structure |
| 290 | * param pre_emphasis pre-emphasis level |
| 291 | * param lane lane number(0 - 3) |
| 292 | * return status |
| 293 | */ |
| 294 | static int s5p_dp_set_lane_lane_pre_emphasis(struct s5p_dp_device *dp, |
| 295 | int pre_emphasis, int lane) |
| 296 | { |
| 297 | u32 reg; |
| 298 | struct exynos5_dp *base = dp->base; |
| 299 | |
| 300 | reg = pre_emphasis << PRE_EMPHASIS_SET_SHIFT; |
| 301 | switch (lane) { |
| 302 | case 0: |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 303 | write32(&base->ln0_link_trn_ctl, reg); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 304 | break; |
| 305 | case 1: |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 306 | write32(&base->ln1_link_trn_ctl, reg); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 307 | break; |
| 308 | |
| 309 | case 2: |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 310 | write32(&base->ln2_link_trn_ctl, reg); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 311 | break; |
| 312 | |
| 313 | case 3: |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 314 | write32(&base->ln3_link_trn_ctl, reg); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 315 | break; |
| 316 | default: |
Ronald G. Minnich | 7576f25 | 2013-04-09 14:39:34 -0700 | [diff] [blame] | 317 | printk(BIOS_DEBUG, "%s: Invalid lane %d\n", __func__, lane); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 318 | return -ERR_INVALID_LANE; |
| 319 | } |
| 320 | return 0; |
| 321 | } |
| 322 | |
| 323 | /* |
| 324 | * Read supported bandwidth type |
| 325 | * param dp pointer to main s5p-dp structure |
| 326 | * param bandwidth pointer to variable holding bandwidth type |
| 327 | */ |
| 328 | static void s5p_dp_get_max_rx_bandwidth(struct s5p_dp_device *dp, |
| 329 | u8 *bandwidth) |
| 330 | { |
| 331 | u8 data; |
| 332 | |
| 333 | /* |
| 334 | * For DP rev.1.1, Maximum link rate of Main Link lanes |
| 335 | * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps |
| 336 | */ |
| 337 | s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LINK_RATE, &data); |
| 338 | *bandwidth = data; |
| 339 | } |
| 340 | |
| 341 | /* |
| 342 | * Reset DP and prepare DP for init training |
| 343 | * param dp pointer to main s5p-dp structure |
| 344 | * param lane_count pointer to variable holding no of lanes |
| 345 | */ |
| 346 | static void s5p_dp_get_max_rx_lane_count(struct s5p_dp_device *dp, |
| 347 | u8 *lane_count) |
| 348 | { |
| 349 | u8 data; |
| 350 | |
| 351 | /* |
| 352 | * For DP rev.1.1, Maximum number of Main Link lanes |
| 353 | * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes |
| 354 | */ |
| 355 | s5p_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data); |
| 356 | *lane_count = data & DPCD_MAX_LANE_COUNT_MASK; |
| 357 | } |
| 358 | |
| 359 | /* |
| 360 | * DP H/w Link Training. Set DPCD link rate and bandwidth. |
| 361 | * param dp pointer to main s5p-dp structure |
| 362 | * param max_lane No of lanes |
| 363 | * param max_rate bandwidth |
| 364 | * return status |
| 365 | */ |
| 366 | static int s5p_dp_hw_link_training(struct s5p_dp_device *dp, |
| 367 | unsigned int max_lane, |
| 368 | unsigned int max_rate) |
| 369 | { |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 370 | int pll_is_locked = 0; |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 371 | u32 data; |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 372 | int lane; |
Aaron Durbin | 4393346 | 2014-09-24 10:27:29 -0500 | [diff] [blame] | 373 | struct stopwatch sw; |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 374 | struct exynos5_dp *base = dp->base; |
| 375 | |
| 376 | /* Stop Video */ |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 377 | clrbits32(&base->video_ctl_1, VIDEO_EN); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 378 | |
Aaron Durbin | 4393346 | 2014-09-24 10:27:29 -0500 | [diff] [blame] | 379 | stopwatch_init_msecs_expire(&sw, PLL_LOCK_TIMEOUT); |
Gabe Black | 9b764a0 | 2013-07-30 15:11:35 -0700 | [diff] [blame] | 380 | |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 381 | while ((pll_is_locked = s5p_dp_get_pll_lock_status(dp)) == PLL_UNLOCKED) { |
Aaron Durbin | 4393346 | 2014-09-24 10:27:29 -0500 | [diff] [blame] | 382 | if (stopwatch_expired(&sw)) { |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 383 | /* Ignore this error, and try to continue */ |
| 384 | printk(BIOS_ERR, "PLL is not locked yet.\n"); |
| 385 | break; |
| 386 | } |
| 387 | } |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 388 | printk(BIOS_SPEW, "PLL is %slocked\n", |
| 389 | pll_is_locked == PLL_LOCKED ? "": "not "); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 390 | /* Reset Macro */ |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 391 | setbits32(&base->dp_phy_test, MACRO_RST); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 392 | |
| 393 | /* 10 us is the minimum reset time. */ |
| 394 | udelay(10); |
| 395 | |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 396 | clrbits32(&base->dp_phy_test, MACRO_RST); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 397 | |
| 398 | /* Set TX pre-emphasis to minimum */ |
| 399 | for (lane = 0; lane < max_lane; lane++) |
| 400 | if (s5p_dp_set_lane_lane_pre_emphasis(dp, |
Ronald G. Minnich | 798f664 | 2013-04-09 14:29:42 -0700 | [diff] [blame] | 401 | PRE_EMPHASIS_LEVEL_0, lane)) { |
Ronald G. Minnich | 7576f25 | 2013-04-09 14:39:34 -0700 | [diff] [blame] | 402 | printk(BIOS_DEBUG, "Unable to set pre emphasis level\n"); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 403 | return -ERR_PRE_EMPHASIS_LEVELS; |
| 404 | } |
| 405 | |
| 406 | /* All DP analog module power up */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 407 | write32(&base->dp_phy_pd, 0x00); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 408 | |
| 409 | /* Initialize by reading RX's DPCD */ |
| 410 | s5p_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate); |
| 411 | s5p_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count); |
| 412 | |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 413 | printk(BIOS_SPEW, "%s: rate 0x%x, lane_count %d\n", __func__, |
| 414 | dp->link_train.link_rate, dp->link_train.lane_count); |
| 415 | |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 416 | if ((dp->link_train.link_rate != LINK_RATE_1_62GBPS) && |
| 417 | (dp->link_train.link_rate != LINK_RATE_2_70GBPS)) { |
Ronald G. Minnich | 7576f25 | 2013-04-09 14:39:34 -0700 | [diff] [blame] | 418 | printk(BIOS_DEBUG, "Rx Max Link Rate is abnormal :%x !\n", |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 419 | dp->link_train.link_rate); |
| 420 | /* Not Retrying */ |
| 421 | return -ERR_LINK_RATE_ABNORMAL; |
| 422 | } |
| 423 | |
| 424 | if (dp->link_train.lane_count == 0) { |
Ronald G. Minnich | 7576f25 | 2013-04-09 14:39:34 -0700 | [diff] [blame] | 425 | printk(BIOS_DEBUG, "Rx Max Lane count is abnormal :%x !\n", |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 426 | dp->link_train.lane_count); |
| 427 | /* Not retrying */ |
| 428 | return -ERR_MAX_LANE_COUNT_ABNORMAL; |
| 429 | } |
| 430 | |
| 431 | /* Setup TX lane count & rate */ |
| 432 | if (dp->link_train.lane_count > max_lane) |
| 433 | dp->link_train.lane_count = max_lane; |
| 434 | if (dp->link_train.link_rate > max_rate) |
| 435 | dp->link_train.link_rate = max_rate; |
| 436 | |
| 437 | /* Set link rate and count as you want to establish*/ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 438 | write32(&base->lane_count_set, dp->link_train.lane_count); |
| 439 | write32(&base->link_bw_set, dp->link_train.link_rate); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 440 | |
| 441 | /* Set sink to D0 (Sink Not Ready) mode. */ |
| 442 | s5p_dp_write_byte_to_dpcd(dp, DPCD_ADDR_SINK_POWER_STATE, |
| 443 | DPCD_SET_POWER_STATE_D0); |
| 444 | |
| 445 | /* Start HW link training */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 446 | write32(&base->dp_hw_link_training, HW_TRAINING_EN); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 447 | |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 448 | /* Wait until HW link training done */ |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 449 | s5p_dp_wait_hw_link_training_done(dp); |
| 450 | |
| 451 | /* Get hardware link training status */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 452 | data = read32(&base->dp_hw_link_training); |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 453 | printk(BIOS_SPEW, "hardware link training status: 0x%08x\n", data); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 454 | if (data != 0) { |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 455 | printk(BIOS_ERR, " H/W link training failure: 0x%x\n", data); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 456 | return -ERR_LINK_TRAINING_FAILURE; |
| 457 | } |
| 458 | |
| 459 | /* Get Link Bandwidth */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 460 | data = read32(&base->link_bw_set); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 461 | |
| 462 | dp->link_train.link_rate = data; |
| 463 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 464 | data = read32(&base->lane_count_set); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 465 | dp->link_train.lane_count = data; |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 466 | printk(BIOS_SPEW, "Done training: Link bandwidth: 0x%x, lane_count: %d\n", |
| 467 | dp->link_train.link_rate, data); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 468 | |
| 469 | return 0; |
| 470 | } |
| 471 | |
| 472 | /* |
| 473 | * Initialize DP display |
| 474 | */ |
David Hendricks | cd4c8c1 | 2013-04-12 16:02:44 -0700 | [diff] [blame] | 475 | int dp_controller_init(struct s5p_dp_device *dp_device) |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 476 | { |
| 477 | int ret; |
| 478 | struct s5p_dp_device *dp = dp_device; |
| 479 | struct exynos5_dp *base; |
| 480 | |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 481 | clock_init_dp_clock(); |
| 482 | |
| 483 | power_enable_dp_phy(); |
| 484 | ret = s5p_dp_init_dp(dp); |
| 485 | if (ret) { |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 486 | printk(BIOS_ERR, "%s: Could not initialize dp\n", __func__); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 487 | return ret; |
| 488 | } |
| 489 | |
| 490 | ret = s5p_dp_hw_link_training(dp, dp->video_info->lane_count, |
| 491 | dp->video_info->link_rate); |
| 492 | if (ret) { |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 493 | printk(BIOS_ERR, "unable to do link train\n"); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 494 | return ret; |
| 495 | } |
| 496 | /* Minimum delay after H/w Link training */ |
| 497 | udelay(1000); |
| 498 | |
| 499 | ret = s5p_dp_enable_scramble(dp); |
| 500 | if (ret) { |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 501 | printk(BIOS_ERR, "unable to set scramble mode\n"); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 502 | return ret; |
| 503 | } |
| 504 | |
| 505 | ret = s5p_dp_enable_rx_to_enhanced_mode(dp); |
| 506 | if (ret) { |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 507 | printk(BIOS_ERR, "unable to set enhanced mode\n"); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 508 | return ret; |
| 509 | } |
| 510 | |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 511 | base = dp->base; |
| 512 | /* Enable enhanced mode */ |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 513 | setbits32(&base->sys_ctl_4, ENHANCED); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 514 | |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 515 | write32(&base->lane_count_set, dp->link_train.lane_count); |
| 516 | write32(&base->link_bw_set, dp->link_train.link_rate); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 517 | |
| 518 | s5p_dp_init_video(dp); |
| 519 | ret = s5p_dp_config_video(dp, dp->video_info); |
| 520 | if (ret) { |
Ronald G. Minnich | 2810afa | 2013-04-18 18:09:24 -0700 | [diff] [blame] | 521 | printk(BIOS_ERR, "unable to config video\n"); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 522 | return ret; |
| 523 | } |
| 524 | |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 525 | return 0; |
| 526 | } |
| 527 | |
| 528 | /** |
| 529 | * Init the LCD controller |
| 530 | * |
Martin Roth | 4934818 | 2016-07-06 10:17:19 -0600 | [diff] [blame] | 531 | * @param panel_data |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 532 | * @param lcdbase Base address of LCD frame buffer |
| 533 | * @return 0 if ok, -ve error code on error |
| 534 | */ |
Gabe Black | 1e797bd | 2013-05-18 15:58:46 -0700 | [diff] [blame] | 535 | int lcd_ctrl_init(unsigned long int fb_size, |
| 536 | struct exynos5_fimd_panel *panel_data, void *lcdbase) |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 537 | { |
| 538 | int ret = 0; |
| 539 | |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 540 | fimd_bypass(); |
Gabe Black | 1e797bd | 2013-05-18 15:58:46 -0700 | [diff] [blame] | 541 | fb_init(fb_size, lcdbase, panel_data); |
Ronald G. Minnich | 6bde149 | 2013-03-01 10:18:14 -0800 | [diff] [blame] | 542 | return ret; |
| 543 | } |