1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2022 Joel Selvaraj <jo@jsfamily.in> 4 * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree: 5 * Copyright (c) 2013, The Linux Foundation. All rights reserved. 6 */ 7 8 #include <linux/delay.h> 9 #include <linux/gpio/consumer.h> 10 #include <linux/regulator/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 static const char * const regulator_names[] = { 21 "vddio", 22 "vddpos", 23 "vddneg", 24 }; 25 26 static const unsigned long regulator_enable_loads[] = { 27 62000, 28 100000, 29 100000 30 }; 31 32 struct ebbg_ft8719 { 33 struct drm_panel panel; 34 struct mipi_dsi_device *dsi; 35 36 struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)]; 37 38 struct gpio_desc *reset_gpio; 39 }; 40 41 static inline 42 struct ebbg_ft8719 *to_ebbg_ft8719(struct drm_panel *panel) 43 { 44 return container_of(panel, struct ebbg_ft8719, panel); 45 } 46 47 static void ebbg_ft8719_reset(struct ebbg_ft8719 *ctx) 48 { 49 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 50 usleep_range(4000, 5000); 51 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 52 usleep_range(1000, 2000); 53 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 54 usleep_range(15000, 16000); 55 } 56 57 static int ebbg_ft8719_on(struct ebbg_ft8719 *ctx) 58 { 59 struct mipi_dsi_device *dsi = ctx->dsi; 60 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 61 62 dsi->mode_flags |= MIPI_DSI_MODE_LPM; 63 64 mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x00ff); 65 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24); 66 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00); 67 68 mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 69 mipi_dsi_msleep(&dsi_ctx, 90); 70 mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 71 72 return dsi_ctx.accum_err; 73 } 74 75 static int ebbg_ft8719_off(struct ebbg_ft8719 *ctx) 76 { 77 struct mipi_dsi_device *dsi = ctx->dsi; 78 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 79 80 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 81 82 mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 83 mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000); 84 mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 85 mipi_dsi_msleep(&dsi_ctx, 90); 86 87 return dsi_ctx.accum_err; 88 } 89 90 static int ebbg_ft8719_prepare(struct drm_panel *panel) 91 { 92 struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel); 93 int ret; 94 95 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 96 if (ret < 0) 97 return ret; 98 99 ebbg_ft8719_reset(ctx); 100 101 ret = ebbg_ft8719_on(ctx); 102 if (ret < 0) { 103 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 104 return ret; 105 } 106 107 return 0; 108 } 109 110 static int ebbg_ft8719_unprepare(struct drm_panel *panel) 111 { 112 struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel); 113 114 ebbg_ft8719_off(ctx); 115 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 116 regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 117 118 return 0; 119 } 120 121 static const struct drm_display_mode ebbg_ft8719_mode = { 122 .clock = (1080 + 28 + 4 + 16) * (2246 + 120 + 4 + 12) * 60 / 1000, 123 .hdisplay = 1080, 124 .hsync_start = 1080 + 28, 125 .hsync_end = 1080 + 28 + 4, 126 .htotal = 1080 + 28 + 4 + 16, 127 .vdisplay = 2246, 128 .vsync_start = 2246 + 120, 129 .vsync_end = 2246 + 120 + 4, 130 .vtotal = 2246 + 120 + 4 + 12, 131 .width_mm = 68, 132 .height_mm = 141, 133 }; 134 135 static int ebbg_ft8719_get_modes(struct drm_panel *panel, 136 struct drm_connector *connector) 137 { 138 struct drm_display_mode *mode; 139 140 mode = drm_mode_duplicate(connector->dev, &ebbg_ft8719_mode); 141 if (!mode) 142 return -ENOMEM; 143 144 drm_mode_set_name(mode); 145 146 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 147 connector->display_info.width_mm = mode->width_mm; 148 connector->display_info.height_mm = mode->height_mm; 149 drm_mode_probed_add(connector, mode); 150 151 return 1; 152 } 153 154 static const struct drm_panel_funcs ebbg_ft8719_panel_funcs = { 155 .prepare = ebbg_ft8719_prepare, 156 .unprepare = ebbg_ft8719_unprepare, 157 .get_modes = ebbg_ft8719_get_modes, 158 }; 159 160 static int ebbg_ft8719_probe(struct mipi_dsi_device *dsi) 161 { 162 struct device *dev = &dsi->dev; 163 struct ebbg_ft8719 *ctx; 164 int i, ret; 165 166 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 167 if (!ctx) 168 return -ENOMEM; 169 170 for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) 171 ctx->supplies[i].supply = regulator_names[i]; 172 173 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 174 ctx->supplies); 175 if (ret < 0) 176 return dev_err_probe(dev, ret, "Failed to get regulators\n"); 177 178 for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) { 179 ret = regulator_set_load(ctx->supplies[i].consumer, 180 regulator_enable_loads[i]); 181 if (ret) 182 return dev_err_probe(dev, ret, 183 "Failed to set regulator load\n"); 184 } 185 186 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 187 if (IS_ERR(ctx->reset_gpio)) 188 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 189 "Failed to get reset-gpios\n"); 190 191 ctx->dsi = dsi; 192 mipi_dsi_set_drvdata(dsi, ctx); 193 194 dsi->lanes = 4; 195 dsi->format = MIPI_DSI_FMT_RGB888; 196 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 197 MIPI_DSI_CLOCK_NON_CONTINUOUS; 198 199 drm_panel_init(&ctx->panel, dev, &ebbg_ft8719_panel_funcs, 200 DRM_MODE_CONNECTOR_DSI); 201 202 ret = drm_panel_of_backlight(&ctx->panel); 203 if (ret) 204 return dev_err_probe(dev, ret, "Failed to get backlight\n"); 205 206 drm_panel_add(&ctx->panel); 207 208 ret = mipi_dsi_attach(dsi); 209 if (ret < 0) { 210 dev_err(dev, "Failed to attach to DSI host: %d\n", ret); 211 drm_panel_remove(&ctx->panel); 212 return ret; 213 } 214 215 return 0; 216 } 217 218 static void ebbg_ft8719_remove(struct mipi_dsi_device *dsi) 219 { 220 struct ebbg_ft8719 *ctx = mipi_dsi_get_drvdata(dsi); 221 int ret; 222 223 ret = mipi_dsi_detach(dsi); 224 if (ret < 0) 225 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 226 227 drm_panel_remove(&ctx->panel); 228 } 229 230 static const struct of_device_id ebbg_ft8719_of_match[] = { 231 { .compatible = "ebbg,ft8719" }, 232 { /* sentinel */ } 233 }; 234 MODULE_DEVICE_TABLE(of, ebbg_ft8719_of_match); 235 236 static struct mipi_dsi_driver ebbg_ft8719_driver = { 237 .probe = ebbg_ft8719_probe, 238 .remove = ebbg_ft8719_remove, 239 .driver = { 240 .name = "panel-ebbg-ft8719", 241 .of_match_table = ebbg_ft8719_of_match, 242 }, 243 }; 244 module_mipi_dsi_driver(ebbg_ft8719_driver); 245 246 MODULE_AUTHOR("Joel Selvaraj <jo@jsfamily.in>"); 247 MODULE_DESCRIPTION("DRM driver for EBBG FT8719 video dsi panel"); 248 MODULE_LICENSE("GPL v2"); 249