1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2024, Danila Tikhonov <danila@jiaxyga.com> 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 /* Manufacturer Command Set */ 21 #define MCS_ACCESS_PROT_OFF 0xb0 22 #define MCS_UNKNOWN_B7 0xb7 23 #define MCS_BIAS_CURRENT_CTRL 0xd1 24 #define MCS_PASSWD1 0xf0 25 #define MCS_PASSWD2 0xfc 26 #define MCS_UNKNOWN_FF 0xff 27 28 struct ams639rq08 { 29 struct drm_panel panel; 30 struct mipi_dsi_device *dsi; 31 struct gpio_desc *reset_gpio; 32 struct regulator_bulk_data *supplies; 33 }; 34 35 static const struct regulator_bulk_data ams639rq08_supplies[] = { 36 { .supply = "vdd3p3" }, 37 { .supply = "vddio" }, 38 { .supply = "vsn" }, 39 { .supply = "vsp" }, 40 }; 41 42 static inline struct ams639rq08 *to_ams639rq08(struct drm_panel *panel) 43 { 44 return container_of(panel, struct ams639rq08, panel); 45 } 46 47 static void ams639rq08_reset(struct ams639rq08 *ctx) 48 { 49 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 50 usleep_range(1000, 2000); 51 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 52 usleep_range(10000, 11000); 53 } 54 55 static int ams639rq08_on(struct ams639rq08 *ctx) 56 { 57 struct mipi_dsi_device *dsi = ctx->dsi; 58 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 59 60 /* Delay 2ms for VCI1 power */ 61 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD1, 0x5a, 0x5a); 62 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD2, 0x5a, 0x5a); 63 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_ACCESS_PROT_OFF, 0x0c); 64 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_UNKNOWN_FF, 0x10); 65 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_ACCESS_PROT_OFF, 0x2f); 66 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_BIAS_CURRENT_CTRL, 0x01); 67 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD1, 0xa5, 0xa5); 68 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD2, 0xa5, 0xa5); 69 70 /* Sleep Out */ 71 mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 72 usleep_range(10000, 11000); 73 74 /* TE OUT (Vsync On) */ 75 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD1, 0x5a, 0x5a); 76 77 mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 78 79 /* DBV Smooth Transition */ 80 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_UNKNOWN_B7, 0x01, 0x4b); 81 82 /* Edge Dimming Speed Setting */ 83 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_ACCESS_PROT_OFF, 0x06); 84 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_UNKNOWN_B7, 0x10); 85 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD1, 0xa5, 0xa5); 86 87 /* Page Address Set */ 88 mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0x0000, 0x0923); 89 90 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD1, 0x5a, 0x5a); 91 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD2, 0x5a, 0x5a); 92 93 /* Set DDIC internal HFP */ 94 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_ACCESS_PROT_OFF, 0x23); 95 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_BIAS_CURRENT_CTRL, 0x11); 96 97 /* OFC Setting 84.1 Mhz */ 98 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe9, 0x11, 0x55, 99 0xa6, 0x75, 0xa3, 100 0xb9, 0xa1, 0x4a, 101 0x00, 0x1a, 0xb8); 102 103 /* Err_FG Setting */ 104 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe1, 105 0x00, 0x00, 0x02, 106 0x02, 0x42, 0x02); 107 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe2, 108 0x00, 0x00, 0x00, 109 0x00, 0x00, 0x00); 110 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_ACCESS_PROT_OFF, 0x0c); 111 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe1, 0x19); 112 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD1, 0xa5, 0xa5); 113 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PASSWD2, 0xa5, 0xa5); 114 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20); 115 116 /* Brightness Control */ 117 mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x0000); 118 119 /* Display On */ 120 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00); 121 mipi_dsi_msleep(&dsi_ctx, 67); 122 123 mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 124 125 return dsi_ctx.accum_err; 126 } 127 128 static void ams639rq08_off(struct ams639rq08 *ctx) 129 { 130 struct mipi_dsi_device *dsi = ctx->dsi; 131 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 132 133 mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 134 mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 135 mipi_dsi_msleep(&dsi_ctx, 120); 136 } 137 138 static int ams639rq08_prepare(struct drm_panel *panel) 139 { 140 struct ams639rq08 *ctx = to_ams639rq08(panel); 141 int ret; 142 143 ret = regulator_bulk_enable(ARRAY_SIZE(ams639rq08_supplies), 144 ctx->supplies); 145 if (ret < 0) 146 return ret; 147 148 ams639rq08_reset(ctx); 149 150 ret = ams639rq08_on(ctx); 151 if (ret < 0) { 152 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 153 regulator_bulk_disable(ARRAY_SIZE(ams639rq08_supplies), 154 ctx->supplies); 155 return ret; 156 } 157 158 return 0; 159 } 160 161 static int ams639rq08_unprepare(struct drm_panel *panel) 162 { 163 struct ams639rq08 *ctx = to_ams639rq08(panel); 164 165 ams639rq08_off(ctx); 166 167 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 168 regulator_bulk_disable(ARRAY_SIZE(ams639rq08_supplies), 169 ctx->supplies); 170 171 return 0; 172 } 173 174 static const struct drm_display_mode ams639rq08_mode = { 175 .clock = (1080 + 64 + 20 + 64) * (2340 + 64 + 20 + 64) * 60 / 1000, 176 .hdisplay = 1080, 177 .hsync_start = 1080 + 64, 178 .hsync_end = 1080 + 64 + 20, 179 .htotal = 1080 + 64 + 20 + 64, 180 .vdisplay = 2340, 181 .vsync_start = 2340 + 64, 182 .vsync_end = 2340 + 64 + 20, 183 .vtotal = 2340 + 64 + 20 + 64, 184 .width_mm = 68, 185 .height_mm = 147, 186 .type = DRM_MODE_TYPE_DRIVER, 187 }; 188 189 static int ams639rq08_get_modes(struct drm_panel *panel, 190 struct drm_connector *connector) 191 { 192 return drm_connector_helper_get_modes_fixed(connector, &ams639rq08_mode); 193 } 194 195 static const struct drm_panel_funcs ams639rq08_panel_funcs = { 196 .prepare = ams639rq08_prepare, 197 .unprepare = ams639rq08_unprepare, 198 .get_modes = ams639rq08_get_modes, 199 }; 200 201 static int ams639rq08_bl_update_status(struct backlight_device *bl) 202 { 203 struct mipi_dsi_device *dsi = bl_get_data(bl); 204 u16 brightness = backlight_get_brightness(bl); 205 int ret; 206 207 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 208 209 ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); 210 if (ret < 0) 211 return ret; 212 213 dsi->mode_flags |= MIPI_DSI_MODE_LPM; 214 215 return 0; 216 } 217 218 static int ams639rq08_bl_get_brightness(struct backlight_device *bl) 219 { 220 struct mipi_dsi_device *dsi = bl_get_data(bl); 221 u16 brightness; 222 int ret; 223 224 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 225 226 ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness); 227 if (ret < 0) 228 return ret; 229 230 dsi->mode_flags |= MIPI_DSI_MODE_LPM; 231 232 return brightness; 233 } 234 235 static const struct backlight_ops ams639rq08_bl_ops = { 236 .update_status = ams639rq08_bl_update_status, 237 .get_brightness = ams639rq08_bl_get_brightness, 238 }; 239 240 static struct backlight_device * 241 ams639rq08_create_backlight(struct mipi_dsi_device *dsi) 242 { 243 struct device *dev = &dsi->dev; 244 const struct backlight_properties props = { 245 .type = BACKLIGHT_RAW, 246 .brightness = 1023, 247 .max_brightness = 2047, 248 }; 249 250 return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, 251 &ams639rq08_bl_ops, &props); 252 } 253 254 static int ams639rq08_probe(struct mipi_dsi_device *dsi) 255 { 256 struct device *dev = &dsi->dev; 257 struct ams639rq08 *ctx; 258 int ret; 259 260 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 261 if (!ctx) 262 return -ENOMEM; 263 264 ret = devm_regulator_bulk_get_const(&dsi->dev, 265 ARRAY_SIZE(ams639rq08_supplies), 266 ams639rq08_supplies, 267 &ctx->supplies); 268 if (ret < 0) 269 return dev_err_probe(dev, ret, "Failed to get regulators\n"); 270 271 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 272 if (IS_ERR(ctx->reset_gpio)) 273 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 274 "Failed to get reset-gpios\n"); 275 276 ctx->dsi = dsi; 277 mipi_dsi_set_drvdata(dsi, ctx); 278 279 dsi->lanes = 4; 280 dsi->format = MIPI_DSI_FMT_RGB888; 281 dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | 282 MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; 283 284 drm_panel_init(&ctx->panel, dev, &ams639rq08_panel_funcs, 285 DRM_MODE_CONNECTOR_DSI); 286 ctx->panel.prepare_prev_first = true; 287 288 ctx->panel.backlight = ams639rq08_create_backlight(dsi); 289 if (IS_ERR(ctx->panel.backlight)) 290 return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), 291 "Failed to create backlight\n"); 292 293 drm_panel_add(&ctx->panel); 294 295 ret = devm_mipi_dsi_attach(dev, dsi); 296 if (ret < 0) { 297 drm_panel_remove(&ctx->panel); 298 return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); 299 } 300 301 return 0; 302 } 303 304 static void ams639rq08_remove(struct mipi_dsi_device *dsi) 305 { 306 struct ams639rq08 *ctx = mipi_dsi_get_drvdata(dsi); 307 308 drm_panel_remove(&ctx->panel); 309 } 310 311 static const struct of_device_id ams639rq08_of_match[] = { 312 { .compatible = "samsung,ams639rq08" }, 313 { /* sentinel */ } 314 }; 315 MODULE_DEVICE_TABLE(of, ams639rq08_of_match); 316 317 static struct mipi_dsi_driver ams639rq08_driver = { 318 .probe = ams639rq08_probe, 319 .remove = ams639rq08_remove, 320 .driver = { 321 .name = "panel-samsung-ams639rq08", 322 .of_match_table = ams639rq08_of_match, 323 }, 324 }; 325 module_mipi_dsi_driver(ams639rq08_driver); 326 327 MODULE_AUTHOR("Danila Tikhonov <danila@jiaxyga.com>"); 328 MODULE_DESCRIPTION("DRM driver for SAMSUNG AMS639RQ08 cmd mode dsi panel"); 329 MODULE_LICENSE("GPL"); 330