1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (c) 2020 Casey Connolly <casey.connolly@linaro.org> 3 * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree: 4 * Copyright (c) 2020, The Linux Foundation. All rights reserved. 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 #include <linux/backlight.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 sofef00_panel { 21 struct drm_panel panel; 22 struct mipi_dsi_device *dsi; 23 struct regulator *supply; 24 struct gpio_desc *reset_gpio; 25 }; 26 27 static inline 28 struct sofef00_panel *to_sofef00_panel(struct drm_panel *panel) 29 { 30 return container_of(panel, struct sofef00_panel, panel); 31 } 32 33 static void sofef00_panel_reset(struct sofef00_panel *ctx) 34 { 35 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 36 usleep_range(5000, 6000); 37 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 38 usleep_range(2000, 3000); 39 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 40 usleep_range(12000, 13000); 41 } 42 43 static int sofef00_panel_on(struct sofef00_panel *ctx) 44 { 45 struct mipi_dsi_device *dsi = ctx->dsi; 46 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 47 48 dsi->mode_flags |= MIPI_DSI_MODE_LPM; 49 50 mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 51 mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000); 52 53 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a); 54 55 mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 56 57 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5); 58 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a); 59 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x07); 60 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb6, 0x12); 61 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5); 62 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20); 63 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00); 64 65 mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 66 67 return dsi_ctx.accum_err; 68 } 69 70 static int sofef00_panel_off(struct sofef00_panel *ctx) 71 { 72 struct mipi_dsi_device *dsi = ctx->dsi; 73 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 74 75 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 76 77 mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 78 mipi_dsi_msleep(&dsi_ctx, 40); 79 80 mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 81 mipi_dsi_msleep(&dsi_ctx, 160); 82 83 return dsi_ctx.accum_err; 84 } 85 86 static int sofef00_panel_prepare(struct drm_panel *panel) 87 { 88 struct sofef00_panel *ctx = to_sofef00_panel(panel); 89 struct device *dev = &ctx->dsi->dev; 90 int ret; 91 92 ret = regulator_enable(ctx->supply); 93 if (ret < 0) { 94 dev_err(dev, "Failed to enable regulator: %d\n", ret); 95 return ret; 96 } 97 98 sofef00_panel_reset(ctx); 99 100 ret = sofef00_panel_on(ctx); 101 if (ret < 0) { 102 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 103 return ret; 104 } 105 106 return 0; 107 } 108 109 static int sofef00_panel_unprepare(struct drm_panel *panel) 110 { 111 struct sofef00_panel *ctx = to_sofef00_panel(panel); 112 113 sofef00_panel_off(ctx); 114 regulator_disable(ctx->supply); 115 116 return 0; 117 } 118 119 static const struct drm_display_mode enchilada_panel_mode = { 120 .clock = (1080 + 112 + 16 + 36) * (2280 + 36 + 8 + 12) * 60 / 1000, 121 .hdisplay = 1080, 122 .hsync_start = 1080 + 112, 123 .hsync_end = 1080 + 112 + 16, 124 .htotal = 1080 + 112 + 16 + 36, 125 .vdisplay = 2280, 126 .vsync_start = 2280 + 36, 127 .vsync_end = 2280 + 36 + 8, 128 .vtotal = 2280 + 36 + 8 + 12, 129 .width_mm = 68, 130 .height_mm = 145, 131 }; 132 133 static int sofef00_panel_get_modes(struct drm_panel *panel, struct drm_connector *connector) 134 { 135 struct drm_display_mode *mode; 136 137 mode = drm_mode_duplicate(connector->dev, &enchilada_panel_mode); 138 if (!mode) 139 return -ENOMEM; 140 141 drm_mode_set_name(mode); 142 143 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 144 connector->display_info.width_mm = mode->width_mm; 145 connector->display_info.height_mm = mode->height_mm; 146 drm_mode_probed_add(connector, mode); 147 148 return 1; 149 } 150 151 static const struct drm_panel_funcs sofef00_panel_panel_funcs = { 152 .prepare = sofef00_panel_prepare, 153 .unprepare = sofef00_panel_unprepare, 154 .get_modes = sofef00_panel_get_modes, 155 }; 156 157 static int sofef00_panel_bl_update_status(struct backlight_device *bl) 158 { 159 struct mipi_dsi_device *dsi = bl_get_data(bl); 160 int err; 161 u16 brightness = (u16)backlight_get_brightness(bl); 162 163 err = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); 164 if (err < 0) 165 return err; 166 167 return 0; 168 } 169 170 static const struct backlight_ops sofef00_panel_bl_ops = { 171 .update_status = sofef00_panel_bl_update_status, 172 }; 173 174 static struct backlight_device * 175 sofef00_create_backlight(struct mipi_dsi_device *dsi) 176 { 177 struct device *dev = &dsi->dev; 178 const struct backlight_properties props = { 179 .type = BACKLIGHT_PLATFORM, 180 .brightness = 1023, 181 .max_brightness = 1023, 182 }; 183 184 return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, 185 &sofef00_panel_bl_ops, &props); 186 } 187 188 static int sofef00_panel_probe(struct mipi_dsi_device *dsi) 189 { 190 struct device *dev = &dsi->dev; 191 struct sofef00_panel *ctx; 192 int ret; 193 194 ctx = devm_drm_panel_alloc(dev, struct sofef00_panel, panel, 195 &sofef00_panel_panel_funcs, 196 DRM_MODE_CONNECTOR_DSI); 197 if (IS_ERR(ctx)) 198 return PTR_ERR(ctx); 199 200 ctx->supply = devm_regulator_get(dev, "vddio"); 201 if (IS_ERR(ctx->supply)) 202 return dev_err_probe(dev, PTR_ERR(ctx->supply), 203 "Failed to get vddio regulator\n"); 204 205 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 206 if (IS_ERR(ctx->reset_gpio)) 207 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 208 "Failed to get reset-gpios\n"); 209 210 ctx->dsi = dsi; 211 mipi_dsi_set_drvdata(dsi, ctx); 212 213 dsi->lanes = 4; 214 dsi->format = MIPI_DSI_FMT_RGB888; 215 216 ctx->panel.backlight = sofef00_create_backlight(dsi); 217 if (IS_ERR(ctx->panel.backlight)) 218 return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), 219 "Failed to create backlight\n"); 220 221 drm_panel_add(&ctx->panel); 222 223 ret = mipi_dsi_attach(dsi); 224 if (ret < 0) { 225 dev_err(dev, "Failed to attach to DSI host: %d\n", ret); 226 drm_panel_remove(&ctx->panel); 227 return ret; 228 } 229 230 return 0; 231 } 232 233 static void sofef00_panel_remove(struct mipi_dsi_device *dsi) 234 { 235 struct sofef00_panel *ctx = mipi_dsi_get_drvdata(dsi); 236 int ret; 237 238 ret = mipi_dsi_detach(dsi); 239 if (ret < 0) 240 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 241 242 drm_panel_remove(&ctx->panel); 243 } 244 245 static const struct of_device_id sofef00_panel_of_match[] = { 246 { .compatible = "samsung,sofef00" }, 247 { /* sentinel */ } 248 }; 249 MODULE_DEVICE_TABLE(of, sofef00_panel_of_match); 250 251 static struct mipi_dsi_driver sofef00_panel_driver = { 252 .probe = sofef00_panel_probe, 253 .remove = sofef00_panel_remove, 254 .driver = { 255 .name = "panel-oneplus6", 256 .of_match_table = sofef00_panel_of_match, 257 }, 258 }; 259 260 module_mipi_dsi_driver(sofef00_panel_driver); 261 262 MODULE_AUTHOR("Casey Connolly <casey.connolly@linaro.org>"); 263 MODULE_DESCRIPTION("DRM driver for Samsung AMOLED DSI panels found in OnePlus 6/6T phones"); 264 MODULE_LICENSE("GPL v2"); 265