1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2021 Raffaele Tranquillini <raffaele.tranquillini@gmail.com> 4 * 5 * Generated using linux-mdss-dsi-panel-driver-generator from Lineage OS device tree: 6 * https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/arch/arm/boot/dts/qcom/a1-msm8996-mtp.dtsi 7 */ 8 9 #include <linux/delay.h> 10 #include <linux/gpio/consumer.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 14 #include <video/mipi_display.h> 15 16 #include <drm/drm_mipi_dsi.h> 17 #include <drm/drm_modes.h> 18 #include <drm/drm_panel.h> 19 20 struct jdi_fhd_r63452 { 21 struct drm_panel panel; 22 struct mipi_dsi_device *dsi; 23 struct gpio_desc *reset_gpio; 24 }; 25 26 static inline struct jdi_fhd_r63452 *to_jdi_fhd_r63452(struct drm_panel *panel) 27 { 28 return container_of(panel, struct jdi_fhd_r63452, panel); 29 } 30 31 static void jdi_fhd_r63452_reset(struct jdi_fhd_r63452 *ctx) 32 { 33 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 34 usleep_range(10000, 11000); 35 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 36 usleep_range(1000, 2000); 37 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 38 usleep_range(10000, 11000); 39 } 40 41 static int jdi_fhd_r63452_on(struct jdi_fhd_r63452 *ctx) 42 { 43 struct mipi_dsi_device *dsi = ctx->dsi; 44 struct device *dev = &dsi->dev; 45 int ret; 46 47 dsi->mode_flags |= MIPI_DSI_MODE_LPM; 48 49 mipi_dsi_generic_write_seq(dsi, 0xb0, 0x00); 50 mipi_dsi_generic_write_seq(dsi, 0xd6, 0x01); 51 mipi_dsi_generic_write_seq(dsi, 0xec, 52 0x64, 0xdc, 0xec, 0x3b, 0x52, 0x00, 0x0b, 0x0b, 53 0x13, 0x15, 0x68, 0x0b, 0xb5); 54 mipi_dsi_generic_write_seq(dsi, 0xb0, 0x03); 55 56 ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 57 if (ret < 0) { 58 dev_err(dev, "Failed to set tear on: %d\n", ret); 59 return ret; 60 } 61 62 mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_ADDRESS_MODE, 0x00); 63 64 ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77); 65 if (ret < 0) { 66 dev_err(dev, "Failed to set pixel format: %d\n", ret); 67 return ret; 68 } 69 70 ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x0437); 71 if (ret < 0) { 72 dev_err(dev, "Failed to set column address: %d\n", ret); 73 return ret; 74 } 75 76 ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077f); 77 if (ret < 0) { 78 dev_err(dev, "Failed to set page address: %d\n", ret); 79 return ret; 80 } 81 82 ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0x0000); 83 if (ret < 0) { 84 dev_err(dev, "Failed to set tear scanline: %d\n", ret); 85 return ret; 86 } 87 88 ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff); 89 if (ret < 0) { 90 dev_err(dev, "Failed to set display brightness: %d\n", ret); 91 return ret; 92 } 93 94 mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24); 95 mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00); 96 mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS, 0x00); 97 mipi_dsi_dcs_write_seq(dsi, 0x84, 0x00); 98 99 ret = mipi_dsi_dcs_set_display_on(dsi); 100 if (ret < 0) { 101 dev_err(dev, "Failed to set display on: %d\n", ret); 102 return ret; 103 } 104 msleep(20); 105 106 ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 107 if (ret < 0) { 108 dev_err(dev, "Failed to exit sleep mode: %d\n", ret); 109 return ret; 110 } 111 msleep(80); 112 113 mipi_dsi_generic_write_seq(dsi, 0xb0, 0x04); 114 mipi_dsi_dcs_write_seq(dsi, 0x84, 0x00); 115 mipi_dsi_generic_write_seq(dsi, 0xc8, 0x11); 116 mipi_dsi_generic_write_seq(dsi, 0xb0, 0x03); 117 118 return 0; 119 } 120 121 static int jdi_fhd_r63452_off(struct jdi_fhd_r63452 *ctx) 122 { 123 struct mipi_dsi_device *dsi = ctx->dsi; 124 struct device *dev = &dsi->dev; 125 int ret; 126 127 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 128 129 mipi_dsi_generic_write_seq(dsi, 0xb0, 0x00); 130 mipi_dsi_generic_write_seq(dsi, 0xd6, 0x01); 131 mipi_dsi_generic_write_seq(dsi, 0xec, 132 0x64, 0xdc, 0xec, 0x3b, 0x52, 0x00, 0x0b, 0x0b, 133 0x13, 0x15, 0x68, 0x0b, 0x95); 134 mipi_dsi_generic_write_seq(dsi, 0xb0, 0x03); 135 136 ret = mipi_dsi_dcs_set_display_off(dsi); 137 if (ret < 0) { 138 dev_err(dev, "Failed to set display off: %d\n", ret); 139 return ret; 140 } 141 usleep_range(2000, 3000); 142 143 ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 144 if (ret < 0) { 145 dev_err(dev, "Failed to enter sleep mode: %d\n", ret); 146 return ret; 147 } 148 msleep(120); 149 150 return 0; 151 } 152 153 static int jdi_fhd_r63452_prepare(struct drm_panel *panel) 154 { 155 struct jdi_fhd_r63452 *ctx = to_jdi_fhd_r63452(panel); 156 struct device *dev = &ctx->dsi->dev; 157 int ret; 158 159 jdi_fhd_r63452_reset(ctx); 160 161 ret = jdi_fhd_r63452_on(ctx); 162 if (ret < 0) { 163 dev_err(dev, "Failed to initialize panel: %d\n", ret); 164 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 165 return ret; 166 } 167 168 return 0; 169 } 170 171 static int jdi_fhd_r63452_unprepare(struct drm_panel *panel) 172 { 173 struct jdi_fhd_r63452 *ctx = to_jdi_fhd_r63452(panel); 174 struct device *dev = &ctx->dsi->dev; 175 int ret; 176 177 ret = jdi_fhd_r63452_off(ctx); 178 if (ret < 0) 179 dev_err(dev, "Failed to un-initialize panel: %d\n", ret); 180 181 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 182 183 return 0; 184 } 185 186 static const struct drm_display_mode jdi_fhd_r63452_mode = { 187 .clock = (1080 + 120 + 16 + 40) * (1920 + 4 + 2 + 4) * 60 / 1000, 188 .hdisplay = 1080, 189 .hsync_start = 1080 + 120, 190 .hsync_end = 1080 + 120 + 16, 191 .htotal = 1080 + 120 + 16 + 40, 192 .vdisplay = 1920, 193 .vsync_start = 1920 + 4, 194 .vsync_end = 1920 + 4 + 2, 195 .vtotal = 1920 + 4 + 2 + 4, 196 .width_mm = 64, 197 .height_mm = 114, 198 }; 199 200 static int jdi_fhd_r63452_get_modes(struct drm_panel *panel, 201 struct drm_connector *connector) 202 { 203 struct drm_display_mode *mode; 204 205 mode = drm_mode_duplicate(connector->dev, &jdi_fhd_r63452_mode); 206 if (!mode) 207 return -ENOMEM; 208 209 drm_mode_set_name(mode); 210 211 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 212 connector->display_info.width_mm = mode->width_mm; 213 connector->display_info.height_mm = mode->height_mm; 214 drm_mode_probed_add(connector, mode); 215 216 return 1; 217 } 218 219 static const struct drm_panel_funcs jdi_fhd_r63452_panel_funcs = { 220 .prepare = jdi_fhd_r63452_prepare, 221 .unprepare = jdi_fhd_r63452_unprepare, 222 .get_modes = jdi_fhd_r63452_get_modes, 223 }; 224 225 static int jdi_fhd_r63452_probe(struct mipi_dsi_device *dsi) 226 { 227 struct device *dev = &dsi->dev; 228 struct jdi_fhd_r63452 *ctx; 229 int ret; 230 231 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 232 if (!ctx) 233 return -ENOMEM; 234 235 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 236 if (IS_ERR(ctx->reset_gpio)) 237 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 238 "Failed to get reset-gpios\n"); 239 240 ctx->dsi = dsi; 241 mipi_dsi_set_drvdata(dsi, ctx); 242 243 dsi->lanes = 4; 244 dsi->format = MIPI_DSI_FMT_RGB888; 245 dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | 246 MIPI_DSI_CLOCK_NON_CONTINUOUS; 247 248 drm_panel_init(&ctx->panel, dev, &jdi_fhd_r63452_panel_funcs, 249 DRM_MODE_CONNECTOR_DSI); 250 251 ret = drm_panel_of_backlight(&ctx->panel); 252 if (ret) 253 return dev_err_probe(dev, ret, "Failed to get backlight\n"); 254 255 drm_panel_add(&ctx->panel); 256 257 ret = mipi_dsi_attach(dsi); 258 if (ret < 0) { 259 dev_err(dev, "Failed to attach to DSI host: %d\n", ret); 260 return ret; 261 } 262 263 return 0; 264 } 265 266 static void jdi_fhd_r63452_remove(struct mipi_dsi_device *dsi) 267 { 268 struct jdi_fhd_r63452 *ctx = mipi_dsi_get_drvdata(dsi); 269 int ret; 270 271 ret = mipi_dsi_detach(dsi); 272 if (ret < 0) 273 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 274 275 drm_panel_remove(&ctx->panel); 276 } 277 278 static const struct of_device_id jdi_fhd_r63452_of_match[] = { 279 { .compatible = "jdi,fhd-r63452" }, 280 { /* sentinel */ } 281 }; 282 MODULE_DEVICE_TABLE(of, jdi_fhd_r63452_of_match); 283 284 static struct mipi_dsi_driver jdi_fhd_r63452_driver = { 285 .probe = jdi_fhd_r63452_probe, 286 .remove = jdi_fhd_r63452_remove, 287 .driver = { 288 .name = "panel-jdi-fhd-r63452", 289 .of_match_table = jdi_fhd_r63452_of_match, 290 }, 291 }; 292 module_mipi_dsi_driver(jdi_fhd_r63452_driver); 293 294 MODULE_AUTHOR("Raffaele Tranquillini <raffaele.tranquillini@gmail.com>"); 295 MODULE_DESCRIPTION("DRM driver for JDI FHD R63452 DSI panel, command mode"); 296 MODULE_LICENSE("GPL v2"); 297