1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. 4 */ 5 6 #include <linux/backlight.h> 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 <video/mipi_display.h> 14 15 #include <drm/drm_mipi_dsi.h> 16 #include <drm/drm_modes.h> 17 #include <drm/drm_panel.h> 18 #include <drm/drm_probe_helper.h> 19 20 struct panel_desc { 21 const struct drm_display_mode *mode; 22 unsigned int lanes; 23 enum mipi_dsi_pixel_format format; 24 unsigned long mode_flags; 25 void (*init)(struct mipi_dsi_multi_context *dsi_ctx); 26 }; 27 28 struct ili7807s { 29 struct drm_panel panel; 30 struct mipi_dsi_device *dsi; 31 const struct panel_desc *desc; 32 33 struct regulator_bulk_data *supplies; 34 struct gpio_desc *reset_gpio; 35 }; 36 37 static const struct regulator_bulk_data ili7807s_supplies[] = { 38 { .supply = "vddi" }, 39 { .supply = "avdd" }, 40 { .supply = "avee" }, 41 }; 42 43 static inline struct ili7807s *to_ili7807s(struct drm_panel *panel) 44 { 45 return container_of(panel, struct ili7807s, panel); 46 } 47 48 static void ili7807s_reset(struct ili7807s *ctx) 49 { 50 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 51 usleep_range(10000, 11000); 52 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 53 usleep_range(10000, 11000); 54 } 55 56 static void dlc0697_init_sequence(struct mipi_dsi_multi_context *dsi_ctx) 57 { 58 mipi_dsi_dcs_soft_reset_multi(dsi_ctx); 59 mipi_dsi_msleep(dsi_ctx, 120); 60 61 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x78, 0x07, 0x00); 62 mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 63 mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS, 0x09, 0x99); 64 mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24); 65 mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x01); 66 mipi_dsi_dcs_set_display_brightness_multi(dsi_ctx, 0x1fff); 67 68 mipi_dsi_dcs_exit_sleep_mode_multi(dsi_ctx); 69 mipi_dsi_msleep(dsi_ctx, 120); 70 71 mipi_dsi_dcs_set_display_on_multi(dsi_ctx); 72 mipi_dsi_msleep(dsi_ctx, 20); 73 } 74 75 static int ili7807s_on(struct ili7807s *ctx) 76 { 77 struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 78 79 ctx->desc->init(&dsi_ctx); 80 81 return dsi_ctx.accum_err; 82 } 83 84 static int ili7807s_off(struct ili7807s *ctx) 85 { 86 struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 87 88 mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 89 mipi_dsi_msleep(&dsi_ctx, 20); 90 91 mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 92 mipi_dsi_msleep(&dsi_ctx, 120); 93 94 return dsi_ctx.accum_err; 95 } 96 97 static int ili7807s_prepare(struct drm_panel *panel) 98 { 99 struct ili7807s *ctx = to_ili7807s(panel); 100 int ret; 101 102 ret = regulator_bulk_enable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies); 103 if (ret < 0) { 104 dev_err(ctx->panel.dev, "failed to enable regulators: %d\n", ret); 105 return ret; 106 } 107 108 msleep(20); 109 110 ili7807s_reset(ctx); 111 112 ret = ili7807s_on(ctx); 113 if (ret < 0) { 114 dev_err(ctx->panel.dev, "failed to initialise panel: %d\n", ret); 115 goto err; 116 } 117 118 return 0; 119 120 err: 121 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 122 123 regulator_bulk_disable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies); 124 return ret; 125 } 126 127 static int ili7807s_unprepare(struct drm_panel *panel) 128 { 129 struct ili7807s *ctx = to_ili7807s(panel); 130 int ret; 131 132 ret = ili7807s_off(ctx); 133 if (ret < 0) 134 dev_err(ctx->panel.dev, "failed to disable panel: %d\n", ret); 135 136 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 137 138 regulator_bulk_disable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies); 139 140 return 0; 141 } 142 143 static int ili7807s_get_modes(struct drm_panel *panel, 144 struct drm_connector *connector) 145 { 146 struct ili7807s *ctx = to_ili7807s(panel); 147 148 return drm_connector_helper_get_modes_fixed(connector, ctx->desc->mode); 149 } 150 151 static const struct drm_panel_funcs ili7807s_panel_funcs = { 152 .prepare = ili7807s_prepare, 153 .unprepare = ili7807s_unprepare, 154 .get_modes = ili7807s_get_modes, 155 }; 156 157 static int ili7807s_bl_update_status(struct backlight_device *bl) 158 { 159 struct mipi_dsi_device *dsi = bl_get_data(bl); 160 u16 brightness = backlight_get_brightness(bl); 161 int ret; 162 163 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 164 165 ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); 166 167 dsi->mode_flags |= MIPI_DSI_MODE_LPM; 168 169 return ret; 170 } 171 172 static const struct backlight_ops ili7807s_bl_ops = { 173 .update_status = ili7807s_bl_update_status, 174 }; 175 176 static struct backlight_device *ili7807s_create_backlight(struct mipi_dsi_device *dsi) 177 { 178 struct device *dev = &dsi->dev; 179 const struct backlight_properties props = { 180 .type = BACKLIGHT_RAW, 181 .brightness = 0x3fff, 182 .max_brightness = 0x3fff, 183 }; 184 185 return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, 186 &ili7807s_bl_ops, &props); 187 } 188 189 static const struct drm_display_mode dlc0697_mode = { 190 .clock = (1080 + 18 + 2 + 16) * (1920 + 26 + 4 + 20) * 60 / 1000, 191 192 .hdisplay = 1080, 193 .hsync_start = 1080 + 18, 194 .hsync_end = 1080 + 18 + 2, 195 .htotal = 1080 + 18 + 2 + 16, 196 197 .vdisplay = 1920, 198 .vsync_start = 1920 + 26, 199 .vsync_end = 1920 + 26 + 4, 200 .vtotal = 1920 + 26 + 4 + 20, 201 202 .width_mm = 87, 203 .height_mm = 154, 204 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 205 }; 206 207 static const struct panel_desc dlc0697_desc = { 208 .mode = &dlc0697_mode, 209 .lanes = 4, 210 .format = MIPI_DSI_FMT_RGB888, 211 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST, 212 .init = dlc0697_init_sequence, 213 }; 214 215 static int ili7807s_probe(struct mipi_dsi_device *dsi) 216 { 217 struct device *dev = &dsi->dev; 218 const struct panel_desc *desc; 219 struct ili7807s *ctx; 220 int ret; 221 222 ctx = devm_drm_panel_alloc(dev, struct ili7807s, panel, 223 &ili7807s_panel_funcs, 224 DRM_MODE_CONNECTOR_DSI); 225 if (IS_ERR(ctx)) 226 return PTR_ERR(ctx); 227 228 desc = of_device_get_match_data(dev); 229 ctx->desc = desc; 230 231 ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(ili7807s_supplies), 232 ili7807s_supplies, &ctx->supplies); 233 if (ret < 0) 234 return dev_err_probe(dev, ret, "failed to get regulators\n"); 235 236 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 237 if (IS_ERR(ctx->reset_gpio)) 238 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 239 "failed to get reset gpio\n"); 240 241 ctx->dsi = dsi; 242 mipi_dsi_set_drvdata(dsi, ctx); 243 244 dsi->lanes = desc->lanes; 245 dsi->format = desc->format; 246 dsi->mode_flags = desc->mode_flags | MIPI_DSI_MODE_LPM; 247 248 ctx->panel.prepare_prev_first = true; 249 250 ret = drm_panel_of_backlight(&ctx->panel); 251 if (ret) 252 return ret; 253 254 if (!ctx->panel.backlight) { 255 ctx->panel.backlight = ili7807s_create_backlight(dsi); 256 if (IS_ERR(ctx->panel.backlight)) 257 return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), 258 "failed to create backlight\n"); 259 } 260 261 ret = devm_drm_panel_add(dev, &ctx->panel); 262 if (ret < 0) 263 return dev_err_probe(dev, ret, "failed to add panel\n"); 264 265 return devm_mipi_dsi_attach(dev, dsi); 266 } 267 268 static const struct of_device_id ili7807s_of_match[] = { 269 { .compatible = "dlc,dlc0697", .data = &dlc0697_desc }, 270 { } 271 }; 272 MODULE_DEVICE_TABLE(of, ili7807s_of_match); 273 274 static struct mipi_dsi_driver ili7807s_dsi_driver = { 275 .probe = ili7807s_probe, 276 .driver = { 277 .name = "panel-ilitek-ili7807s", 278 .of_match_table = ili7807s_of_match, 279 }, 280 }; 281 module_mipi_dsi_driver(ili7807s_dsi_driver); 282 283 MODULE_AUTHOR("Arpit Saini <arpit.saini@oss.qualcomm.com>"); 284 MODULE_DESCRIPTION("Panel driver for Ilitek ILI7807S LCD DSI panel"); 285 MODULE_LICENSE("GPL"); 286