linux/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
<<
>>
Prefs
   1/*
   2 * LCD panel driver for LG.Philips LB035Q02
   3 *
   4 * Author: Steve Sakoman <steve@sakoman.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms of the GNU General Public License version 2 as published by
   8 * the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 *
  15 * You should have received a copy of the GNU General Public License along with
  16 * this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/delay.h>
  21#include <linux/spi/spi.h>
  22#include <linux/mutex.h>
  23
  24#include <video/omapdss.h>
  25
  26struct lb035q02_data {
  27        struct mutex lock;
  28};
  29
  30static struct omap_video_timings lb035q02_timings = {
  31        .x_res = 320,
  32        .y_res = 240,
  33
  34        .pixel_clock    = 6500,
  35
  36        .hsw            = 2,
  37        .hfp            = 20,
  38        .hbp            = 68,
  39
  40        .vsw            = 2,
  41        .vfp            = 4,
  42        .vbp            = 18,
  43
  44        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
  45        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
  46        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
  47        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
  48        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
  49};
  50
  51static int lb035q02_panel_power_on(struct omap_dss_device *dssdev)
  52{
  53        int r;
  54
  55        if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
  56                return 0;
  57
  58        omapdss_dpi_set_timings(dssdev, &dssdev->panel.timings);
  59        omapdss_dpi_set_data_lines(dssdev, dssdev->phy.dpi.data_lines);
  60
  61        r = omapdss_dpi_display_enable(dssdev);
  62        if (r)
  63                goto err0;
  64
  65        if (dssdev->platform_enable) {
  66                r = dssdev->platform_enable(dssdev);
  67                if (r)
  68                        goto err1;
  69        }
  70
  71        return 0;
  72err1:
  73        omapdss_dpi_display_disable(dssdev);
  74err0:
  75        return r;
  76}
  77
  78static void lb035q02_panel_power_off(struct omap_dss_device *dssdev)
  79{
  80        if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
  81                return;
  82
  83        if (dssdev->platform_disable)
  84                dssdev->platform_disable(dssdev);
  85
  86        omapdss_dpi_display_disable(dssdev);
  87}
  88
  89static int lb035q02_panel_probe(struct omap_dss_device *dssdev)
  90{
  91        struct lb035q02_data *ld;
  92        int r;
  93
  94        dssdev->panel.timings = lb035q02_timings;
  95
  96        ld = kzalloc(sizeof(*ld), GFP_KERNEL);
  97        if (!ld) {
  98                r = -ENOMEM;
  99                goto err;
 100        }
 101        mutex_init(&ld->lock);
 102        dev_set_drvdata(&dssdev->dev, ld);
 103        return 0;
 104err:
 105        return r;
 106}
 107
 108static void lb035q02_panel_remove(struct omap_dss_device *dssdev)
 109{
 110        struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
 111
 112        kfree(ld);
 113}
 114
 115static int lb035q02_panel_enable(struct omap_dss_device *dssdev)
 116{
 117        struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
 118        int r;
 119
 120        mutex_lock(&ld->lock);
 121
 122        r = lb035q02_panel_power_on(dssdev);
 123        if (r)
 124                goto err;
 125        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 126
 127        mutex_unlock(&ld->lock);
 128        return 0;
 129err:
 130        mutex_unlock(&ld->lock);
 131        return r;
 132}
 133
 134static void lb035q02_panel_disable(struct omap_dss_device *dssdev)
 135{
 136        struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
 137
 138        mutex_lock(&ld->lock);
 139
 140        lb035q02_panel_power_off(dssdev);
 141        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 142
 143        mutex_unlock(&ld->lock);
 144}
 145
 146static int lb035q02_panel_suspend(struct omap_dss_device *dssdev)
 147{
 148        struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
 149
 150        mutex_lock(&ld->lock);
 151
 152        lb035q02_panel_power_off(dssdev);
 153        dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
 154
 155        mutex_unlock(&ld->lock);
 156        return 0;
 157}
 158
 159static int lb035q02_panel_resume(struct omap_dss_device *dssdev)
 160{
 161        struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
 162        int r;
 163
 164        mutex_lock(&ld->lock);
 165
 166        r = lb035q02_panel_power_on(dssdev);
 167        if (r)
 168                goto err;
 169        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 170
 171        mutex_unlock(&ld->lock);
 172        return 0;
 173err:
 174        mutex_unlock(&ld->lock);
 175        return r;
 176}
 177
 178static struct omap_dss_driver lb035q02_driver = {
 179        .probe          = lb035q02_panel_probe,
 180        .remove         = lb035q02_panel_remove,
 181
 182        .enable         = lb035q02_panel_enable,
 183        .disable        = lb035q02_panel_disable,
 184        .suspend        = lb035q02_panel_suspend,
 185        .resume         = lb035q02_panel_resume,
 186
 187        .driver         = {
 188                .name   = "lgphilips_lb035q02_panel",
 189                .owner  = THIS_MODULE,
 190        },
 191};
 192
 193static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val)
 194{
 195        struct spi_message msg;
 196        struct spi_transfer index_xfer = {
 197                .len            = 3,
 198                .cs_change      = 1,
 199        };
 200        struct spi_transfer value_xfer = {
 201                .len            = 3,
 202        };
 203        u8      buffer[16];
 204
 205        spi_message_init(&msg);
 206
 207        /* register index */
 208        buffer[0] = 0x70;
 209        buffer[1] = 0x00;
 210        buffer[2] = reg & 0x7f;
 211        index_xfer.tx_buf = buffer;
 212        spi_message_add_tail(&index_xfer, &msg);
 213
 214        /* register value */
 215        buffer[4] = 0x72;
 216        buffer[5] = val >> 8;
 217        buffer[6] = val;
 218        value_xfer.tx_buf = buffer + 4;
 219        spi_message_add_tail(&value_xfer, &msg);
 220
 221        return spi_sync(spi, &msg);
 222}
 223
 224static void init_lb035q02_panel(struct spi_device *spi)
 225{
 226        /* Init sequence from page 28 of the lb035q02 spec */
 227        lb035q02_write_reg(spi, 0x01, 0x6300);
 228        lb035q02_write_reg(spi, 0x02, 0x0200);
 229        lb035q02_write_reg(spi, 0x03, 0x0177);
 230        lb035q02_write_reg(spi, 0x04, 0x04c7);
 231        lb035q02_write_reg(spi, 0x05, 0xffc0);
 232        lb035q02_write_reg(spi, 0x06, 0xe806);
 233        lb035q02_write_reg(spi, 0x0a, 0x4008);
 234        lb035q02_write_reg(spi, 0x0b, 0x0000);
 235        lb035q02_write_reg(spi, 0x0d, 0x0030);
 236        lb035q02_write_reg(spi, 0x0e, 0x2800);
 237        lb035q02_write_reg(spi, 0x0f, 0x0000);
 238        lb035q02_write_reg(spi, 0x16, 0x9f80);
 239        lb035q02_write_reg(spi, 0x17, 0x0a0f);
 240        lb035q02_write_reg(spi, 0x1e, 0x00c1);
 241        lb035q02_write_reg(spi, 0x30, 0x0300);
 242        lb035q02_write_reg(spi, 0x31, 0x0007);
 243        lb035q02_write_reg(spi, 0x32, 0x0000);
 244        lb035q02_write_reg(spi, 0x33, 0x0000);
 245        lb035q02_write_reg(spi, 0x34, 0x0707);
 246        lb035q02_write_reg(spi, 0x35, 0x0004);
 247        lb035q02_write_reg(spi, 0x36, 0x0302);
 248        lb035q02_write_reg(spi, 0x37, 0x0202);
 249        lb035q02_write_reg(spi, 0x3a, 0x0a0d);
 250        lb035q02_write_reg(spi, 0x3b, 0x0806);
 251}
 252
 253static int __devinit lb035q02_panel_spi_probe(struct spi_device *spi)
 254{
 255        init_lb035q02_panel(spi);
 256        return omap_dss_register_driver(&lb035q02_driver);
 257}
 258
 259static int __devexit lb035q02_panel_spi_remove(struct spi_device *spi)
 260{
 261        omap_dss_unregister_driver(&lb035q02_driver);
 262        return 0;
 263}
 264
 265static struct spi_driver lb035q02_spi_driver = {
 266        .driver         = {
 267                .name   = "lgphilips_lb035q02_panel-spi",
 268                .owner  = THIS_MODULE,
 269        },
 270        .probe          = lb035q02_panel_spi_probe,
 271        .remove         = __devexit_p(lb035q02_panel_spi_remove),
 272};
 273
 274module_spi_driver(lb035q02_spi_driver);
 275
 276MODULE_LICENSE("GPL");
 277