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 device *dev = &dsi->dev; 61 int ret; 62 63 dsi->mode_flags |= MIPI_DSI_MODE_LPM; 64 65 ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff); 66 if (ret < 0) { 67 dev_err(dev, "Failed to set display brightness: %d\n", ret); 68 return ret; 69 } 70 71 mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24); 72 mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00); 73 74 ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 75 if (ret < 0) { 76 dev_err(dev, "Failed to exit sleep mode: %d\n", ret); 77 return ret; 78 } 79 msleep(90); 80 81 ret = mipi_dsi_dcs_set_display_on(dsi); 82 if (ret < 0) { 83 dev_err(dev, "Failed to set display on: %d\n", ret); 84 return ret; 85 } 86 87 return 0; 88 } 89 90 static int ebbg_ft8719_off(struct ebbg_ft8719 *ctx) 91 { 92 struct mipi_dsi_device *dsi = ctx->dsi; 93 struct device *dev = &dsi->dev; 94 int ret; 95 96 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 97 98 ret = mipi_dsi_dcs_set_display_off(dsi); 99 if (ret < 0) { 100 dev_err(dev, "Failed to set display off: %d\n", ret); 101 return ret; 102 } 103 usleep_range(10000, 11000); 104 105 ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 106 if (ret < 0) { 107 dev_err(dev, "Failed to enter sleep mode: %d\n", ret); 108 return ret; 109 } 110 msleep(90); 111 112 return 0; 113 } 114 115 static int ebbg_ft8719_prepare(struct drm_panel *panel) 116 { 117 struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel); 118 struct device *dev = &ctx->dsi->dev; 119 int ret; 120 121 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 122 if (ret < 0) 123 return ret; 124 125 ebbg_ft8719_reset(ctx); 126 127 ret = ebbg_ft8719_on(ctx); 128 if (ret < 0) { 129 dev_err(dev, "Failed to initialize panel: %d\n", ret); 130 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 131 return ret; 132 } 133 134 return 0; 135 } 136 137 static int ebbg_ft8719_unprepare(struct drm_panel *panel) 138 { 139 struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel); 140 struct device *dev = &ctx->dsi->dev; 141 int ret; 142 143 ret = ebbg_ft8719_off(ctx); 144 if (ret < 0) 145 dev_err(dev, "Failed to un-initialize panel: %d\n", ret); 146 147 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 148 149 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 150 if (ret) 151 dev_err(panel->dev, "Failed to disable regulators: %d\n", ret); 152 153 return 0; 154 } 155 156 static const struct drm_display_mode ebbg_ft8719_mode = { 157 .clock = (1080 + 28 + 4 + 16) * (2246 + 120 + 4 + 12) * 60 / 1000, 158 .hdisplay = 1080, 159 .hsync_start = 1080 + 28, 160 .hsync_end = 1080 + 28 + 4, 161 .htotal = 1080 + 28 + 4 + 16, 162 .vdisplay = 2246, 163 .vsync_start = 2246 + 120, 164 .vsync_end = 2246 + 120 + 4, 165 .vtotal = 2246 + 120 + 4 + 12, 166 .width_mm = 68, 167 .height_mm = 141, 168 }; 169 170 static int ebbg_ft8719_get_modes(struct drm_panel *panel, 171 struct drm_connector *connector) 172 { 173 struct drm_display_mode *mode; 174 175 mode = drm_mode_duplicate(connector->dev, &ebbg_ft8719_mode); 176 if (!mode) 177 return -ENOMEM; 178 179 drm_mode_set_name(mode); 180 181 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 182 connector->display_info.width_mm = mode->width_mm; 183 connector->display_info.height_mm = mode->height_mm; 184 drm_mode_probed_add(connector, mode); 185 186 return 1; 187 } 188 189 static const struct drm_panel_funcs ebbg_ft8719_panel_funcs = { 190 .prepare = ebbg_ft8719_prepare, 191 .unprepare = ebbg_ft8719_unprepare, 192 .get_modes = ebbg_ft8719_get_modes, 193 }; 194 195 static int ebbg_ft8719_probe(struct mipi_dsi_device *dsi) 196 { 197 struct device *dev = &dsi->dev; 198 struct ebbg_ft8719 *ctx; 199 int i, ret; 200 201 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 202 if (!ctx) 203 return -ENOMEM; 204 205 for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) 206 ctx->supplies[i].supply = regulator_names[i]; 207 208 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 209 ctx->supplies); 210 if (ret < 0) 211 return dev_err_probe(dev, ret, "Failed to get regulators\n"); 212 213 for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) { 214 ret = regulator_set_load(ctx->supplies[i].consumer, 215 regulator_enable_loads[i]); 216 if (ret) 217 return dev_err_probe(dev, ret, 218 "Failed to set regulator load\n"); 219 } 220 221 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 222 if (IS_ERR(ctx->reset_gpio)) 223 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 224 "Failed to get reset-gpios\n"); 225 226 ctx->dsi = dsi; 227 mipi_dsi_set_drvdata(dsi, ctx); 228 229 dsi->lanes = 4; 230 dsi->format = MIPI_DSI_FMT_RGB888; 231 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 232 MIPI_DSI_CLOCK_NON_CONTINUOUS; 233 234 drm_panel_init(&ctx->panel, dev, &ebbg_ft8719_panel_funcs, 235 DRM_MODE_CONNECTOR_DSI); 236 237 ret = drm_panel_of_backlight(&ctx->panel); 238 if (ret) 239 return dev_err_probe(dev, ret, "Failed to get backlight\n"); 240 241 drm_panel_add(&ctx->panel); 242 243 ret = mipi_dsi_attach(dsi); 244 if (ret < 0) { 245 dev_err(dev, "Failed to attach to DSI host: %d\n", ret); 246 drm_panel_remove(&ctx->panel); 247 return ret; 248 } 249 250 return 0; 251 } 252 253 static int ebbg_ft8719_remove(struct mipi_dsi_device *dsi) 254 { 255 struct ebbg_ft8719 *ctx = mipi_dsi_get_drvdata(dsi); 256 int ret; 257 258 ret = mipi_dsi_detach(dsi); 259 if (ret < 0) 260 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 261 262 drm_panel_remove(&ctx->panel); 263 264 return 0; 265 } 266 267 static const struct of_device_id ebbg_ft8719_of_match[] = { 268 { .compatible = "ebbg,ft8719" }, 269 { /* sentinel */ } 270 }; 271 MODULE_DEVICE_TABLE(of, ebbg_ft8719_of_match); 272 273 static struct mipi_dsi_driver ebbg_ft8719_driver = { 274 .probe = ebbg_ft8719_probe, 275 .remove = ebbg_ft8719_remove, 276 .driver = { 277 .name = "panel-ebbg-ft8719", 278 .of_match_table = ebbg_ft8719_of_match, 279 }, 280 }; 281 module_mipi_dsi_driver(ebbg_ft8719_driver); 282 283 MODULE_AUTHOR("Joel Selvaraj <jo@jsfamily.in>"); 284 MODULE_DESCRIPTION("DRM driver for EBBG FT8719 video dsi panel"); 285 MODULE_LICENSE("GPL v2"); 286