1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree. 4 * Copyright (c) 2024 Luca Weiss <luca.weiss@fairphone.com> 5 */ 6 7 #include <linux/delay.h> 8 #include <linux/gpio/consumer.h> 9 #include <linux/module.h> 10 #include <linux/of.h> 11 #include <linux/regulator/consumer.h> 12 13 #include <drm/drm_mipi_dsi.h> 14 #include <drm/drm_modes.h> 15 #include <drm/drm_panel.h> 16 #include <drm/drm_probe_helper.h> 17 18 /* Manufacturer specific DSI commands */ 19 #define HX83112A_SETPOWER1 0xb1 20 #define HX83112A_SETDISP 0xb2 21 #define HX83112A_SETDRV 0xb4 22 #define HX83112A_SETEXTC 0xb9 23 #define HX83112A_SETBANK 0xbd 24 #define HX83112A_SETPTBA 0xbf 25 #define HX83112A_SETDGCLUT 0xc1 26 #define HX83112A_SETTCON 0xc7 27 #define HX83112A_SETCLOCK 0xcb 28 #define HX83112A_SETPANEL 0xcc 29 #define HX83112A_SETPOWER2 0xd2 30 #define HX83112A_SETGIP0 0xd3 31 #define HX83112A_SETGIP1 0xd5 32 #define HX83112A_SETGIP2 0xd6 33 #define HX83112A_SETGIP3 0xd8 34 #define HX83112A_SETTP1 0xe7 35 #define HX83112A_UNKNOWN1 0xe9 36 37 struct hx83112a_panel { 38 struct drm_panel panel; 39 struct mipi_dsi_device *dsi; 40 struct regulator_bulk_data supplies[3]; 41 struct gpio_desc *reset_gpio; 42 }; 43 44 static inline struct hx83112a_panel *to_hx83112a_panel(struct drm_panel *panel) 45 { 46 return container_of(panel, struct hx83112a_panel, panel); 47 } 48 49 static void hx83112a_reset(struct hx83112a_panel *ctx) 50 { 51 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 52 msleep(20); 53 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 54 msleep(20); 55 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 56 msleep(50); 57 } 58 59 static int hx83112a_on(struct mipi_dsi_device *dsi) 60 { 61 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 62 63 dsi->mode_flags |= MIPI_DSI_MODE_LPM; 64 65 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETEXTC, 0x83, 0x11, 0x2a); 66 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETPOWER1, 67 0x08, 0x28, 0x28, 0x83, 0x83, 0x4c, 0x4f, 0x33); 68 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDISP, 69 0x00, 0x02, 0x00, 0x90, 0x24, 0x00, 0x08, 0x19, 70 0xea, 0x11, 0x11, 0x00, 0x11, 0xa3); 71 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDRV, 72 0x58, 0x68, 0x58, 0x68, 0x0f, 0xef, 0x0b, 0xc0, 73 0x0b, 0xc0, 0x0b, 0xc0, 0x00, 0xff, 0x00, 0xff, 74 0x00, 0x00, 0x14, 0x15, 0x00, 0x29, 0x11, 0x07, 75 0x12, 0x00, 0x29); 76 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x02); 77 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDRV, 78 0x00, 0x12, 0x12, 0x11, 0x88, 0x12, 0x12, 0x00, 79 0x53); 80 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00); 81 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x03); 82 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDGCLUT, 83 0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6, 84 0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6, 85 0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d, 86 0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49, 87 0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a, 88 0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3, 89 0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad, 90 0x40); 91 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x02); 92 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDGCLUT, 93 0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6, 94 0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6, 95 0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d, 96 0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49, 97 0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a, 98 0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3, 99 0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad, 100 0x40); 101 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x01); 102 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDGCLUT, 103 0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6, 104 0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6, 105 0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d, 106 0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49, 107 0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a, 108 0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3, 109 0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad, 110 0x40); 111 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00); 112 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDGCLUT, 0x01); 113 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETTCON, 114 0x70, 0x00, 0x04, 0xe0, 0x33, 0x00); 115 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETPANEL, 0x08); 116 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETPOWER2, 0x2b, 0x2b); 117 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP0, 118 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 119 0x08, 0x03, 0x03, 0x22, 0x18, 0x07, 0x07, 0x07, 120 0x07, 0x32, 0x10, 0x06, 0x00, 0x06, 0x32, 0x10, 121 0x07, 0x00, 0x07, 0x32, 0x19, 0x31, 0x09, 0x31, 122 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x08, 123 0x09, 0x30, 0x00, 0x00, 0x00, 0x06, 0x0d, 0x00, 124 0x0f); 125 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x01); 126 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP0, 127 0x00, 0x00, 0x19, 0x10, 0x00, 0x0a, 0x00, 0x81); 128 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00); 129 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP1, 130 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 131 0xc0, 0xc0, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18, 132 0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f, 133 0x28, 0x28, 0x24, 0x24, 0x02, 0x03, 0x02, 0x03, 134 0x00, 0x01, 0x00, 0x01, 0x31, 0x31, 0x31, 0x31, 135 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f); 136 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP2, 137 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 138 0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 139 0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f, 140 0x24, 0x24, 0x28, 0x28, 0x01, 0x00, 0x01, 0x00, 141 0x03, 0x02, 0x03, 0x02, 0x31, 0x31, 0x31, 0x31, 142 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f); 143 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP3, 144 0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea, 145 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa, 146 0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa, 0xaa, 0xaa); 147 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x01); 148 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP3, 149 0xaa, 0x2e, 0x28, 0x00, 0x00, 0x00, 0xaa, 0x2e, 150 0x28, 0x00, 0x00, 0x00, 0xaa, 0xee, 0xaa, 0xaa, 151 0xaa, 0xaa, 0xaa, 0xee, 0xaa, 0xaa, 0xaa, 0xaa); 152 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x02); 153 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP3, 154 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xff, 155 0xff, 0xff, 0xff, 0xff); 156 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x03); 157 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP3, 158 0xaa, 0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 159 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xff, 160 0xff, 0xff, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff); 161 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00); 162 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETTP1, 163 0x0e, 0x0e, 0x1e, 0x65, 0x1c, 0x65, 0x00, 0x50, 164 0x20, 0x20, 0x00, 0x00, 0x02, 0x02, 0x02, 0x05, 165 0x14, 0x14, 0x32, 0xb9, 0x23, 0xb9, 0x08); 166 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x01); 167 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETTP1, 168 0x02, 0x00, 0xa8, 0x01, 0xa8, 0x0d, 0xa4, 0x0e); 169 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x02); 170 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETTP1, 171 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 173 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 174 0x00, 0x00, 0x00, 0x02, 0x00); 175 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00); 176 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_UNKNOWN1, 0xc3); 177 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETCLOCK, 0xd1, 0xd6); 178 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_UNKNOWN1, 0x3f); 179 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_UNKNOWN1, 0xc6); 180 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETPTBA, 0x37); 181 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_UNKNOWN1, 0x3f); 182 183 mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 184 mipi_dsi_msleep(&dsi_ctx, 150); 185 186 mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 187 mipi_dsi_msleep(&dsi_ctx, 50); 188 189 return dsi_ctx.accum_err; 190 } 191 192 static int hx83112a_disable(struct drm_panel *panel) 193 { 194 struct hx83112a_panel *ctx = to_hx83112a_panel(panel); 195 struct mipi_dsi_device *dsi = ctx->dsi; 196 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 197 198 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 199 200 mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 201 mipi_dsi_msleep(&dsi_ctx, 20); 202 mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 203 mipi_dsi_msleep(&dsi_ctx, 120); 204 205 return dsi_ctx.accum_err; 206 } 207 208 static int hx83112a_prepare(struct drm_panel *panel) 209 { 210 struct hx83112a_panel *ctx = to_hx83112a_panel(panel); 211 int ret; 212 213 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 214 if (ret < 0) 215 return ret; 216 217 hx83112a_reset(ctx); 218 219 ret = hx83112a_on(ctx->dsi); 220 if (ret < 0) { 221 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 222 regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 223 } 224 225 return ret; 226 } 227 228 static int hx83112a_unprepare(struct drm_panel *panel) 229 { 230 struct hx83112a_panel *ctx = to_hx83112a_panel(panel); 231 232 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 233 regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 234 235 return 0; 236 } 237 238 static const struct drm_display_mode hx83112a_mode = { 239 .clock = (1080 + 28 + 8 + 8) * (2340 + 27 + 5 + 5) * 60 / 1000, 240 .hdisplay = 1080, 241 .hsync_start = 1080 + 28, 242 .hsync_end = 1080 + 28 + 8, 243 .htotal = 1080 + 28 + 8 + 8, 244 .vdisplay = 2340, 245 .vsync_start = 2340 + 27, 246 .vsync_end = 2340 + 27 + 5, 247 .vtotal = 2340 + 27 + 5 + 5, 248 .width_mm = 67, 249 .height_mm = 145, 250 .type = DRM_MODE_TYPE_DRIVER, 251 }; 252 253 static int hx83112a_get_modes(struct drm_panel *panel, 254 struct drm_connector *connector) 255 { 256 return drm_connector_helper_get_modes_fixed(connector, &hx83112a_mode); 257 } 258 259 static const struct drm_panel_funcs hx83112a_panel_funcs = { 260 .prepare = hx83112a_prepare, 261 .unprepare = hx83112a_unprepare, 262 .disable = hx83112a_disable, 263 .get_modes = hx83112a_get_modes, 264 }; 265 266 static int hx83112a_probe(struct mipi_dsi_device *dsi) 267 { 268 struct device *dev = &dsi->dev; 269 struct hx83112a_panel *ctx; 270 int ret; 271 272 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 273 if (!ctx) 274 return -ENOMEM; 275 276 ctx->supplies[0].supply = "vdd1"; 277 ctx->supplies[1].supply = "vsn"; 278 ctx->supplies[2].supply = "vsp"; 279 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 280 ctx->supplies); 281 if (ret < 0) 282 return dev_err_probe(dev, ret, "Failed to get regulators\n"); 283 284 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 285 if (IS_ERR(ctx->reset_gpio)) 286 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 287 "Failed to get reset-gpios\n"); 288 289 ctx->dsi = dsi; 290 mipi_dsi_set_drvdata(dsi, ctx); 291 292 dsi->lanes = 4; 293 dsi->format = MIPI_DSI_FMT_RGB888; 294 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 295 MIPI_DSI_MODE_VIDEO_HSE | 296 MIPI_DSI_CLOCK_NON_CONTINUOUS; 297 298 drm_panel_init(&ctx->panel, dev, &hx83112a_panel_funcs, 299 DRM_MODE_CONNECTOR_DSI); 300 ctx->panel.prepare_prev_first = true; 301 302 ret = drm_panel_of_backlight(&ctx->panel); 303 if (ret) 304 return dev_err_probe(dev, ret, "Failed to get backlight\n"); 305 306 drm_panel_add(&ctx->panel); 307 308 ret = mipi_dsi_attach(dsi); 309 if (ret < 0) { 310 dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); 311 drm_panel_remove(&ctx->panel); 312 return ret; 313 } 314 315 return 0; 316 } 317 318 static void hx83112a_remove(struct mipi_dsi_device *dsi) 319 { 320 struct hx83112a_panel *ctx = mipi_dsi_get_drvdata(dsi); 321 int ret; 322 323 ret = mipi_dsi_detach(dsi); 324 if (ret < 0) 325 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 326 327 drm_panel_remove(&ctx->panel); 328 } 329 330 static const struct of_device_id hx83112a_of_match[] = { 331 { .compatible = "djn,9a-3r063-1102b" }, 332 { /* sentinel */ } 333 }; 334 MODULE_DEVICE_TABLE(of, hx83112a_of_match); 335 336 static struct mipi_dsi_driver hx83112a_driver = { 337 .probe = hx83112a_probe, 338 .remove = hx83112a_remove, 339 .driver = { 340 .name = "panel-himax-hx83112a", 341 .of_match_table = hx83112a_of_match, 342 }, 343 }; 344 module_mipi_dsi_driver(hx83112a_driver); 345 346 MODULE_DESCRIPTION("DRM driver for hx83112a-equipped DSI panels"); 347 MODULE_LICENSE("GPL"); 348