Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2015 Heiko Schocher <hs@denx.de>
0004  *
0005  * from:
0006  * drivers/gpu/drm/panel/panel-ld9040.c
0007  * ld9040 AMOLED LCD drm_panel driver.
0008  *
0009  * Copyright (c) 2014 Samsung Electronics Co., Ltd
0010  * Derived from drivers/video/backlight/ld9040.c
0011  *
0012  * Andrzej Hajda <a.hajda@samsung.com>
0013 */
0014 
0015 #include <linux/delay.h>
0016 #include <linux/gpio/consumer.h>
0017 #include <linux/module.h>
0018 #include <linux/regulator/consumer.h>
0019 #include <linux/spi/spi.h>
0020 
0021 #include <video/mipi_display.h>
0022 #include <video/of_videomode.h>
0023 #include <video/videomode.h>
0024 
0025 #include <drm/drm_device.h>
0026 #include <drm/drm_modes.h>
0027 #include <drm/drm_panel.h>
0028 
0029 struct lg4573 {
0030     struct drm_panel panel;
0031     struct spi_device *spi;
0032     struct videomode vm;
0033 };
0034 
0035 static inline struct lg4573 *panel_to_lg4573(struct drm_panel *panel)
0036 {
0037     return container_of(panel, struct lg4573, panel);
0038 }
0039 
0040 static int lg4573_spi_write_u16(struct lg4573 *ctx, u16 data)
0041 {
0042     struct spi_transfer xfer = {
0043         .len = 2,
0044     };
0045     __be16 temp = cpu_to_be16(data);
0046     struct spi_message msg;
0047 
0048     dev_dbg(ctx->panel.dev, "writing data: %x\n", data);
0049     xfer.tx_buf = &temp;
0050     spi_message_init(&msg);
0051     spi_message_add_tail(&xfer, &msg);
0052 
0053     return spi_sync(ctx->spi, &msg);
0054 }
0055 
0056 static int lg4573_spi_write_u16_array(struct lg4573 *ctx, const u16 *buffer,
0057                       unsigned int count)
0058 {
0059     unsigned int i;
0060     int ret;
0061 
0062     for (i = 0; i < count; i++) {
0063         ret = lg4573_spi_write_u16(ctx, buffer[i]);
0064         if (ret)
0065             return ret;
0066     }
0067 
0068     return 0;
0069 }
0070 
0071 static int lg4573_spi_write_dcs(struct lg4573 *ctx, u8 dcs)
0072 {
0073     return lg4573_spi_write_u16(ctx, (0x70 << 8 | dcs));
0074 }
0075 
0076 static int lg4573_display_on(struct lg4573 *ctx)
0077 {
0078     int ret;
0079 
0080     ret = lg4573_spi_write_dcs(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
0081     if (ret)
0082         return ret;
0083 
0084     msleep(5);
0085 
0086     return lg4573_spi_write_dcs(ctx, MIPI_DCS_SET_DISPLAY_ON);
0087 }
0088 
0089 static int lg4573_display_off(struct lg4573 *ctx)
0090 {
0091     int ret;
0092 
0093     ret = lg4573_spi_write_dcs(ctx, MIPI_DCS_SET_DISPLAY_OFF);
0094     if (ret)
0095         return ret;
0096 
0097     msleep(120);
0098 
0099     return lg4573_spi_write_dcs(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
0100 }
0101 
0102 static int lg4573_display_mode_settings(struct lg4573 *ctx)
0103 {
0104     static const u16 display_mode_settings[] = {
0105         0x703A, 0x7270, 0x70B1, 0x7208,
0106         0x723B, 0x720F, 0x70B2, 0x7200,
0107         0x72C8, 0x70B3, 0x7200, 0x70B4,
0108         0x7200, 0x70B5, 0x7242, 0x7210,
0109         0x7210, 0x7200, 0x7220, 0x70B6,
0110         0x720B, 0x720F, 0x723C, 0x7213,
0111         0x7213, 0x72E8, 0x70B7, 0x7246,
0112         0x7206, 0x720C, 0x7200, 0x7200,
0113     };
0114 
0115     dev_dbg(ctx->panel.dev, "transfer display mode settings\n");
0116     return lg4573_spi_write_u16_array(ctx, display_mode_settings,
0117                       ARRAY_SIZE(display_mode_settings));
0118 }
0119 
0120 static int lg4573_power_settings(struct lg4573 *ctx)
0121 {
0122     static const u16 power_settings[] = {
0123         0x70C0, 0x7201, 0x7211, 0x70C3,
0124         0x7207, 0x7203, 0x7204, 0x7204,
0125         0x7204, 0x70C4, 0x7212, 0x7224,
0126         0x7218, 0x7218, 0x7202, 0x7249,
0127         0x70C5, 0x726F, 0x70C6, 0x7241,
0128         0x7263,
0129     };
0130 
0131     dev_dbg(ctx->panel.dev, "transfer power settings\n");
0132     return lg4573_spi_write_u16_array(ctx, power_settings,
0133                       ARRAY_SIZE(power_settings));
0134 }
0135 
0136 static int lg4573_gamma_settings(struct lg4573 *ctx)
0137 {
0138     static const u16 gamma_settings[] = {
0139         0x70D0, 0x7203, 0x7207, 0x7273,
0140         0x7235, 0x7200, 0x7201, 0x7220,
0141         0x7200, 0x7203, 0x70D1, 0x7203,
0142         0x7207, 0x7273, 0x7235, 0x7200,
0143         0x7201, 0x7220, 0x7200, 0x7203,
0144         0x70D2, 0x7203, 0x7207, 0x7273,
0145         0x7235, 0x7200, 0x7201, 0x7220,
0146         0x7200, 0x7203, 0x70D3, 0x7203,
0147         0x7207, 0x7273, 0x7235, 0x7200,
0148         0x7201, 0x7220, 0x7200, 0x7203,
0149         0x70D4, 0x7203, 0x7207, 0x7273,
0150         0x7235, 0x7200, 0x7201, 0x7220,
0151         0x7200, 0x7203, 0x70D5, 0x7203,
0152         0x7207, 0x7273, 0x7235, 0x7200,
0153         0x7201, 0x7220, 0x7200, 0x7203,
0154     };
0155 
0156     dev_dbg(ctx->panel.dev, "transfer gamma settings\n");
0157     return lg4573_spi_write_u16_array(ctx, gamma_settings,
0158                       ARRAY_SIZE(gamma_settings));
0159 }
0160 
0161 static int lg4573_init(struct lg4573 *ctx)
0162 {
0163     int ret;
0164 
0165     dev_dbg(ctx->panel.dev, "initializing LCD\n");
0166 
0167     ret = lg4573_display_mode_settings(ctx);
0168     if (ret)
0169         return ret;
0170 
0171     ret = lg4573_power_settings(ctx);
0172     if (ret)
0173         return ret;
0174 
0175     return lg4573_gamma_settings(ctx);
0176 }
0177 
0178 static int lg4573_power_on(struct lg4573 *ctx)
0179 {
0180     return lg4573_display_on(ctx);
0181 }
0182 
0183 static int lg4573_disable(struct drm_panel *panel)
0184 {
0185     struct lg4573 *ctx = panel_to_lg4573(panel);
0186 
0187     return lg4573_display_off(ctx);
0188 }
0189 
0190 static int lg4573_enable(struct drm_panel *panel)
0191 {
0192     struct lg4573 *ctx = panel_to_lg4573(panel);
0193 
0194     lg4573_init(ctx);
0195 
0196     return lg4573_power_on(ctx);
0197 }
0198 
0199 static const struct drm_display_mode default_mode = {
0200     .clock = 28341,
0201     .hdisplay = 480,
0202     .hsync_start = 480 + 10,
0203     .hsync_end = 480 + 10 + 59,
0204     .htotal = 480 + 10 + 59 + 10,
0205     .vdisplay = 800,
0206     .vsync_start = 800 + 15,
0207     .vsync_end = 800 + 15 + 15,
0208     .vtotal = 800 + 15 + 15 + 15,
0209 };
0210 
0211 static int lg4573_get_modes(struct drm_panel *panel,
0212                 struct drm_connector *connector)
0213 {
0214     struct drm_display_mode *mode;
0215 
0216     mode = drm_mode_duplicate(connector->dev, &default_mode);
0217     if (!mode) {
0218         dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
0219             default_mode.hdisplay, default_mode.vdisplay,
0220             drm_mode_vrefresh(&default_mode));
0221         return -ENOMEM;
0222     }
0223 
0224     drm_mode_set_name(mode);
0225 
0226     mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0227     drm_mode_probed_add(connector, mode);
0228 
0229     connector->display_info.width_mm = 61;
0230     connector->display_info.height_mm = 103;
0231 
0232     return 1;
0233 }
0234 
0235 static const struct drm_panel_funcs lg4573_drm_funcs = {
0236     .disable = lg4573_disable,
0237     .enable = lg4573_enable,
0238     .get_modes = lg4573_get_modes,
0239 };
0240 
0241 static int lg4573_probe(struct spi_device *spi)
0242 {
0243     struct lg4573 *ctx;
0244     int ret;
0245 
0246     ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL);
0247     if (!ctx)
0248         return -ENOMEM;
0249 
0250     ctx->spi = spi;
0251 
0252     spi_set_drvdata(spi, ctx);
0253     spi->bits_per_word = 8;
0254 
0255     ret = spi_setup(spi);
0256     if (ret < 0) {
0257         dev_err(&spi->dev, "SPI setup failed: %d\n", ret);
0258         return ret;
0259     }
0260 
0261     drm_panel_init(&ctx->panel, &spi->dev, &lg4573_drm_funcs,
0262                DRM_MODE_CONNECTOR_DPI);
0263 
0264     drm_panel_add(&ctx->panel);
0265 
0266     return 0;
0267 }
0268 
0269 static void lg4573_remove(struct spi_device *spi)
0270 {
0271     struct lg4573 *ctx = spi_get_drvdata(spi);
0272 
0273     lg4573_display_off(ctx);
0274     drm_panel_remove(&ctx->panel);
0275 }
0276 
0277 static const struct of_device_id lg4573_of_match[] = {
0278     { .compatible = "lg,lg4573" },
0279     { }
0280 };
0281 MODULE_DEVICE_TABLE(of, lg4573_of_match);
0282 
0283 static struct spi_driver lg4573_driver = {
0284     .probe = lg4573_probe,
0285     .remove = lg4573_remove,
0286     .driver = {
0287         .name = "lg4573",
0288         .of_match_table = lg4573_of_match,
0289     },
0290 };
0291 module_spi_driver(lg4573_driver);
0292 
0293 MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
0294 MODULE_DESCRIPTION("lg4573 LCD Driver");
0295 MODULE_LICENSE("GPL v2");