Cathy Hsu | aaba0cf | 2024-01-16 10:59:40 +0000 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | /* |
| 3 | * MIPI-DSI based ct3e AMOLED LCD panel driver. |
| 4 | * |
| 5 | * Copyright (c) 2024 Google LLC |
| 6 | */ |
| 7 | |
| 8 | #include <drm/display/drm_dsc_helper.h> |
| 9 | #include <linux/debugfs.h> |
| 10 | #include <linux/module.h> |
| 11 | #include <video/mipi_display.h> |
| 12 | |
| 13 | #include "trace/dpu_trace.h" |
| 14 | #include "panel/panel-samsung-drv.h" |
| 15 | |
| 16 | /* DSC1.2 */ |
| 17 | static const struct drm_dsc_config pps_config = { |
| 18 | .line_buf_depth = 9, |
| 19 | .bits_per_component = 8, |
| 20 | .convert_rgb = true, |
| 21 | .slice_width = 540, |
| 22 | .slice_height = 101, |
| 23 | .simple_422 = false, |
| 24 | .pic_width = 1080, |
| 25 | .pic_height = 2424, |
| 26 | .rc_tgt_offset_high = 3, |
| 27 | .rc_tgt_offset_low = 3, |
| 28 | .bits_per_pixel = 128, |
| 29 | .rc_edge_factor = 6, |
| 30 | .rc_quant_incr_limit1 = 11, |
| 31 | .rc_quant_incr_limit0 = 11, |
| 32 | .initial_xmit_delay = 512, |
| 33 | .initial_dec_delay = 526, |
| 34 | .block_pred_enable = true, |
| 35 | .first_line_bpg_offset = 12, |
| 36 | .initial_offset = 6144, |
| 37 | .rc_buf_thresh = { |
| 38 | 14, 28, 42, 56, |
| 39 | 70, 84, 98, 105, |
| 40 | 112, 119, 121, 123, |
| 41 | 125, 126 |
| 42 | }, |
| 43 | .rc_range_params = { |
| 44 | {.range_min_qp = 0, .range_max_qp = 4, .range_bpg_offset = 2}, |
| 45 | {.range_min_qp = 0, .range_max_qp = 4, .range_bpg_offset = 0}, |
| 46 | {.range_min_qp = 1, .range_max_qp = 5, .range_bpg_offset = 0}, |
| 47 | {.range_min_qp = 1, .range_max_qp = 6, .range_bpg_offset = 62}, |
| 48 | {.range_min_qp = 3, .range_max_qp = 7, .range_bpg_offset = 60}, |
| 49 | {.range_min_qp = 3, .range_max_qp = 7, .range_bpg_offset = 58}, |
| 50 | {.range_min_qp = 3, .range_max_qp = 7, .range_bpg_offset = 56}, |
| 51 | {.range_min_qp = 3, .range_max_qp = 8, .range_bpg_offset = 56}, |
| 52 | {.range_min_qp = 3, .range_max_qp = 9, .range_bpg_offset = 56}, |
| 53 | {.range_min_qp = 3, .range_max_qp = 10, .range_bpg_offset = 54}, |
| 54 | {.range_min_qp = 5, .range_max_qp = 11, .range_bpg_offset = 54}, |
| 55 | {.range_min_qp = 5, .range_max_qp = 12, .range_bpg_offset = 52}, |
| 56 | {.range_min_qp = 5, .range_max_qp = 13, .range_bpg_offset = 52}, |
| 57 | {.range_min_qp = 7, .range_max_qp = 13, .range_bpg_offset = 52}, |
| 58 | {.range_min_qp = 13, .range_max_qp = 15, .range_bpg_offset = 52} |
| 59 | }, |
| 60 | .rc_model_size = 8192, |
| 61 | .flatness_min_qp = 3, |
| 62 | .flatness_max_qp = 12, |
| 63 | .initial_scale_value = 32, |
| 64 | .scale_decrement_interval = 7, |
| 65 | .scale_increment_interval = 2517, |
| 66 | .nfl_bpg_offset = 246, |
| 67 | .slice_bpg_offset = 258, |
| 68 | .final_offset = 4336, |
| 69 | .vbr_enable = false, |
| 70 | .slice_chunk_size = 540, |
| 71 | .dsc_version_minor = 2, |
| 72 | .dsc_version_major = 1, |
| 73 | .native_422 = false, |
| 74 | .native_420 = false, |
| 75 | .second_line_bpg_offset = 0, |
| 76 | .nsl_bpg_offset = 0, |
| 77 | .second_line_offset_adj = 0, |
| 78 | }; |
| 79 | |
| 80 | |
| 81 | #define CT3E_WRCTRLD_DIMMING_BIT 0x08 |
| 82 | #define CT3E_WRCTRLD_BCTRL_BIT 0x20 |
| 83 | |
| 84 | static const u8 test_key_enable[] = { 0xF0, 0x5A, 0x5A }; |
| 85 | static const u8 test_key_disable[] = { 0xF0, 0xA5, 0xA5 }; |
| 86 | static const u8 pixel_off[] = { 0x22 }; |
| 87 | |
| 88 | static const struct exynos_dsi_cmd ct3e_off_cmds[] = { |
| 89 | EXYNOS_DSI_CMD_SEQ_DELAY(MIPI_DCS_SET_DISPLAY_OFF), |
| 90 | EXYNOS_DSI_CMD_SEQ_DELAY(120, MIPI_DCS_ENTER_SLEEP_MODE), |
| 91 | }; |
| 92 | static DEFINE_EXYNOS_CMD_SET(ct3e_off); |
| 93 | |
| 94 | static const struct exynos_dsi_cmd ct3e_lp_cmds[] = { |
| 95 | EXYNOS_DSI_CMD_SEQ(MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24), |
| 96 | }; |
| 97 | static DEFINE_EXYNOS_CMD_SET(ct3e_lp); |
| 98 | |
| 99 | static const struct exynos_dsi_cmd ct3e_lp_low_cmds[] = { |
| 100 | EXYNOS_DSI_CMD_SEQ(MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0x01, 0x7E), |
| 101 | }; |
| 102 | |
| 103 | static const struct exynos_dsi_cmd ct3e_lp_high_cmds[] = { |
| 104 | EXYNOS_DSI_CMD_SEQ(MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0x03, 0x1A), |
| 105 | }; |
| 106 | |
| 107 | static const struct exynos_binned_lp ct3e_binned_lp[] = { |
| 108 | /* low threshold 40 nits */ |
| 109 | BINNED_LP_MODE_TIMING("low", 717, ct3e_lp_low_cmds, |
| 110 | 12, 12 + 50), |
| 111 | BINNED_LP_MODE_TIMING("high", 3427, ct3e_lp_high_cmds, |
| 112 | 12, 12 + 50), |
| 113 | }; |
| 114 | |
| 115 | static const struct exynos_dsi_cmd ct3e_init_cmds[] = { |
| 116 | /* TE on */ |
| 117 | EXYNOS_DSI_CMD_SEQ(MIPI_DCS_SET_TEAR_ON), |
| 118 | |
| 119 | /* TE2 setting */ |
| 120 | EXYNOS_DSI_CMD0(test_key_enable), |
| 121 | EXYNOS_DSI_CMD_SEQ(0xB0, 0x00, 0x26, 0xB9), |
| 122 | EXYNOS_DSI_CMD_SEQ(0xB9, 0x00, 0x00, 0x10, 0x00, 0x00, |
| 123 | 0x3D, 0x00, 0x09, 0x90, 0x00, 0x09, 0x90), |
Cathy Hsu | aaba0cf | 2024-01-16 10:59:40 +0000 | [diff] [blame] | 124 | |
| 125 | /* CASET: 1080 */ |
| 126 | EXYNOS_DSI_CMD_SEQ(MIPI_DCS_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x04, 0x37), |
| 127 | |
| 128 | /* PASET: 2424 */ |
| 129 | EXYNOS_DSI_CMD_SEQ(MIPI_DCS_SET_PAGE_ADDRESS, 0x00, 0x00, 0x09, 0x77), |
| 130 | |
| 131 | /* FFC 865Mbps */ |
Cathy Hsu | aaba0cf | 2024-01-16 10:59:40 +0000 | [diff] [blame] | 132 | EXYNOS_DSI_CMD_SEQ(0xFC, 0x5A, 0x5A), |
| 133 | EXYNOS_DSI_CMD_SEQ(0xB0, 0x00, 0x3A, 0xC5), |
| 134 | EXYNOS_DSI_CMD_SEQ(0xC5, 0x5E, 0xB5), /* 865Mbps FFC Setting */ |
| 135 | EXYNOS_DSI_CMD_SEQ(0xB0, 0x00, 0x36, 0xC5), |
| 136 | EXYNOS_DSI_CMD_SEQ(0xC5, 0x11, 0x10, 0x50, 0x05), |
| 137 | EXYNOS_DSI_CMD_SEQ(0xFC, 0xA5, 0xA5), |
Cathy Hsu | 20957ee | 2024-01-24 06:19:01 +0000 | [diff] [blame] | 138 | |
| 139 | /* TSP HSYNC setting */ |
| 140 | EXYNOS_DSI_CMD_SEQ(0xB0, 0x00, 0x42, 0xB9), |
| 141 | EXYNOS_DSI_CMD_SEQ(0xB9, 0x19), |
| 142 | EXYNOS_DSI_CMD_SEQ(0xB0, 0x00, 0x46, 0xB9), |
| 143 | EXYNOS_DSI_CMD_SEQ(0xB9, 0xB0), |
Cathy Hsu | aaba0cf | 2024-01-16 10:59:40 +0000 | [diff] [blame] | 144 | EXYNOS_DSI_CMD0(test_key_disable), |
| 145 | }; |
| 146 | static DEFINE_EXYNOS_CMD_SET(ct3e_init); |
| 147 | |
| 148 | /** |
| 149 | * struct ct3e_panel - panel specific runtime info |
| 150 | * |
| 151 | * This struct maintains ct3e panel specific runtime info, any fixed details about panel |
| 152 | * should most likely go into struct exynos_panel_desc |
| 153 | */ |
| 154 | struct ct3e_panel { |
| 155 | /** @base: base panel struct */ |
| 156 | struct exynos_panel base; |
| 157 | /** |
| 158 | * @is_pixel_off: pixel-off command is sent to panel. Only sending normal-on or resetting |
| 159 | * panel can recover to normal mode after entering pixel-off state. |
| 160 | */ |
| 161 | bool is_pixel_off; |
| 162 | }; |
| 163 | #define to_spanel(ctx) container_of(ctx, struct ct3e_panel, base) |
| 164 | |
| 165 | static void ct3e_change_frequency(struct exynos_panel *ctx, |
| 166 | const struct exynos_panel_mode *pmode) |
| 167 | { |
| 168 | u32 vrefresh = drm_mode_vrefresh(&pmode->mode); |
| 169 | |
| 170 | if (!ctx || (vrefresh != 60 && vrefresh != 120)) |
| 171 | return; |
| 172 | |
| 173 | EXYNOS_DCS_BUF_ADD_SET(ctx, test_key_enable); |
| 174 | EXYNOS_DCS_BUF_ADD(ctx, 0x83, (vrefresh == 120) ? 0x00 : 0x08); |
| 175 | EXYNOS_DCS_BUF_ADD(ctx, 0xF7, 0x2F); |
| 176 | EXYNOS_DCS_BUF_ADD_SET_AND_FLUSH(ctx, test_key_disable); |
| 177 | |
| 178 | dev_info(ctx->dev, "%s: change to %uHz\n", __func__, vrefresh); |
| 179 | return; |
| 180 | } |
| 181 | |
| 182 | static void ct3e_update_wrctrld(struct exynos_panel *ctx) |
| 183 | { |
| 184 | u8 val = CT3E_WRCTRLD_BCTRL_BIT; |
| 185 | |
| 186 | if (ctx->dimming_on) |
| 187 | val |= CT3E_WRCTRLD_DIMMING_BIT; |
| 188 | |
| 189 | dev_dbg(ctx->dev, |
| 190 | "%s(wrctrld:0x%x, hbm: %s, dimming: %s\n", |
| 191 | __func__, val, IS_HBM_ON(ctx->hbm_mode) ? "on" : "off", |
| 192 | ctx->dimming_on ? "on" : "off"); |
| 193 | |
| 194 | EXYNOS_DCS_BUF_ADD_AND_FLUSH(ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, val); |
| 195 | } |
| 196 | |
| 197 | static int ct3e_set_brightness(struct exynos_panel *ctx, u16 br) |
| 198 | { |
| 199 | u16 brightness; |
| 200 | u32 max_brightness; |
| 201 | struct ct3e_panel *spanel = to_spanel(ctx); |
| 202 | |
| 203 | if (ctx->current_mode && ctx->current_mode->exynos_mode.is_lp_mode) { |
| 204 | const struct exynos_panel_funcs *funcs; |
| 205 | |
| 206 | /* don't stay at pixel-off state in AOD, or black screen is possibly seen */ |
| 207 | if (spanel->is_pixel_off) { |
| 208 | EXYNOS_DCS_WRITE_SEQ(ctx, MIPI_DCS_ENTER_NORMAL_MODE); |
| 209 | spanel->is_pixel_off = false; |
| 210 | } |
| 211 | funcs = ctx->desc->exynos_panel_func; |
| 212 | if (funcs && funcs->set_binned_lp) |
| 213 | funcs->set_binned_lp(ctx, br); |
| 214 | return 0; |
| 215 | } |
| 216 | |
| 217 | /* Use pixel off command instead of setting DBV 0 */ |
| 218 | if (!br) { |
| 219 | if (!spanel->is_pixel_off) { |
| 220 | EXYNOS_DCS_WRITE_TABLE(ctx, pixel_off); |
| 221 | spanel->is_pixel_off = true; |
| 222 | dev_dbg(ctx->dev, "%s: pixel off instead of dbv 0\n", __func__); |
| 223 | } |
| 224 | return 0; |
| 225 | } else if (br && spanel->is_pixel_off) { |
| 226 | EXYNOS_DCS_WRITE_SEQ(ctx, MIPI_DCS_ENTER_NORMAL_MODE); |
| 227 | spanel->is_pixel_off = false; |
| 228 | } |
| 229 | |
| 230 | if (!ctx->desc->brt_capability) { |
| 231 | dev_err(ctx->dev, "no available brightness capability\n"); |
| 232 | return -EINVAL; |
| 233 | } |
| 234 | |
| 235 | max_brightness = ctx->desc->brt_capability->hbm.level.max; |
| 236 | |
| 237 | if (br > max_brightness) { |
| 238 | br = max_brightness; |
| 239 | dev_warn(ctx->dev, "%s: capped to dbv(%d)\n", __func__, |
| 240 | max_brightness); |
| 241 | } |
| 242 | |
| 243 | brightness = __builtin_bswap16(br); |
| 244 | |
| 245 | return exynos_dcs_set_brightness(ctx, brightness); |
| 246 | } |
| 247 | |
| 248 | static void ct3e_set_hbm_mode(struct exynos_panel *ctx, |
| 249 | enum exynos_hbm_mode mode) |
| 250 | { |
| 251 | ctx->hbm_mode = mode; |
| 252 | |
| 253 | EXYNOS_DCS_BUF_ADD_SET(ctx, test_key_enable); |
| 254 | |
| 255 | EXYNOS_DCS_BUF_ADD(ctx, 0xB0, 0x00, 0x01, 0xBD); |
| 256 | if (IS_HBM_ON_IRC_OFF(ctx->hbm_mode)) { |
| 257 | EXYNOS_DCS_BUF_ADD(ctx, 0xBD, 0x80); /* HBM EM Cyc Set */ |
| 258 | EXYNOS_DCS_BUF_ADD(ctx, 0xB0, 0x00, 0x2E, 0xBD); /* Global para */ |
| 259 | EXYNOS_DCS_BUF_ADD(ctx, 0xBD, 0x00, 0x01); /* HBM EM Cyc Set */ |
| 260 | } else { |
| 261 | EXYNOS_DCS_BUF_ADD(ctx, 0xBD, 0x81); /* Normal EM Cyc Set */ |
| 262 | EXYNOS_DCS_BUF_ADD(ctx, 0xB0, 0x00, 0x2E, 0xBD); /* Global para */ |
| 263 | EXYNOS_DCS_BUF_ADD(ctx, 0xBD, 0x00, 0x02); /* Normal EM Cyc Set */ |
| 264 | } |
| 265 | EXYNOS_DCS_BUF_ADD(ctx, 0xF7, 0x2F); |
| 266 | |
| 267 | EXYNOS_DCS_BUF_ADD_SET_AND_FLUSH(ctx, test_key_disable); |
| 268 | |
| 269 | dev_info(ctx->dev, "hbm_on=%d hbm_ircoff=%d.\n", IS_HBM_ON(ctx->hbm_mode), |
| 270 | IS_HBM_ON_IRC_OFF(ctx->hbm_mode)); |
| 271 | } |
| 272 | |
| 273 | static void ct3e_set_dimming_on(struct exynos_panel *exynos_panel, |
| 274 | bool dimming_on) |
| 275 | { |
| 276 | const struct exynos_panel_mode *pmode = exynos_panel->current_mode; |
| 277 | exynos_panel->dimming_on = dimming_on; |
| 278 | |
| 279 | if (pmode->exynos_mode.is_lp_mode) { |
| 280 | dev_warn(exynos_panel->dev, "in lp mode, skip to update\n"); |
| 281 | return; |
| 282 | } |
| 283 | |
| 284 | ct3e_update_wrctrld(exynos_panel); |
| 285 | } |
| 286 | |
| 287 | static void ct3e_mode_set(struct exynos_panel *ctx, |
| 288 | const struct exynos_panel_mode *pmode) |
| 289 | { |
| 290 | ct3e_change_frequency(ctx, pmode); |
| 291 | } |
| 292 | |
| 293 | static bool ct3e_is_mode_seamless(const struct exynos_panel *ctx, |
| 294 | const struct exynos_panel_mode *pmode) |
| 295 | { |
| 296 | /* seamless mode switch is possible if only changing refresh rate */ |
| 297 | return drm_mode_equal_no_clocks(&ctx->current_mode->mode, &pmode->mode); |
| 298 | } |
| 299 | |
| 300 | static void ct3e_debugfs_init(struct drm_panel *panel, struct dentry *root) |
| 301 | { |
| 302 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { |
| 303 | struct exynos_panel *ctx = container_of(panel, struct exynos_panel, panel); |
| 304 | struct dentry *panel_root, *csroot; |
| 305 | |
| 306 | if (!ctx) |
| 307 | return; |
| 308 | |
| 309 | panel_root = debugfs_lookup("panel", root); |
| 310 | if (!panel_root) |
| 311 | return; |
| 312 | |
| 313 | csroot = debugfs_lookup("cmdsets", panel_root); |
| 314 | if (!csroot) |
| 315 | goto panel_out; |
| 316 | |
| 317 | exynos_panel_debugfs_create_cmdset(ctx, csroot, &ct3e_init_cmd_set, "init"); |
| 318 | |
| 319 | dput(csroot); |
| 320 | |
| 321 | panel_out: |
| 322 | dput(panel_root); |
| 323 | } |
| 324 | } |
| 325 | |
| 326 | static void ct3e_get_panel_rev(struct exynos_panel *ctx, u32 id) |
| 327 | { |
| 328 | /* extract command 0xDB */ |
| 329 | u8 build_code = (id & 0xFF00) >> 8; |
| 330 | u8 main = (build_code & 0xE0) >> 3; |
| 331 | u8 sub = (build_code & 0x0C) >> 2; |
| 332 | u8 rev = main | sub; |
| 333 | |
| 334 | exynos_panel_get_panel_rev(ctx, rev); |
| 335 | } |
| 336 | |
| 337 | static void ct3e_set_lp_mode(struct exynos_panel *ctx, |
| 338 | const struct exynos_panel_mode *pmode) |
| 339 | { |
| 340 | exynos_panel_set_lp_mode(ctx, pmode); |
| 341 | } |
| 342 | |
| 343 | static void ct3e_set_nolp_mode(struct exynos_panel *ctx, |
| 344 | const struct exynos_panel_mode *pmode) |
| 345 | { |
| 346 | const struct exynos_panel_mode *current_mode = ctx->current_mode; |
| 347 | unsigned int vrefresh = current_mode ? drm_mode_vrefresh(¤t_mode->mode) : 30; |
| 348 | unsigned int te_usec = current_mode ? current_mode->exynos_mode.te_usec : 1109; |
| 349 | |
| 350 | if (!is_panel_active(ctx)) |
| 351 | return; |
| 352 | |
| 353 | /* AOD Mode Off Setting */ |
| 354 | EXYNOS_DCS_BUF_ADD_SET(ctx, test_key_enable); |
Cathy Hsu | aaba0cf | 2024-01-16 10:59:40 +0000 | [diff] [blame] | 355 | EXYNOS_DCS_BUF_ADD(ctx, 0x53, 0x20); |
| 356 | EXYNOS_DCS_BUF_ADD_SET_AND_FLUSH(ctx, test_key_disable); |
| 357 | |
| 358 | /* backlight control and dimming */ |
| 359 | ct3e_update_wrctrld(ctx); |
| 360 | ct3e_change_frequency(ctx, pmode); |
| 361 | |
| 362 | DPU_ATRACE_BEGIN("ct3e_wait_one_vblank"); |
| 363 | exynos_panel_wait_for_vsync_done(ctx, te_usec, |
| 364 | EXYNOS_VREFRESH_TO_PERIOD_USEC(vrefresh)); |
| 365 | |
| 366 | /* Additional sleep time to account for TE variability */ |
| 367 | usleep_range(1000, 1010); |
| 368 | DPU_ATRACE_END("ct3e_wait_one_vblank"); |
| 369 | |
| 370 | dev_info(ctx->dev, "exit LP mode\n"); |
| 371 | } |
| 372 | |
| 373 | static int ct3e_enable(struct drm_panel *panel) |
| 374 | { |
| 375 | struct exynos_panel *ctx = container_of(panel, struct exynos_panel, panel); |
| 376 | const struct exynos_panel_mode *pmode = ctx->current_mode; |
| 377 | struct drm_dsc_picture_parameter_set pps_payload; |
| 378 | |
| 379 | if (!pmode) { |
| 380 | dev_err(ctx->dev, "no current mode set\n"); |
| 381 | return -EINVAL; |
| 382 | } |
| 383 | |
| 384 | dev_info(ctx->dev, "%s\n", __func__); |
| 385 | |
| 386 | exynos_panel_reset(ctx); |
| 387 | |
| 388 | /* sleep out */ |
| 389 | EXYNOS_DCS_WRITE_SEQ_DELAY(ctx, 120, MIPI_DCS_EXIT_SLEEP_MODE); |
| 390 | |
| 391 | /* initial command */ |
| 392 | exynos_panel_send_cmd_set(ctx, &ct3e_init_cmd_set); |
| 393 | |
| 394 | /* frequency */ |
| 395 | ct3e_change_frequency(ctx, pmode); |
| 396 | |
| 397 | /* DSC related configuration */ |
| 398 | exynos_dcs_compression_mode(ctx, 0x1); |
| 399 | drm_dsc_pps_payload_pack(&pps_payload, &pps_config); |
| 400 | EXYNOS_PPS_WRITE_BUF(ctx, &pps_payload); |
| 401 | /* DSC Enable */ |
| 402 | EXYNOS_DCS_BUF_ADD(ctx, 0xC2, 0x14); |
| 403 | EXYNOS_DCS_BUF_ADD(ctx, 0x9D, 0x01); |
| 404 | |
| 405 | /* dimming and HBM */ |
| 406 | ct3e_update_wrctrld(ctx); |
| 407 | |
| 408 | if (pmode->exynos_mode.is_lp_mode) |
| 409 | exynos_panel_set_lp_mode(ctx, pmode); |
| 410 | |
| 411 | /* display on */ |
| 412 | EXYNOS_DCS_WRITE_SEQ(ctx, MIPI_DCS_SET_DISPLAY_ON); |
| 413 | |
| 414 | return 0; |
| 415 | } |
| 416 | |
| 417 | static int ct3e_panel_probe(struct mipi_dsi_device *dsi) |
| 418 | { |
| 419 | struct ct3e_panel *spanel; |
| 420 | |
| 421 | spanel = devm_kzalloc(&dsi->dev, sizeof(*spanel), GFP_KERNEL); |
| 422 | if (!spanel) |
| 423 | return -ENOMEM; |
| 424 | |
| 425 | spanel->is_pixel_off = false; |
| 426 | |
| 427 | return exynos_panel_common_init(dsi, &spanel->base); |
| 428 | } |
| 429 | |
| 430 | static const struct exynos_display_underrun_param underrun_param = { |
| 431 | .te_idle_us = 500, |
| 432 | .te_var = 1, |
| 433 | }; |
| 434 | |
| 435 | static const u16 WIDTH_MM = 65, HEIGHT_MM = 146; |
| 436 | static const u16 HDISPLAY = 1080, VDISPLAY = 2424; |
| 437 | static const u16 HFP = 44, HSA = 16, HBP = 20; |
| 438 | static const u16 VFP = 10, VSA = 6, VBP = 10; |
| 439 | |
| 440 | #define CT3E_DSC {\ |
| 441 | .enabled = true,\ |
| 442 | .dsc_count = 1,\ |
| 443 | .slice_count = 2,\ |
| 444 | .slice_height = 101,\ |
| 445 | .cfg = &pps_config,\ |
| 446 | } |
| 447 | |
| 448 | static const struct exynos_panel_mode ct3e_modes[] = { |
| 449 | { |
| 450 | .mode = { |
| 451 | .name = "1080x2424x60", |
| 452 | .clock = 170520, |
| 453 | .hdisplay = HDISPLAY, |
| 454 | .hsync_start = HDISPLAY + HFP, |
| 455 | .hsync_end = HDISPLAY + HFP + HSA, |
| 456 | .htotal = HDISPLAY + HFP + HSA + HBP, |
| 457 | .vdisplay = VDISPLAY, |
| 458 | .vsync_start = VDISPLAY + VFP, |
| 459 | .vsync_end = VDISPLAY + VFP + VSA, |
| 460 | .vtotal = VDISPLAY + VFP + VSA + VBP, |
| 461 | .flags = 0, |
| 462 | .width_mm = WIDTH_MM, |
| 463 | .height_mm = HEIGHT_MM, |
| 464 | }, |
| 465 | .exynos_mode = { |
| 466 | .mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS, |
| 467 | .vblank_usec = 120, |
Cathy Hsu | 20957ee | 2024-01-24 06:19:01 +0000 | [diff] [blame] | 468 | .te_usec = 8605, |
Cathy Hsu | aaba0cf | 2024-01-16 10:59:40 +0000 | [diff] [blame] | 469 | .bpc = 8, |
| 470 | .dsc = CT3E_DSC, |
| 471 | .underrun_param = &underrun_param, |
| 472 | }, |
| 473 | }, |
| 474 | { |
| 475 | .mode = { |
| 476 | .name = "1080x2424x120", |
| 477 | .clock = 341040, |
| 478 | .hdisplay = HDISPLAY, |
| 479 | .hsync_start = HDISPLAY + HFP, |
| 480 | .hsync_end = HDISPLAY + HFP + HSA, |
| 481 | .htotal = HDISPLAY + HFP + HSA + HBP, |
| 482 | .vdisplay = VDISPLAY, |
| 483 | .vsync_start = VDISPLAY + VFP, |
| 484 | .vsync_end = VDISPLAY + VFP + VSA, |
| 485 | .vtotal = VDISPLAY + VFP + VSA + VBP, |
| 486 | .flags = 0, |
| 487 | .width_mm = WIDTH_MM, |
| 488 | .height_mm = HEIGHT_MM, |
| 489 | }, |
| 490 | .exynos_mode = { |
| 491 | .mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS, |
| 492 | .vblank_usec = 120, |
Cathy Hsu | aaba0cf | 2024-01-16 10:59:40 +0000 | [diff] [blame] | 493 | .te_usec = 276, |
| 494 | .bpc = 8, |
| 495 | .dsc = CT3E_DSC, |
| 496 | .underrun_param = &underrun_param, |
| 497 | }, |
| 498 | }, |
| 499 | }; |
| 500 | |
| 501 | const struct brightness_capability ct3e_brightness_capability = { |
| 502 | .normal = { |
| 503 | .nits = { |
| 504 | .min = 2, |
| 505 | .max = 1200, |
| 506 | }, |
| 507 | .level = { |
| 508 | .min = 184, |
| 509 | .max = 3427, |
| 510 | }, |
| 511 | .percentage = { |
| 512 | .min = 0, |
| 513 | .max = 67, |
| 514 | }, |
| 515 | }, |
| 516 | .hbm = { |
| 517 | .nits = { |
| 518 | .min = 1200, |
| 519 | .max = 1800, |
| 520 | }, |
| 521 | .level = { |
| 522 | .min = 3428, |
| 523 | .max = 4095, |
| 524 | }, |
| 525 | .percentage = { |
| 526 | .min = 67, |
| 527 | .max = 100, |
| 528 | }, |
| 529 | }, |
| 530 | }; |
| 531 | |
| 532 | static const struct exynos_panel_mode ct3e_lp_mode = { |
| 533 | .mode = { |
| 534 | .name = "1080x2424x30", |
| 535 | .clock = 85260, |
| 536 | .hdisplay = HDISPLAY, |
| 537 | .hsync_start = HDISPLAY + HFP, |
| 538 | .hsync_end = HDISPLAY + HFP + HSA, |
| 539 | .htotal = HDISPLAY + HFP + HSA + HBP, |
| 540 | .vdisplay = VDISPLAY, |
| 541 | .vsync_start = VDISPLAY + VFP, |
| 542 | .vsync_end = VDISPLAY + VFP + VSA, |
| 543 | .vtotal = VDISPLAY + VFP + VSA + VBP, |
| 544 | .flags = 0, |
| 545 | .width_mm = WIDTH_MM, |
| 546 | .height_mm = HEIGHT_MM, |
| 547 | }, |
| 548 | .exynos_mode = { |
| 549 | .mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS, |
| 550 | .vblank_usec = 120, |
| 551 | .te_usec = 1109, |
| 552 | .bpc = 8, |
| 553 | .dsc = CT3E_DSC, |
| 554 | .underrun_param = &underrun_param, |
| 555 | .is_lp_mode = true, |
| 556 | } |
| 557 | }; |
| 558 | |
| 559 | static const struct drm_panel_funcs ct3e_drm_funcs = { |
| 560 | .disable = exynos_panel_disable, |
| 561 | .unprepare = exynos_panel_unprepare, |
| 562 | .prepare = exynos_panel_prepare, |
| 563 | .enable = ct3e_enable, |
| 564 | .get_modes = exynos_panel_get_modes, |
| 565 | .debugfs_init = ct3e_debugfs_init, |
| 566 | }; |
| 567 | |
| 568 | static const struct exynos_panel_funcs ct3e_exynos_funcs = { |
| 569 | .set_brightness = ct3e_set_brightness, |
| 570 | .set_lp_mode = ct3e_set_lp_mode, |
| 571 | .set_nolp_mode = ct3e_set_nolp_mode, |
| 572 | .set_binned_lp = exynos_panel_set_binned_lp, |
| 573 | .set_dimming_on = ct3e_set_dimming_on, |
| 574 | .set_hbm_mode = ct3e_set_hbm_mode, |
| 575 | .is_mode_seamless = ct3e_is_mode_seamless, |
| 576 | .mode_set = ct3e_mode_set, |
| 577 | .get_panel_rev = ct3e_get_panel_rev, |
| 578 | .read_id = exynos_panel_read_ddic_id, |
| 579 | }; |
| 580 | |
| 581 | const struct exynos_panel_desc google_ct3e = { |
| 582 | .data_lane_cnt = 4, |
| 583 | .max_brightness = 4095, |
| 584 | .min_brightness = 2, |
| 585 | .dft_brightness = 1290, /* 140 nits */ |
| 586 | .brt_capability = &ct3e_brightness_capability, |
| 587 | /* supported HDR format bitmask : 1(DOLBY_VISION), 2(HDR10), 3(HLG) */ |
| 588 | .hdr_formats = BIT(2) | BIT(3), |
| 589 | .max_luminance = 10000000, |
| 590 | .max_avg_luminance = 1200000, |
| 591 | .min_luminance = 5, |
| 592 | .modes = ct3e_modes, |
| 593 | .num_modes = ARRAY_SIZE(ct3e_modes), |
| 594 | .off_cmd_set = &ct3e_off_cmd_set, |
| 595 | .lp_mode = &ct3e_lp_mode, |
| 596 | .lp_cmd_set = &ct3e_lp_cmd_set, |
| 597 | .binned_lp = ct3e_binned_lp, |
| 598 | .num_binned_lp = ARRAY_SIZE(ct3e_binned_lp), |
| 599 | .panel_func = &ct3e_drm_funcs, |
| 600 | .exynos_panel_func = &ct3e_exynos_funcs, |
derickhong | 5c1c9d4 | 2024-03-06 16:34:41 +0800 | [diff] [blame] | 601 | .reset_timing_ms = {-1, 1, 6}, |
Cathy Hsu | aaba0cf | 2024-01-16 10:59:40 +0000 | [diff] [blame] | 602 | .reg_ctrl_enable = { |
| 603 | {PANEL_REG_ID_VDDI, 0}, |
derickhong | 5c1c9d4 | 2024-03-06 16:34:41 +0800 | [diff] [blame] | 604 | {PANEL_REG_ID_VCI, 2}, |
Cathy Hsu | aaba0cf | 2024-01-16 10:59:40 +0000 | [diff] [blame] | 605 | }, |
| 606 | .reg_ctrl_post_enable = { |
| 607 | {PANEL_REG_ID_VDDD, 5}, |
| 608 | }, |
| 609 | .reg_ctrl_pre_disable = { |
| 610 | {PANEL_REG_ID_VDDD, 0}, |
| 611 | }, |
| 612 | .reg_ctrl_disable = { |
| 613 | {PANEL_REG_ID_VCI, 0}, |
| 614 | {PANEL_REG_ID_VDDI, 0}, |
| 615 | }, |
| 616 | }; |
| 617 | |
| 618 | static const struct of_device_id exynos_panel_of_match[] = { |
| 619 | { .compatible = "google,ct3e", .data = &google_ct3e }, |
| 620 | { } |
| 621 | }; |
| 622 | MODULE_DEVICE_TABLE(of, exynos_panel_of_match); |
| 623 | |
| 624 | static struct mipi_dsi_driver exynos_panel_driver = { |
| 625 | .probe = ct3e_panel_probe, |
| 626 | .remove = exynos_panel_remove, |
| 627 | .driver = { |
| 628 | .name = "panel-google-ct3e", |
| 629 | .of_match_table = exynos_panel_of_match, |
| 630 | }, |
| 631 | }; |
| 632 | module_mipi_dsi_driver(exynos_panel_driver); |
| 633 | |
| 634 | MODULE_AUTHOR("Cathy Hsu <cathsu@google.com>"); |
| 635 | MODULE_DESCRIPTION("MIPI-DSI based Google ct3e panel driver"); |
| 636 | MODULE_LICENSE("GPL"); |