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