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