1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2015 Heiko Schocher <hs@denx.de> 4 * 5 * from: 6 * drivers/gpu/drm/panel/panel-ld9040.c 7 * ld9040 AMOLED LCD drm_panel driver. 8 * 9 * Copyright (c) 2014 Samsung Electronics Co., Ltd 10 * Derived from drivers/video/backlight/ld9040.c 11 * 12 * Andrzej Hajda <a.hajda@samsung.com> 13 */ 14 15 #include <drm/drmP.h> 16 #include <drm/drm_panel.h> 17 18 #include <linux/gpio/consumer.h> 19 #include <linux/regulator/consumer.h> 20 #include <linux/spi/spi.h> 21 22 #include <video/mipi_display.h> 23 #include <video/of_videomode.h> 24 #include <video/videomode.h> 25 26 struct lg4573 { 27 struct drm_panel panel; 28 struct spi_device *spi; 29 struct videomode vm; 30 }; 31 32 static inline struct lg4573 *panel_to_lg4573(struct drm_panel *panel) 33 { 34 return container_of(panel, struct lg4573, panel); 35 } 36 37 static int lg4573_spi_write_u16(struct lg4573 *ctx, u16 data) 38 { 39 struct spi_transfer xfer = { 40 .len = 2, 41 }; 42 u16 temp = cpu_to_be16(data); 43 struct spi_message msg; 44 45 dev_dbg(ctx->panel.dev, "writing data: %x\n", data); 46 xfer.tx_buf = &temp; 47 spi_message_init(&msg); 48 spi_message_add_tail(&xfer, &msg); 49 50 return spi_sync(ctx->spi, &msg); 51 } 52 53 static int lg4573_spi_write_u16_array(struct lg4573 *ctx, const u16 *buffer, 54 unsigned int count) 55 { 56 unsigned int i; 57 int ret; 58 59 for (i = 0; i < count; i++) { 60 ret = lg4573_spi_write_u16(ctx, buffer[i]); 61 if (ret) 62 return ret; 63 } 64 65 return 0; 66 } 67 68 static int lg4573_spi_write_dcs(struct lg4573 *ctx, u8 dcs) 69 { 70 return lg4573_spi_write_u16(ctx, (0x70 << 8 | dcs)); 71 } 72 73 static int lg4573_display_on(struct lg4573 *ctx) 74 { 75 int ret; 76 77 ret = lg4573_spi_write_dcs(ctx, MIPI_DCS_EXIT_SLEEP_MODE); 78 if (ret) 79 return ret; 80 81 msleep(5); 82 83 return lg4573_spi_write_dcs(ctx, MIPI_DCS_SET_DISPLAY_ON); 84 } 85 86 static int lg4573_display_off(struct lg4573 *ctx) 87 { 88 int ret; 89 90 ret = lg4573_spi_write_dcs(ctx, MIPI_DCS_SET_DISPLAY_OFF); 91 if (ret) 92 return ret; 93 94 msleep(120); 95 96 return lg4573_spi_write_dcs(ctx, MIPI_DCS_ENTER_SLEEP_MODE); 97 } 98 99 static int lg4573_display_mode_settings(struct lg4573 *ctx) 100 { 101 static const u16 display_mode_settings[] = { 102 0x703A, 0x7270, 0x70B1, 0x7208, 103 0x723B, 0x720F, 0x70B2, 0x7200, 104 0x72C8, 0x70B3, 0x7200, 0x70B4, 105 0x7200, 0x70B5, 0x7242, 0x7210, 106 0x7210, 0x7200, 0x7220, 0x70B6, 107 0x720B, 0x720F, 0x723C, 0x7213, 108 0x7213, 0x72E8, 0x70B7, 0x7246, 109 0x7206, 0x720C, 0x7200, 0x7200, 110 }; 111 112 dev_dbg(ctx->panel.dev, "transfer display mode settings\n"); 113 return lg4573_spi_write_u16_array(ctx, display_mode_settings, 114 ARRAY_SIZE(display_mode_settings)); 115 } 116 117 static int lg4573_power_settings(struct lg4573 *ctx) 118 { 119 static const u16 power_settings[] = { 120 0x70C0, 0x7201, 0x7211, 0x70C3, 121 0x7207, 0x7203, 0x7204, 0x7204, 122 0x7204, 0x70C4, 0x7212, 0x7224, 123 0x7218, 0x7218, 0x7202, 0x7249, 124 0x70C5, 0x726F, 0x70C6, 0x7241, 125 0x7263, 126 }; 127 128 dev_dbg(ctx->panel.dev, "transfer power settings\n"); 129 return lg4573_spi_write_u16_array(ctx, power_settings, 130 ARRAY_SIZE(power_settings)); 131 } 132 133 static int lg4573_gamma_settings(struct lg4573 *ctx) 134 { 135 static const u16 gamma_settings[] = { 136 0x70D0, 0x7203, 0x7207, 0x7273, 137 0x7235, 0x7200, 0x7201, 0x7220, 138 0x7200, 0x7203, 0x70D1, 0x7203, 139 0x7207, 0x7273, 0x7235, 0x7200, 140 0x7201, 0x7220, 0x7200, 0x7203, 141 0x70D2, 0x7203, 0x7207, 0x7273, 142 0x7235, 0x7200, 0x7201, 0x7220, 143 0x7200, 0x7203, 0x70D3, 0x7203, 144 0x7207, 0x7273, 0x7235, 0x7200, 145 0x7201, 0x7220, 0x7200, 0x7203, 146 0x70D4, 0x7203, 0x7207, 0x7273, 147 0x7235, 0x7200, 0x7201, 0x7220, 148 0x7200, 0x7203, 0x70D5, 0x7203, 149 0x7207, 0x7273, 0x7235, 0x7200, 150 0x7201, 0x7220, 0x7200, 0x7203, 151 }; 152 153 dev_dbg(ctx->panel.dev, "transfer gamma settings\n"); 154 return lg4573_spi_write_u16_array(ctx, gamma_settings, 155 ARRAY_SIZE(gamma_settings)); 156 } 157 158 static int lg4573_init(struct lg4573 *ctx) 159 { 160 int ret; 161 162 dev_dbg(ctx->panel.dev, "initializing LCD\n"); 163 164 ret = lg4573_display_mode_settings(ctx); 165 if (ret) 166 return ret; 167 168 ret = lg4573_power_settings(ctx); 169 if (ret) 170 return ret; 171 172 return lg4573_gamma_settings(ctx); 173 } 174 175 static int lg4573_power_on(struct lg4573 *ctx) 176 { 177 return lg4573_display_on(ctx); 178 } 179 180 static int lg4573_disable(struct drm_panel *panel) 181 { 182 struct lg4573 *ctx = panel_to_lg4573(panel); 183 184 return lg4573_display_off(ctx); 185 } 186 187 static int lg4573_enable(struct drm_panel *panel) 188 { 189 struct lg4573 *ctx = panel_to_lg4573(panel); 190 191 lg4573_init(ctx); 192 193 return lg4573_power_on(ctx); 194 } 195 196 static const struct drm_display_mode default_mode = { 197 .clock = 27000, 198 .hdisplay = 480, 199 .hsync_start = 480 + 10, 200 .hsync_end = 480 + 10 + 59, 201 .htotal = 480 + 10 + 59 + 10, 202 .vdisplay = 800, 203 .vsync_start = 800 + 15, 204 .vsync_end = 800 + 15 + 15, 205 .vtotal = 800 + 15 + 15 + 15, 206 .vrefresh = 60, 207 }; 208 209 static int lg4573_get_modes(struct drm_panel *panel) 210 { 211 struct drm_connector *connector = panel->connector; 212 struct drm_display_mode *mode; 213 214 mode = drm_mode_duplicate(panel->drm, &default_mode); 215 if (!mode) { 216 dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n", 217 default_mode.hdisplay, default_mode.vdisplay, 218 default_mode.vrefresh); 219 return -ENOMEM; 220 } 221 222 drm_mode_set_name(mode); 223 224 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 225 drm_mode_probed_add(connector, mode); 226 227 panel->connector->display_info.width_mm = 61; 228 panel->connector->display_info.height_mm = 103; 229 230 return 1; 231 } 232 233 static const struct drm_panel_funcs lg4573_drm_funcs = { 234 .disable = lg4573_disable, 235 .enable = lg4573_enable, 236 .get_modes = lg4573_get_modes, 237 }; 238 239 static int lg4573_probe(struct spi_device *spi) 240 { 241 struct lg4573 *ctx; 242 int ret; 243 244 ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL); 245 if (!ctx) 246 return -ENOMEM; 247 248 ctx->spi = spi; 249 250 spi_set_drvdata(spi, ctx); 251 spi->bits_per_word = 8; 252 253 ret = spi_setup(spi); 254 if (ret < 0) { 255 dev_err(&spi->dev, "SPI setup failed: %d\n", ret); 256 return ret; 257 } 258 259 drm_panel_init(&ctx->panel); 260 ctx->panel.dev = &spi->dev; 261 ctx->panel.funcs = &lg4573_drm_funcs; 262 263 return drm_panel_add(&ctx->panel); 264 } 265 266 static int lg4573_remove(struct spi_device *spi) 267 { 268 struct lg4573 *ctx = spi_get_drvdata(spi); 269 270 lg4573_display_off(ctx); 271 drm_panel_remove(&ctx->panel); 272 273 return 0; 274 } 275 276 static const struct of_device_id lg4573_of_match[] = { 277 { .compatible = "lg,lg4573" }, 278 { } 279 }; 280 MODULE_DEVICE_TABLE(of, lg4573_of_match); 281 282 static struct spi_driver lg4573_driver = { 283 .probe = lg4573_probe, 284 .remove = lg4573_remove, 285 .driver = { 286 .name = "lg4573", 287 .of_match_table = lg4573_of_match, 288 }, 289 }; 290 module_spi_driver(lg4573_driver); 291 292 MODULE_AUTHOR("Heiko Schocher <hs@denx.de>"); 293 MODULE_DESCRIPTION("lg4573 LCD Driver"); 294 MODULE_LICENSE("GPL v2"); 295