linux/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c
<<
>>
Prefs
   1/*
   2 * LG.Philips LB035Q02 LCD Panel driver
   3 *
   4 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
   5 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
   6 * Based on a driver by: Steve Sakoman <steve@sakoman.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms of the GNU General Public License version 2 as published by
  10 * the Free Software Foundation.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/delay.h>
  15#include <linux/spi/spi.h>
  16#include <linux/mutex.h>
  17#include <linux/gpio.h>
  18#include <linux/gpio/consumer.h>
  19
  20#include "../dss/omapdss.h"
  21
  22static const struct videomode lb035q02_vm = {
  23        .hactive = 320,
  24        .vactive = 240,
  25
  26        .pixelclock     = 6500000,
  27
  28        .hsync_len      = 2,
  29        .hfront_porch   = 20,
  30        .hback_porch    = 68,
  31
  32        .vsync_len      = 2,
  33        .vfront_porch   = 4,
  34        .vback_porch    = 18,
  35
  36        .flags          = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
  37                          DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_SYNC_NEGEDGE |
  38                          DISPLAY_FLAGS_PIXDATA_POSEDGE,
  39        /*
  40         * Note: According to the panel documentation:
  41         * DE is active LOW
  42         * DATA needs to be driven on the FALLING edge
  43         */
  44};
  45
  46struct panel_drv_data {
  47        struct omap_dss_device dssdev;
  48        struct omap_dss_device *in;
  49
  50        struct spi_device *spi;
  51
  52        struct videomode vm;
  53
  54        struct gpio_desc *enable_gpio;
  55};
  56
  57#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
  58
  59static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val)
  60{
  61        struct spi_message msg;
  62        struct spi_transfer index_xfer = {
  63                .len            = 3,
  64                .cs_change      = 1,
  65        };
  66        struct spi_transfer value_xfer = {
  67                .len            = 3,
  68        };
  69        u8      buffer[16];
  70
  71        spi_message_init(&msg);
  72
  73        /* register index */
  74        buffer[0] = 0x70;
  75        buffer[1] = 0x00;
  76        buffer[2] = reg & 0x7f;
  77        index_xfer.tx_buf = buffer;
  78        spi_message_add_tail(&index_xfer, &msg);
  79
  80        /* register value */
  81        buffer[4] = 0x72;
  82        buffer[5] = val >> 8;
  83        buffer[6] = val;
  84        value_xfer.tx_buf = buffer + 4;
  85        spi_message_add_tail(&value_xfer, &msg);
  86
  87        return spi_sync(spi, &msg);
  88}
  89
  90static void init_lb035q02_panel(struct spi_device *spi)
  91{
  92        /* Init sequence from page 28 of the lb035q02 spec */
  93        lb035q02_write_reg(spi, 0x01, 0x6300);
  94        lb035q02_write_reg(spi, 0x02, 0x0200);
  95        lb035q02_write_reg(spi, 0x03, 0x0177);
  96        lb035q02_write_reg(spi, 0x04, 0x04c7);
  97        lb035q02_write_reg(spi, 0x05, 0xffc0);
  98        lb035q02_write_reg(spi, 0x06, 0xe806);
  99        lb035q02_write_reg(spi, 0x0a, 0x4008);
 100        lb035q02_write_reg(spi, 0x0b, 0x0000);
 101        lb035q02_write_reg(spi, 0x0d, 0x0030);
 102        lb035q02_write_reg(spi, 0x0e, 0x2800);
 103        lb035q02_write_reg(spi, 0x0f, 0x0000);
 104        lb035q02_write_reg(spi, 0x16, 0x9f80);
 105        lb035q02_write_reg(spi, 0x17, 0x0a0f);
 106        lb035q02_write_reg(spi, 0x1e, 0x00c1);
 107        lb035q02_write_reg(spi, 0x30, 0x0300);
 108        lb035q02_write_reg(spi, 0x31, 0x0007);
 109        lb035q02_write_reg(spi, 0x32, 0x0000);
 110        lb035q02_write_reg(spi, 0x33, 0x0000);
 111        lb035q02_write_reg(spi, 0x34, 0x0707);
 112        lb035q02_write_reg(spi, 0x35, 0x0004);
 113        lb035q02_write_reg(spi, 0x36, 0x0302);
 114        lb035q02_write_reg(spi, 0x37, 0x0202);
 115        lb035q02_write_reg(spi, 0x3a, 0x0a0d);
 116        lb035q02_write_reg(spi, 0x3b, 0x0806);
 117}
 118
 119static int lb035q02_connect(struct omap_dss_device *dssdev)
 120{
 121        struct panel_drv_data *ddata = to_panel_data(dssdev);
 122        struct omap_dss_device *in;
 123        int r;
 124
 125        if (omapdss_device_is_connected(dssdev))
 126                return 0;
 127
 128        in = omapdss_of_find_source_for_first_ep(dssdev->dev->of_node);
 129        if (IS_ERR(in)) {
 130                dev_err(dssdev->dev, "failed to find video source\n");
 131                return PTR_ERR(in);
 132        }
 133
 134        r = in->ops.dpi->connect(in, dssdev);
 135        if (r) {
 136                omap_dss_put_device(in);
 137                return r;
 138        }
 139
 140        init_lb035q02_panel(ddata->spi);
 141
 142        ddata->in = in;
 143        return 0;
 144}
 145
 146static void lb035q02_disconnect(struct omap_dss_device *dssdev)
 147{
 148        struct panel_drv_data *ddata = to_panel_data(dssdev);
 149        struct omap_dss_device *in = ddata->in;
 150
 151        if (!omapdss_device_is_connected(dssdev))
 152                return;
 153
 154        in->ops.dpi->disconnect(in, dssdev);
 155
 156        omap_dss_put_device(in);
 157        ddata->in = NULL;
 158}
 159
 160static int lb035q02_enable(struct omap_dss_device *dssdev)
 161{
 162        struct panel_drv_data *ddata = to_panel_data(dssdev);
 163        struct omap_dss_device *in = ddata->in;
 164        int r;
 165
 166        if (!omapdss_device_is_connected(dssdev))
 167                return -ENODEV;
 168
 169        if (omapdss_device_is_enabled(dssdev))
 170                return 0;
 171
 172        in->ops.dpi->set_timings(in, &ddata->vm);
 173
 174        r = in->ops.dpi->enable(in);
 175        if (r)
 176                return r;
 177
 178        if (ddata->enable_gpio)
 179                gpiod_set_value_cansleep(ddata->enable_gpio, 1);
 180
 181        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 182
 183        return 0;
 184}
 185
 186static void lb035q02_disable(struct omap_dss_device *dssdev)
 187{
 188        struct panel_drv_data *ddata = to_panel_data(dssdev);
 189        struct omap_dss_device *in = ddata->in;
 190
 191        if (!omapdss_device_is_enabled(dssdev))
 192                return;
 193
 194        if (ddata->enable_gpio)
 195                gpiod_set_value_cansleep(ddata->enable_gpio, 0);
 196
 197        in->ops.dpi->disable(in);
 198
 199        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 200}
 201
 202static void lb035q02_set_timings(struct omap_dss_device *dssdev,
 203                                 struct videomode *vm)
 204{
 205        struct panel_drv_data *ddata = to_panel_data(dssdev);
 206        struct omap_dss_device *in = ddata->in;
 207
 208        ddata->vm = *vm;
 209        dssdev->panel.vm = *vm;
 210
 211        in->ops.dpi->set_timings(in, vm);
 212}
 213
 214static void lb035q02_get_timings(struct omap_dss_device *dssdev,
 215                                 struct videomode *vm)
 216{
 217        struct panel_drv_data *ddata = to_panel_data(dssdev);
 218
 219        *vm = ddata->vm;
 220}
 221
 222static int lb035q02_check_timings(struct omap_dss_device *dssdev,
 223                                  struct videomode *vm)
 224{
 225        struct panel_drv_data *ddata = to_panel_data(dssdev);
 226        struct omap_dss_device *in = ddata->in;
 227
 228        return in->ops.dpi->check_timings(in, vm);
 229}
 230
 231static struct omap_dss_driver lb035q02_ops = {
 232        .connect        = lb035q02_connect,
 233        .disconnect     = lb035q02_disconnect,
 234
 235        .enable         = lb035q02_enable,
 236        .disable        = lb035q02_disable,
 237
 238        .set_timings    = lb035q02_set_timings,
 239        .get_timings    = lb035q02_get_timings,
 240        .check_timings  = lb035q02_check_timings,
 241};
 242
 243static int lb035q02_probe_of(struct spi_device *spi)
 244{
 245        struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
 246        struct gpio_desc *gpio;
 247
 248        gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
 249        if (IS_ERR(gpio)) {
 250                dev_err(&spi->dev, "failed to parse enable gpio\n");
 251                return PTR_ERR(gpio);
 252        }
 253
 254        ddata->enable_gpio = gpio;
 255
 256        return 0;
 257}
 258
 259static int lb035q02_panel_spi_probe(struct spi_device *spi)
 260{
 261        struct panel_drv_data *ddata;
 262        struct omap_dss_device *dssdev;
 263        int r;
 264
 265        ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
 266        if (ddata == NULL)
 267                return -ENOMEM;
 268
 269        dev_set_drvdata(&spi->dev, ddata);
 270
 271        ddata->spi = spi;
 272
 273        r = lb035q02_probe_of(spi);
 274        if (r)
 275                return r;
 276
 277        ddata->vm = lb035q02_vm;
 278
 279        dssdev = &ddata->dssdev;
 280        dssdev->dev = &spi->dev;
 281        dssdev->driver = &lb035q02_ops;
 282        dssdev->type = OMAP_DISPLAY_TYPE_DPI;
 283        dssdev->owner = THIS_MODULE;
 284        dssdev->panel.vm = ddata->vm;
 285
 286        r = omapdss_register_display(dssdev);
 287        if (r) {
 288                dev_err(&spi->dev, "Failed to register panel\n");
 289                return r;
 290        }
 291
 292        return 0;
 293}
 294
 295static int lb035q02_panel_spi_remove(struct spi_device *spi)
 296{
 297        struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
 298        struct omap_dss_device *dssdev = &ddata->dssdev;
 299
 300        omapdss_unregister_display(dssdev);
 301
 302        lb035q02_disable(dssdev);
 303        lb035q02_disconnect(dssdev);
 304
 305        return 0;
 306}
 307
 308static const struct of_device_id lb035q02_of_match[] = {
 309        { .compatible = "omapdss,lgphilips,lb035q02", },
 310        {},
 311};
 312
 313MODULE_DEVICE_TABLE(of, lb035q02_of_match);
 314
 315static struct spi_driver lb035q02_spi_driver = {
 316        .probe          = lb035q02_panel_spi_probe,
 317        .remove         = lb035q02_panel_spi_remove,
 318        .driver         = {
 319                .name   = "panel_lgphilips_lb035q02",
 320                .of_match_table = lb035q02_of_match,
 321                .suppress_bind_attrs = true,
 322        },
 323};
 324
 325module_spi_driver(lb035q02_spi_driver);
 326
 327MODULE_ALIAS("spi:lgphilips,lb035q02");
 328MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 329MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
 330MODULE_LICENSE("GPL");
 331