Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/panel: jdi-lpm102a188a: Fix bug and clean up driver

Fix bug in unprepare() which causes the function's return value to be
that of the last mipi "enter sleep mode" command.

Update driver to use the "multi" variant of MIPI functions in order to
facilitate improved error handling and remove the panel's dependency on
deprecated MIPI functions.

Use the new mipi_dsi_dual macro to reduce code duplication.

Reviewed-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Diogo Ivo <diogo.ivo@tecnico.ulisboa.pt>
Tested-by: Diogo Ivo <diogo.ivo@tecnico.ulisboa.pt>
Signed-off-by: Brigham Campbell <me@brighamcampbell.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Link: https://lore.kernel.org/r/20250722015313.561966-3-me@brighamcampbell.com

authored by

Brigham Campbell and committed by
Douglas Anderson
a6adf47d d94a2a00

+59 -137
+59 -137
drivers/gpu/drm/panel/panel-jdi-lpm102a188a.c
··· 81 81 static int jdi_panel_unprepare(struct drm_panel *panel) 82 82 { 83 83 struct jdi_panel *jdi = to_panel_jdi(panel); 84 - int ret; 85 84 86 - ret = mipi_dsi_dcs_set_display_off(jdi->link1); 87 - if (ret < 0) 88 - dev_err(panel->dev, "failed to set display off: %d\n", ret); 85 + /* 86 + * One context per panel since we'll continue trying to shut down the 87 + * other panel even if one isn't responding. 88 + */ 89 + struct mipi_dsi_multi_context dsi_ctx1 = { .dsi = jdi->link1 }; 90 + struct mipi_dsi_multi_context dsi_ctx2 = { .dsi = jdi->link2 }; 89 91 90 - ret = mipi_dsi_dcs_set_display_off(jdi->link2); 91 - if (ret < 0) 92 - dev_err(panel->dev, "failed to set display off: %d\n", ret); 92 + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx1); 93 + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx2); 93 94 94 95 /* Specified by JDI @ 50ms, subject to change */ 95 96 msleep(50); 96 97 97 - ret = mipi_dsi_dcs_enter_sleep_mode(jdi->link1); 98 - if (ret < 0) 99 - dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret); 100 - ret = mipi_dsi_dcs_enter_sleep_mode(jdi->link2); 101 - if (ret < 0) 102 - dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret); 98 + /* Doesn't hurt to try sleep mode even if display off fails */ 99 + dsi_ctx1.accum_err = 0; 100 + dsi_ctx2.accum_err = 0; 101 + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx1); 102 + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx2); 103 103 104 104 /* Specified by JDI @ 150ms, subject to change */ 105 105 msleep(150); ··· 123 123 /* Specified by JDI @ 20ms, subject to change */ 124 124 msleep(20); 125 125 126 - return ret; 127 - } 128 - 129 - static int jdi_setup_symmetrical_split(struct mipi_dsi_device *left, 130 - struct mipi_dsi_device *right, 131 - const struct drm_display_mode *mode) 132 - { 133 - int err; 134 - 135 - err = mipi_dsi_dcs_set_column_address(left, 0, mode->hdisplay / 2 - 1); 136 - if (err < 0) { 137 - dev_err(&left->dev, "failed to set column address: %d\n", err); 138 - return err; 139 - } 140 - 141 - err = mipi_dsi_dcs_set_column_address(right, 0, mode->hdisplay / 2 - 1); 142 - if (err < 0) { 143 - dev_err(&right->dev, "failed to set column address: %d\n", err); 144 - return err; 145 - } 146 - 147 - err = mipi_dsi_dcs_set_page_address(left, 0, mode->vdisplay - 1); 148 - if (err < 0) { 149 - dev_err(&left->dev, "failed to set page address: %d\n", err); 150 - return err; 151 - } 152 - 153 - err = mipi_dsi_dcs_set_page_address(right, 0, mode->vdisplay - 1); 154 - if (err < 0) { 155 - dev_err(&right->dev, "failed to set page address: %d\n", err); 156 - return err; 157 - } 158 - 159 126 return 0; 160 127 } 161 128 162 - static int jdi_write_dcdc_registers(struct jdi_panel *jdi) 129 + static void jdi_setup_symmetrical_split(struct mipi_dsi_multi_context *dsi_ctx, 130 + struct mipi_dsi_device *left, 131 + struct mipi_dsi_device *right, 132 + const struct drm_display_mode *mode) 133 + { 134 + mipi_dsi_dual(mipi_dsi_dcs_set_column_address_multi, 135 + dsi_ctx, left, right, 136 + 0, mode->hdisplay / 2 - 1); 137 + mipi_dsi_dual(mipi_dsi_dcs_set_page_address_multi, 138 + dsi_ctx, left, right, 139 + 0, mode->vdisplay - 1); 140 + } 141 + 142 + static void jdi_write_dcdc_registers(struct mipi_dsi_multi_context *dsi_ctx, 143 + struct jdi_panel *jdi) 163 144 { 164 145 /* Clear the manufacturer command access protection */ 165 - mipi_dsi_generic_write_seq(jdi->link1, MCS_CMD_ACS_PROT, 166 - MCS_CMD_ACS_PROT_OFF); 167 - mipi_dsi_generic_write_seq(jdi->link2, MCS_CMD_ACS_PROT, 168 - MCS_CMD_ACS_PROT_OFF); 146 + mipi_dsi_dual_generic_write_seq_multi(dsi_ctx, jdi->link1, jdi->link2, 147 + MCS_CMD_ACS_PROT, 148 + MCS_CMD_ACS_PROT_OFF); 169 149 /* 170 - * Change the VGH/VGL divide rations to move the noise generated by the 150 + * Change the VGH/VGL divide ratios to move the noise generated by the 171 151 * TCONN. This should hopefully avoid interaction with the backlight 172 152 * controller. 173 153 */ 174 - mipi_dsi_generic_write_seq(jdi->link1, MCS_PWR_CTRL_FUNC, 175 - MCS_PWR_CTRL_PARAM1_VGH_330_DIV | 176 - MCS_PWR_CTRL_PARAM1_DEFAULT, 177 - MCS_PWR_CTRL_PARAM2_VGL_410_DIV | 178 - MCS_PWR_CTRL_PARAM2_DEFAULT); 179 - 180 - mipi_dsi_generic_write_seq(jdi->link2, MCS_PWR_CTRL_FUNC, 181 - MCS_PWR_CTRL_PARAM1_VGH_330_DIV | 182 - MCS_PWR_CTRL_PARAM1_DEFAULT, 183 - MCS_PWR_CTRL_PARAM2_VGL_410_DIV | 184 - MCS_PWR_CTRL_PARAM2_DEFAULT); 185 - 186 - return 0; 154 + mipi_dsi_dual_generic_write_seq_multi(dsi_ctx, jdi->link1, jdi->link2, 155 + MCS_PWR_CTRL_FUNC, 156 + MCS_PWR_CTRL_PARAM1_VGH_330_DIV | 157 + MCS_PWR_CTRL_PARAM1_DEFAULT, 158 + MCS_PWR_CTRL_PARAM2_VGL_410_DIV | 159 + MCS_PWR_CTRL_PARAM2_DEFAULT); 187 160 } 188 161 189 162 static int jdi_panel_prepare(struct drm_panel *panel) 190 163 { 191 164 struct jdi_panel *jdi = to_panel_jdi(panel); 165 + struct mipi_dsi_multi_context dsi_ctx = {}; 192 166 int err; 193 167 194 168 /* Disable backlight to avoid showing random pixels ··· 205 231 * put in place to communicate the configuration back to the DSI host 206 232 * controller. 207 233 */ 208 - err = jdi_setup_symmetrical_split(jdi->link1, jdi->link2, 209 - jdi->mode); 210 - if (err < 0) { 211 - dev_err(panel->dev, "failed to set up symmetrical split: %d\n", 212 - err); 213 - goto poweroff; 214 - } 234 + jdi_setup_symmetrical_split(&dsi_ctx, jdi->link1, jdi->link2, 235 + jdi->mode); 215 236 216 - err = mipi_dsi_dcs_set_tear_scanline(jdi->link1, 217 - jdi->mode->vdisplay - 16); 218 - if (err < 0) { 219 - dev_err(panel->dev, "failed to set tear scanline: %d\n", err); 220 - goto poweroff; 221 - } 237 + mipi_dsi_dual(mipi_dsi_dcs_set_tear_scanline_multi, 238 + &dsi_ctx, jdi->link1, jdi->link2, 239 + jdi->mode->vdisplay - 16); 222 240 223 - err = mipi_dsi_dcs_set_tear_scanline(jdi->link2, 224 - jdi->mode->vdisplay - 16); 225 - if (err < 0) { 226 - dev_err(panel->dev, "failed to set tear scanline: %d\n", err); 227 - goto poweroff; 228 - } 241 + mipi_dsi_dual(mipi_dsi_dcs_set_tear_on_multi, 242 + &dsi_ctx, jdi->link1, jdi->link2, 243 + MIPI_DSI_DCS_TEAR_MODE_VBLANK); 229 244 230 - err = mipi_dsi_dcs_set_tear_on(jdi->link1, 231 - MIPI_DSI_DCS_TEAR_MODE_VBLANK); 232 - if (err < 0) { 233 - dev_err(panel->dev, "failed to set tear on: %d\n", err); 234 - goto poweroff; 235 - } 245 + mipi_dsi_dual(mipi_dsi_dcs_set_pixel_format_multi, 246 + &dsi_ctx, jdi->link1, jdi->link2, 247 + MIPI_DCS_PIXEL_FMT_24BIT); 236 248 237 - err = mipi_dsi_dcs_set_tear_on(jdi->link2, 238 - MIPI_DSI_DCS_TEAR_MODE_VBLANK); 239 - if (err < 0) { 240 - dev_err(panel->dev, "failed to set tear on: %d\n", err); 241 - goto poweroff; 242 - } 249 + mipi_dsi_dual(mipi_dsi_dcs_exit_sleep_mode_multi, 250 + &dsi_ctx, jdi->link1, jdi->link2); 243 251 244 - err = mipi_dsi_dcs_set_pixel_format(jdi->link1, MIPI_DCS_PIXEL_FMT_24BIT); 245 - if (err < 0) { 246 - dev_err(panel->dev, "failed to set pixel format: %d\n", err); 247 - goto poweroff; 248 - } 249 - 250 - err = mipi_dsi_dcs_set_pixel_format(jdi->link2, MIPI_DCS_PIXEL_FMT_24BIT); 251 - if (err < 0) { 252 - dev_err(panel->dev, "failed to set pixel format: %d\n", err); 253 - goto poweroff; 254 - } 255 - 256 - err = mipi_dsi_dcs_exit_sleep_mode(jdi->link1); 257 - if (err < 0) { 258 - dev_err(panel->dev, "failed to exit sleep mode: %d\n", err); 259 - goto poweroff; 260 - } 261 - 262 - err = mipi_dsi_dcs_exit_sleep_mode(jdi->link2); 263 - if (err < 0) { 264 - dev_err(panel->dev, "failed to exit sleep mode: %d\n", err); 265 - goto poweroff; 266 - } 267 - 268 - err = jdi_write_dcdc_registers(jdi); 269 - if (err < 0) { 270 - dev_err(panel->dev, "failed to write dcdc registers: %d\n", err); 271 - goto poweroff; 272 - } 252 + jdi_write_dcdc_registers(&dsi_ctx, jdi); 273 253 /* 274 - * We need to wait 150ms between mipi_dsi_dcs_exit_sleep_mode() and 275 - * mipi_dsi_dcs_set_display_on(). 254 + * We need to wait 150ms between mipi_dsi_dcs_exit_sleep_mode_multi() 255 + * and mipi_dsi_dcs_set_display_on_multi(). 276 256 */ 277 - msleep(150); 257 + mipi_dsi_msleep(&dsi_ctx, 150); 278 258 279 - err = mipi_dsi_dcs_set_display_on(jdi->link1); 280 - if (err < 0) { 281 - dev_err(panel->dev, "failed to set display on: %d\n", err); 282 - goto poweroff; 283 - } 259 + mipi_dsi_dual(mipi_dsi_dcs_set_display_on_multi, 260 + &dsi_ctx, jdi->link1, jdi->link2); 284 261 285 - err = mipi_dsi_dcs_set_display_on(jdi->link2); 286 - if (err < 0) { 287 - dev_err(panel->dev, "failed to set display on: %d\n", err); 262 + if (dsi_ctx.accum_err < 0) 288 263 goto poweroff; 289 - } 290 264 291 265 jdi->link1->mode_flags &= ~MIPI_DSI_MODE_LPM; 292 266 jdi->link2->mode_flags &= ~MIPI_DSI_MODE_LPM;