linux/drivers/gpu/drm/panel/panel-samsung-db7430.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Panel driver for the Samsung LMS397KF04 480x800 DPI RGB panel.
   4 * According to the data sheet the display controller is called DB7430.
   5 * Found in the Samsung Galaxy Beam GT-I8350 mobile phone.
   6 * Linus Walleij <linus.walleij@linaro.org>
   7 */
   8#include <drm/drm_mipi_dbi.h>
   9#include <drm/drm_modes.h>
  10#include <drm/drm_panel.h>
  11
  12#include <linux/delay.h>
  13#include <linux/gpio/consumer.h>
  14#include <linux/init.h>
  15#include <linux/kernel.h>
  16#include <linux/media-bus-format.h>
  17#include <linux/module.h>
  18#include <linux/of.h>
  19#include <linux/regulator/consumer.h>
  20#include <linux/spi/spi.h>
  21
  22#include <video/mipi_display.h>
  23
  24#define DB7430_ACCESS_PROT_OFF          0xb0
  25#define DB7430_UNKNOWN_B4               0xb4
  26#define DB7430_USER_SELECT              0xb5
  27#define DB7430_UNKNOWN_B7               0xb7
  28#define DB7430_UNKNOWN_B8               0xb8
  29#define DB7430_PANEL_DRIVING            0xc0
  30#define DB7430_SOURCE_CONTROL           0xc1
  31#define DB7430_GATE_INTERFACE           0xc4
  32#define DB7430_DISPLAY_H_TIMING         0xc5
  33#define DB7430_RGB_SYNC_OPTION          0xc6
  34#define DB7430_GAMMA_SET_RED            0xc8
  35#define DB7430_GAMMA_SET_GREEN          0xc9
  36#define DB7430_GAMMA_SET_BLUE           0xca
  37#define DB7430_BIAS_CURRENT_CTRL        0xd1
  38#define DB7430_DDV_CTRL                 0xd2
  39#define DB7430_GAMMA_CTRL_REF           0xd3
  40#define DB7430_UNKNOWN_D4               0xd4
  41#define DB7430_DCDC_CTRL                0xd5
  42#define DB7430_VCL_CTRL                 0xd6
  43#define DB7430_UNKNOWN_F8               0xf8
  44#define DB7430_UNKNOWN_FC               0xfc
  45
  46#define DATA_MASK       0x100
  47
  48/**
  49 * struct db7430 - state container for a panel controlled by the DB7430
  50 * controller
  51 */
  52struct db7430 {
  53        /** @dev: the container device */
  54        struct device *dev;
  55        /** @dbi: the DBI bus abstraction handle */
  56        struct mipi_dbi dbi;
  57        /** @panel: the DRM panel instance for this device */
  58        struct drm_panel panel;
  59        /** @width: the width of this panel in mm */
  60        u32 width;
  61        /** @height: the height of this panel in mm */
  62        u32 height;
  63        /** @reset: reset GPIO line */
  64        struct gpio_desc *reset;
  65        /** @regulators: VCCIO and VIO supply regulators */
  66        struct regulator_bulk_data regulators[2];
  67};
  68
  69static const struct drm_display_mode db7430_480_800_mode = {
  70        /*
  71         * 31 ns period min (htotal*vtotal*vrefresh)/1000
  72         * gives a Vrefresh of ~71 Hz.
  73         */
  74        .clock = 32258,
  75        .hdisplay = 480,
  76        .hsync_start = 480 + 10,
  77        .hsync_end = 480 + 10 + 4,
  78        .htotal = 480 + 10 + 4 + 40,
  79        .vdisplay = 800,
  80        .vsync_start = 800 + 6,
  81        .vsync_end = 800 + 6 + 1,
  82        .vtotal = 800 + 6 + 1 + 7,
  83        .width_mm = 53,
  84        .height_mm = 87,
  85        .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
  86};
  87
  88static inline struct db7430 *to_db7430(struct drm_panel *panel)
  89{
  90        return container_of(panel, struct db7430, panel);
  91}
  92
  93static int db7430_power_on(struct db7430 *db)
  94{
  95        struct mipi_dbi *dbi = &db->dbi;
  96        int ret;
  97
  98        /* Power up */
  99        ret = regulator_bulk_enable(ARRAY_SIZE(db->regulators),
 100                                    db->regulators);
 101        if (ret) {
 102                dev_err(db->dev, "failed to enable regulators: %d\n", ret);
 103                return ret;
 104        }
 105        msleep(50);
 106
 107        /* Assert reset >=1 ms */
 108        gpiod_set_value_cansleep(db->reset, 1);
 109        usleep_range(1000, 5000);
 110        /* De-assert reset */
 111        gpiod_set_value_cansleep(db->reset, 0);
 112        /* Wait >= 10 ms */
 113        msleep(10);
 114        dev_dbg(db->dev, "de-asserted RESET\n");
 115
 116        /*
 117         * This is set to 0x0a (RGB/BGR order + horizontal flip) in order
 118         * to make the display behave normally. If this is not set the displays
 119         * normal output behaviour is horizontally flipped and BGR ordered. Do
 120         * it twice because the first message doesn't always "take".
 121         */
 122        mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0x0a);
 123        mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0x0a);
 124        mipi_dbi_command(dbi, DB7430_ACCESS_PROT_OFF, 0x00);
 125        mipi_dbi_command(dbi, DB7430_PANEL_DRIVING, 0x28, 0x08);
 126        mipi_dbi_command(dbi, DB7430_SOURCE_CONTROL,
 127                         0x01, 0x30, 0x15, 0x05, 0x22);
 128        mipi_dbi_command(dbi, DB7430_GATE_INTERFACE,
 129                         0x10, 0x01, 0x00);
 130        mipi_dbi_command(dbi, DB7430_DISPLAY_H_TIMING,
 131                         0x06, 0x55, 0x03, 0x07, 0x0b,
 132                         0x33, 0x00, 0x01, 0x03);
 133        /*
 134         * 0x00 in datasheet 0x01 in vendor code 0x00, it seems 0x01 means
 135         * DE active high and 0x00 means DE active low.
 136         */
 137        mipi_dbi_command(dbi, DB7430_RGB_SYNC_OPTION, 0x01);
 138        mipi_dbi_command(dbi, DB7430_GAMMA_SET_RED,
 139                /* R positive gamma */ 0x00,
 140                0x0A, 0x31, 0x3B, 0x4E, 0x58, 0x59, 0x5B, 0x58, 0x5E, 0x62,
 141                0x60, 0x61, 0x5E, 0x62, 0x55, 0x55, 0x7F, 0x08,
 142                /* R negative gamma */ 0x00,
 143                0x0A, 0x31, 0x3B, 0x4E, 0x58, 0x59, 0x5B, 0x58, 0x5E, 0x62,
 144                0x60, 0x61, 0x5E, 0x62, 0x55, 0x55, 0x7F, 0x08);
 145        mipi_dbi_command(dbi, DB7430_GAMMA_SET_GREEN,
 146                /* G positive gamma */ 0x00,
 147                0x25, 0x15, 0x28, 0x3D, 0x4A, 0x48, 0x4C, 0x4A, 0x52, 0x59,
 148                0x59, 0x5B, 0x56, 0x60, 0x5D, 0x55, 0x7F, 0x0A,
 149                /* G negative gamma */ 0x00,
 150                0x25, 0x15, 0x28, 0x3D, 0x4A, 0x48, 0x4C, 0x4A, 0x52, 0x59,
 151                0x59, 0x5B, 0x56, 0x60, 0x5D, 0x55, 0x7F, 0x0A);
 152        mipi_dbi_command(dbi, DB7430_GAMMA_SET_BLUE,
 153                /* B positive gamma */ 0x00,
 154                0x48, 0x10, 0x1F, 0x2F, 0x35, 0x38, 0x3D, 0x3C, 0x45, 0x4D,
 155                0x4E, 0x52, 0x51, 0x60, 0x7F, 0x7E, 0x7F, 0x0C,
 156                /* B negative gamma */ 0x00,
 157                0x48, 0x10, 0x1F, 0x2F, 0x35, 0x38, 0x3D, 0x3C, 0x45, 0x4D,
 158                0x4E, 0x52, 0x51, 0x60, 0x7F, 0x7E, 0x7F, 0x0C);
 159        mipi_dbi_command(dbi, DB7430_BIAS_CURRENT_CTRL, 0x33, 0x13);
 160        mipi_dbi_command(dbi, DB7430_DDV_CTRL, 0x11, 0x00, 0x00);
 161        mipi_dbi_command(dbi, DB7430_GAMMA_CTRL_REF, 0x50, 0x50);
 162        mipi_dbi_command(dbi, DB7430_DCDC_CTRL, 0x2f, 0x11, 0x1e, 0x46);
 163        mipi_dbi_command(dbi, DB7430_VCL_CTRL, 0x11, 0x0a);
 164
 165        return 0;
 166}
 167
 168static int db7430_power_off(struct db7430 *db)
 169{
 170        /* Go into RESET and disable regulators */
 171        gpiod_set_value_cansleep(db->reset, 1);
 172        return regulator_bulk_disable(ARRAY_SIZE(db->regulators),
 173                                      db->regulators);
 174}
 175
 176static int db7430_unprepare(struct drm_panel *panel)
 177{
 178        return db7430_power_off(to_db7430(panel));
 179}
 180
 181static int db7430_disable(struct drm_panel *panel)
 182{
 183        struct db7430 *db = to_db7430(panel);
 184        struct mipi_dbi *dbi = &db->dbi;
 185
 186        mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
 187        msleep(25);
 188        mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE);
 189        msleep(120);
 190
 191        return 0;
 192}
 193
 194static int db7430_prepare(struct drm_panel *panel)
 195{
 196        return db7430_power_on(to_db7430(panel));
 197}
 198
 199static int db7430_enable(struct drm_panel *panel)
 200{
 201        struct db7430 *db = to_db7430(panel);
 202        struct mipi_dbi *dbi = &db->dbi;
 203
 204        /* Exit sleep mode */
 205        mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
 206        msleep(20);
 207
 208        /* NVM (non-volatile memory) load sequence */
 209        mipi_dbi_command(dbi, DB7430_UNKNOWN_D4, 0x52, 0x5e);
 210        mipi_dbi_command(dbi, DB7430_UNKNOWN_F8, 0x01, 0xf5, 0xf2, 0x71, 0x44);
 211        mipi_dbi_command(dbi, DB7430_UNKNOWN_FC, 0x00, 0x08);
 212        msleep(150);
 213
 214        /* CABC turn on sequence (BC = backlight control) */
 215        mipi_dbi_command(dbi, DB7430_UNKNOWN_B4, 0x0f, 0x00, 0x50);
 216        mipi_dbi_command(dbi, DB7430_USER_SELECT, 0x80);
 217        mipi_dbi_command(dbi, DB7430_UNKNOWN_B7, 0x24);
 218        mipi_dbi_command(dbi, DB7430_UNKNOWN_B8, 0x01);
 219
 220        /* Turn on display */
 221        mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
 222
 223        return 0;
 224}
 225
 226/**
 227 * db7430_get_modes() - return the mode
 228 * @panel: the panel to get the mode for
 229 * @connector: reference to the central DRM connector control structure
 230 */
 231static int db7430_get_modes(struct drm_panel *panel,
 232                            struct drm_connector *connector)
 233{
 234        struct db7430 *db = to_db7430(panel);
 235        struct drm_display_mode *mode;
 236        static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
 237
 238        mode = drm_mode_duplicate(connector->dev, &db7430_480_800_mode);
 239        if (!mode) {
 240                dev_err(db->dev, "failed to add mode\n");
 241                return -ENOMEM;
 242        }
 243
 244        connector->display_info.bpc = 8;
 245        connector->display_info.width_mm = mode->width_mm;
 246        connector->display_info.height_mm = mode->height_mm;
 247        connector->display_info.bus_flags =
 248                DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
 249        drm_display_info_set_bus_formats(&connector->display_info,
 250                                         &bus_format, 1);
 251
 252        drm_mode_set_name(mode);
 253        mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 254
 255        drm_mode_probed_add(connector, mode);
 256
 257        return 1;
 258}
 259
 260static const struct drm_panel_funcs db7430_drm_funcs = {
 261        .disable = db7430_disable,
 262        .unprepare = db7430_unprepare,
 263        .prepare = db7430_prepare,
 264        .enable = db7430_enable,
 265        .get_modes = db7430_get_modes,
 266};
 267
 268static int db7430_probe(struct spi_device *spi)
 269{
 270        struct device *dev = &spi->dev;
 271        struct db7430 *db;
 272        int ret;
 273
 274        db = devm_kzalloc(dev, sizeof(*db), GFP_KERNEL);
 275        if (!db)
 276                return -ENOMEM;
 277        db->dev = dev;
 278
 279        /*
 280         * VCI   is the analog voltage supply
 281         * VCCIO is the digital I/O voltage supply
 282         */
 283        db->regulators[0].supply = "vci";
 284        db->regulators[1].supply = "vccio";
 285        ret = devm_regulator_bulk_get(dev,
 286                                      ARRAY_SIZE(db->regulators),
 287                                      db->regulators);
 288        if (ret)
 289                return dev_err_probe(dev, ret, "failed to get regulators\n");
 290
 291        db->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
 292        if (IS_ERR(db->reset)) {
 293                ret = PTR_ERR(db->reset);
 294                return dev_err_probe(dev, ret, "no RESET GPIO\n");
 295        }
 296
 297        ret = mipi_dbi_spi_init(spi, &db->dbi, NULL);
 298        if (ret)
 299                return dev_err_probe(dev, ret, "MIPI DBI init failed\n");
 300
 301        drm_panel_init(&db->panel, dev, &db7430_drm_funcs,
 302                       DRM_MODE_CONNECTOR_DPI);
 303
 304        /* FIXME: if no external backlight, use internal backlight */
 305        ret = drm_panel_of_backlight(&db->panel);
 306        if (ret)
 307                return dev_err_probe(dev, ret, "failed to add backlight\n");
 308
 309        spi_set_drvdata(spi, db);
 310
 311        drm_panel_add(&db->panel);
 312        dev_dbg(dev, "added panel\n");
 313
 314        return 0;
 315}
 316
 317static void db7430_remove(struct spi_device *spi)
 318{
 319        struct db7430 *db = spi_get_drvdata(spi);
 320
 321        drm_panel_remove(&db->panel);
 322}
 323
 324/*
 325 * The DB7430 display controller may be used in several display products,
 326 * so list the different variants here and add per-variant data if needed.
 327 */
 328static const struct of_device_id db7430_match[] = {
 329        { .compatible = "samsung,lms397kf04", },
 330        {},
 331};
 332MODULE_DEVICE_TABLE(of, db7430_match);
 333
 334static struct spi_driver db7430_driver = {
 335        .probe          = db7430_probe,
 336        .remove         = db7430_remove,
 337        .driver         = {
 338                .name   = "db7430-panel",
 339                .of_match_table = db7430_match,
 340        },
 341};
 342module_spi_driver(db7430_driver);
 343
 344MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
 345MODULE_DESCRIPTION("Samsung DB7430 panel driver");
 346MODULE_LICENSE("GPL v2");
 347