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