linux/drivers/video/fbdev/omap2/omapfb/displays/panel-sharp-ls037v7dw01.c
<<
>>
Prefs
   1/*
   2 * LCD panel driver for Sharp LS037V7DW01
   3 *
   4 * Copyright (C) 2013 Texas Instruments
   5 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License version 2 as published by
   9 * the Free Software Foundation.
  10 */
  11
  12#include <linux/delay.h>
  13#include <linux/gpio.h>
  14#include <linux/module.h>
  15#include <linux/of.h>
  16#include <linux/of_gpio.h>
  17#include <linux/platform_device.h>
  18#include <linux/slab.h>
  19#include <linux/regulator/consumer.h>
  20#include <video/omapfb_dss.h>
  21
  22struct panel_drv_data {
  23        struct omap_dss_device dssdev;
  24        struct omap_dss_device *in;
  25        struct regulator *vcc;
  26
  27        int data_lines;
  28
  29        struct omap_video_timings videomode;
  30
  31        struct gpio_desc *resb_gpio;    /* low = reset active min 20 us */
  32        struct gpio_desc *ini_gpio;     /* high = power on */
  33        struct gpio_desc *mo_gpio;      /* low = 480x640, high = 240x320 */
  34        struct gpio_desc *lr_gpio;      /* high = conventional horizontal scanning */
  35        struct gpio_desc *ud_gpio;      /* high = conventional vertical scanning */
  36};
  37
  38static const struct omap_video_timings sharp_ls_timings = {
  39        .x_res = 480,
  40        .y_res = 640,
  41
  42        .pixelclock     = 19200000,
  43
  44        .hsw            = 2,
  45        .hfp            = 1,
  46        .hbp            = 28,
  47
  48        .vsw            = 1,
  49        .vfp            = 1,
  50        .vbp            = 1,
  51
  52        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
  53        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
  54        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
  55        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
  56        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
  57};
  58
  59#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
  60
  61static int sharp_ls_connect(struct omap_dss_device *dssdev)
  62{
  63        struct panel_drv_data *ddata = to_panel_data(dssdev);
  64        struct omap_dss_device *in = ddata->in;
  65        int r;
  66
  67        if (omapdss_device_is_connected(dssdev))
  68                return 0;
  69
  70        r = in->ops.dpi->connect(in, dssdev);
  71        if (r)
  72                return r;
  73
  74        return 0;
  75}
  76
  77static void sharp_ls_disconnect(struct omap_dss_device *dssdev)
  78{
  79        struct panel_drv_data *ddata = to_panel_data(dssdev);
  80        struct omap_dss_device *in = ddata->in;
  81
  82        if (!omapdss_device_is_connected(dssdev))
  83                return;
  84
  85        in->ops.dpi->disconnect(in, dssdev);
  86}
  87
  88static int sharp_ls_enable(struct omap_dss_device *dssdev)
  89{
  90        struct panel_drv_data *ddata = to_panel_data(dssdev);
  91        struct omap_dss_device *in = ddata->in;
  92        int r;
  93
  94        if (!omapdss_device_is_connected(dssdev))
  95                return -ENODEV;
  96
  97        if (omapdss_device_is_enabled(dssdev))
  98                return 0;
  99
 100        if (ddata->data_lines)
 101                in->ops.dpi->set_data_lines(in, ddata->data_lines);
 102        in->ops.dpi->set_timings(in, &ddata->videomode);
 103
 104        if (ddata->vcc) {
 105                r = regulator_enable(ddata->vcc);
 106                if (r != 0)
 107                        return r;
 108        }
 109
 110        r = in->ops.dpi->enable(in);
 111        if (r) {
 112                regulator_disable(ddata->vcc);
 113                return r;
 114        }
 115
 116        /* wait couple of vsyncs until enabling the LCD */
 117        msleep(50);
 118
 119        if (ddata->resb_gpio)
 120                gpiod_set_value_cansleep(ddata->resb_gpio, 1);
 121
 122        if (ddata->ini_gpio)
 123                gpiod_set_value_cansleep(ddata->ini_gpio, 1);
 124
 125        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 126
 127        return 0;
 128}
 129
 130static void sharp_ls_disable(struct omap_dss_device *dssdev)
 131{
 132        struct panel_drv_data *ddata = to_panel_data(dssdev);
 133        struct omap_dss_device *in = ddata->in;
 134
 135        if (!omapdss_device_is_enabled(dssdev))
 136                return;
 137
 138        if (ddata->ini_gpio)
 139                gpiod_set_value_cansleep(ddata->ini_gpio, 0);
 140
 141        if (ddata->resb_gpio)
 142                gpiod_set_value_cansleep(ddata->resb_gpio, 0);
 143
 144        /* wait at least 5 vsyncs after disabling the LCD */
 145
 146        msleep(100);
 147
 148        in->ops.dpi->disable(in);
 149
 150        if (ddata->vcc)
 151                regulator_disable(ddata->vcc);
 152
 153        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 154}
 155
 156static void sharp_ls_set_timings(struct omap_dss_device *dssdev,
 157                struct omap_video_timings *timings)
 158{
 159        struct panel_drv_data *ddata = to_panel_data(dssdev);
 160        struct omap_dss_device *in = ddata->in;
 161
 162        ddata->videomode = *timings;
 163        dssdev->panel.timings = *timings;
 164
 165        in->ops.dpi->set_timings(in, timings);
 166}
 167
 168static void sharp_ls_get_timings(struct omap_dss_device *dssdev,
 169                struct omap_video_timings *timings)
 170{
 171        struct panel_drv_data *ddata = to_panel_data(dssdev);
 172
 173        *timings = ddata->videomode;
 174}
 175
 176static int sharp_ls_check_timings(struct omap_dss_device *dssdev,
 177                struct omap_video_timings *timings)
 178{
 179        struct panel_drv_data *ddata = to_panel_data(dssdev);
 180        struct omap_dss_device *in = ddata->in;
 181
 182        return in->ops.dpi->check_timings(in, timings);
 183}
 184
 185static struct omap_dss_driver sharp_ls_ops = {
 186        .connect        = sharp_ls_connect,
 187        .disconnect     = sharp_ls_disconnect,
 188
 189        .enable         = sharp_ls_enable,
 190        .disable        = sharp_ls_disable,
 191
 192        .set_timings    = sharp_ls_set_timings,
 193        .get_timings    = sharp_ls_get_timings,
 194        .check_timings  = sharp_ls_check_timings,
 195
 196        .get_resolution = omapdss_default_get_resolution,
 197};
 198
 199static  int sharp_ls_get_gpio_of(struct device *dev, int index, int val,
 200        const char *desc, struct gpio_desc **gpiod)
 201{
 202        struct gpio_desc *gd;
 203
 204        *gpiod = NULL;
 205
 206        gd = devm_gpiod_get_index(dev, desc, index, GPIOD_OUT_LOW);
 207        if (IS_ERR(gd))
 208                return PTR_ERR(gd);
 209
 210        *gpiod = gd;
 211        return 0;
 212}
 213
 214static int sharp_ls_probe_of(struct platform_device *pdev)
 215{
 216        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 217        struct device_node *node = pdev->dev.of_node;
 218        struct omap_dss_device *in;
 219        int r;
 220
 221        ddata->vcc = devm_regulator_get(&pdev->dev, "envdd");
 222        if (IS_ERR(ddata->vcc)) {
 223                dev_err(&pdev->dev, "failed to get regulator\n");
 224                return PTR_ERR(ddata->vcc);
 225        }
 226
 227        /* lcd INI */
 228        r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "enable", &ddata->ini_gpio);
 229        if (r)
 230                return r;
 231
 232        /* lcd RESB */
 233        r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "reset", &ddata->resb_gpio);
 234        if (r)
 235                return r;
 236
 237        /* lcd MO */
 238        r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "mode", &ddata->mo_gpio);
 239        if (r)
 240                return r;
 241
 242        /* lcd LR */
 243        r = sharp_ls_get_gpio_of(&pdev->dev, 1, 1, "mode", &ddata->lr_gpio);
 244        if (r)
 245                return r;
 246
 247        /* lcd UD */
 248        r = sharp_ls_get_gpio_of(&pdev->dev, 2, 1, "mode", &ddata->ud_gpio);
 249        if (r)
 250                return r;
 251
 252        in = omapdss_of_find_source_for_first_ep(node);
 253        if (IS_ERR(in)) {
 254                dev_err(&pdev->dev, "failed to find video source\n");
 255                return PTR_ERR(in);
 256        }
 257
 258        ddata->in = in;
 259
 260        return 0;
 261}
 262
 263static int sharp_ls_probe(struct platform_device *pdev)
 264{
 265        struct panel_drv_data *ddata;
 266        struct omap_dss_device *dssdev;
 267        int r;
 268
 269        if (!pdev->dev.of_node)
 270                return -ENODEV;
 271
 272        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
 273        if (ddata == NULL)
 274                return -ENOMEM;
 275
 276        platform_set_drvdata(pdev, ddata);
 277
 278        r = sharp_ls_probe_of(pdev);
 279        if (r)
 280                return r;
 281
 282        ddata->videomode = sharp_ls_timings;
 283
 284        dssdev = &ddata->dssdev;
 285        dssdev->dev = &pdev->dev;
 286        dssdev->driver = &sharp_ls_ops;
 287        dssdev->type = OMAP_DISPLAY_TYPE_DPI;
 288        dssdev->owner = THIS_MODULE;
 289        dssdev->panel.timings = ddata->videomode;
 290        dssdev->phy.dpi.data_lines = ddata->data_lines;
 291
 292        r = omapdss_register_display(dssdev);
 293        if (r) {
 294                dev_err(&pdev->dev, "Failed to register panel\n");
 295                goto err_reg;
 296        }
 297
 298        return 0;
 299
 300err_reg:
 301        omap_dss_put_device(ddata->in);
 302        return r;
 303}
 304
 305static int __exit sharp_ls_remove(struct platform_device *pdev)
 306{
 307        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 308        struct omap_dss_device *dssdev = &ddata->dssdev;
 309        struct omap_dss_device *in = ddata->in;
 310
 311        omapdss_unregister_display(dssdev);
 312
 313        sharp_ls_disable(dssdev);
 314        sharp_ls_disconnect(dssdev);
 315
 316        omap_dss_put_device(in);
 317
 318        return 0;
 319}
 320
 321static const struct of_device_id sharp_ls_of_match[] = {
 322        { .compatible = "omapdss,sharp,ls037v7dw01", },
 323        {},
 324};
 325
 326MODULE_DEVICE_TABLE(of, sharp_ls_of_match);
 327
 328static struct platform_driver sharp_ls_driver = {
 329        .probe = sharp_ls_probe,
 330        .remove = __exit_p(sharp_ls_remove),
 331        .driver = {
 332                .name = "panel-sharp-ls037v7dw01",
 333                .of_match_table = sharp_ls_of_match,
 334                .suppress_bind_attrs = true,
 335        },
 336};
 337
 338module_platform_driver(sharp_ls_driver);
 339
 340MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 341MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver");
 342MODULE_LICENSE("GPL");
 343