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        int r;
  63
  64        if (omapdss_device_is_connected(dssdev))
  65                return 0;
  66
  67        r = in->ops.dpi->connect(in, dssdev);
  68        if (r)
  69                return r;
  70
  71        return 0;
  72}
  73
  74static void sharp_ls_disconnect(struct omap_dss_device *dssdev)
  75{
  76        struct panel_drv_data *ddata = to_panel_data(dssdev);
  77        struct omap_dss_device *in = ddata->in;
  78
  79        if (!omapdss_device_is_connected(dssdev))
  80                return;
  81
  82        in->ops.dpi->disconnect(in, dssdev);
  83}
  84
  85static int sharp_ls_enable(struct omap_dss_device *dssdev)
  86{
  87        struct panel_drv_data *ddata = to_panel_data(dssdev);
  88        struct omap_dss_device *in = ddata->in;
  89        int r;
  90
  91        if (!omapdss_device_is_connected(dssdev))
  92                return -ENODEV;
  93
  94        if (omapdss_device_is_enabled(dssdev))
  95                return 0;
  96
  97        if (ddata->data_lines)
  98                in->ops.dpi->set_data_lines(in, ddata->data_lines);
  99        in->ops.dpi->set_timings(in, &ddata->videomode);
 100
 101        if (ddata->vcc) {
 102                r = regulator_enable(ddata->vcc);
 103                if (r != 0)
 104                        return r;
 105        }
 106
 107        r = in->ops.dpi->enable(in);
 108        if (r) {
 109                regulator_disable(ddata->vcc);
 110                return r;
 111        }
 112
 113        /* wait couple of vsyncs until enabling the LCD */
 114        msleep(50);
 115
 116        if (ddata->resb_gpio)
 117                gpiod_set_value_cansleep(ddata->resb_gpio, 1);
 118
 119        if (ddata->ini_gpio)
 120                gpiod_set_value_cansleep(ddata->ini_gpio, 1);
 121
 122        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 123
 124        return 0;
 125}
 126
 127static void sharp_ls_disable(struct omap_dss_device *dssdev)
 128{
 129        struct panel_drv_data *ddata = to_panel_data(dssdev);
 130        struct omap_dss_device *in = ddata->in;
 131
 132        if (!omapdss_device_is_enabled(dssdev))
 133                return;
 134
 135        if (ddata->ini_gpio)
 136                gpiod_set_value_cansleep(ddata->ini_gpio, 0);
 137
 138        if (ddata->resb_gpio)
 139                gpiod_set_value_cansleep(ddata->resb_gpio, 0);
 140
 141        /* wait at least 5 vsyncs after disabling the LCD */
 142
 143        msleep(100);
 144
 145        in->ops.dpi->disable(in);
 146
 147        if (ddata->vcc)
 148                regulator_disable(ddata->vcc);
 149
 150        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 151}
 152
 153static void sharp_ls_set_timings(struct omap_dss_device *dssdev,
 154                struct omap_video_timings *timings)
 155{
 156        struct panel_drv_data *ddata = to_panel_data(dssdev);
 157        struct omap_dss_device *in = ddata->in;
 158
 159        ddata->videomode = *timings;
 160        dssdev->panel.timings = *timings;
 161
 162        in->ops.dpi->set_timings(in, timings);
 163}
 164
 165static void sharp_ls_get_timings(struct omap_dss_device *dssdev,
 166                struct omap_video_timings *timings)
 167{
 168        struct panel_drv_data *ddata = to_panel_data(dssdev);
 169
 170        *timings = ddata->videomode;
 171}
 172
 173static int sharp_ls_check_timings(struct omap_dss_device *dssdev,
 174                struct omap_video_timings *timings)
 175{
 176        struct panel_drv_data *ddata = to_panel_data(dssdev);
 177        struct omap_dss_device *in = ddata->in;
 178
 179        return in->ops.dpi->check_timings(in, timings);
 180}
 181
 182static struct omap_dss_driver sharp_ls_ops = {
 183        .connect        = sharp_ls_connect,
 184        .disconnect     = sharp_ls_disconnect,
 185
 186        .enable         = sharp_ls_enable,
 187        .disable        = sharp_ls_disable,
 188
 189        .set_timings    = sharp_ls_set_timings,
 190        .get_timings    = sharp_ls_get_timings,
 191        .check_timings  = sharp_ls_check_timings,
 192
 193        .get_resolution = omapdss_default_get_resolution,
 194};
 195
 196static  int sharp_ls_get_gpio_of(struct device *dev, int index, int val,
 197        const char *desc, struct gpio_desc **gpiod)
 198{
 199        struct gpio_desc *gd;
 200
 201        *gpiod = NULL;
 202
 203        gd = devm_gpiod_get_index(dev, desc, index, GPIOD_OUT_LOW);
 204        if (IS_ERR(gd))
 205                return PTR_ERR(gd);
 206
 207        *gpiod = gd;
 208        return 0;
 209}
 210
 211static int sharp_ls_probe_of(struct platform_device *pdev)
 212{
 213        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 214        struct device_node *node = pdev->dev.of_node;
 215        struct omap_dss_device *in;
 216        int r;
 217
 218        ddata->vcc = devm_regulator_get(&pdev->dev, "envdd");
 219        if (IS_ERR(ddata->vcc)) {
 220                dev_err(&pdev->dev, "failed to get regulator\n");
 221                return PTR_ERR(ddata->vcc);
 222        }
 223
 224        /* lcd INI */
 225        r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "enable", &ddata->ini_gpio);
 226        if (r)
 227                return r;
 228
 229        /* lcd RESB */
 230        r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "reset", &ddata->resb_gpio);
 231        if (r)
 232                return r;
 233
 234        /* lcd MO */
 235        r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "mode", &ddata->mo_gpio);
 236        if (r)
 237                return r;
 238
 239        /* lcd LR */
 240        r = sharp_ls_get_gpio_of(&pdev->dev, 1, 1, "mode", &ddata->lr_gpio);
 241        if (r)
 242                return r;
 243
 244        /* lcd UD */
 245        r = sharp_ls_get_gpio_of(&pdev->dev, 2, 1, "mode", &ddata->ud_gpio);
 246        if (r)
 247                return r;
 248
 249        in = omapdss_of_find_source_for_first_ep(node);
 250        if (IS_ERR(in)) {
 251                dev_err(&pdev->dev, "failed to find video source\n");
 252                return PTR_ERR(in);
 253        }
 254
 255        ddata->in = in;
 256
 257        return 0;
 258}
 259
 260static int sharp_ls_probe(struct platform_device *pdev)
 261{
 262        struct panel_drv_data *ddata;
 263        struct omap_dss_device *dssdev;
 264        int r;
 265
 266        if (!pdev->dev.of_node)
 267                return -ENODEV;
 268
 269        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
 270        if (ddata == NULL)
 271                return -ENOMEM;
 272
 273        platform_set_drvdata(pdev, ddata);
 274
 275        r = sharp_ls_probe_of(pdev);
 276        if (r)
 277                return r;
 278
 279        ddata->videomode = sharp_ls_timings;
 280
 281        dssdev = &ddata->dssdev;
 282        dssdev->dev = &pdev->dev;
 283        dssdev->driver = &sharp_ls_ops;
 284        dssdev->type = OMAP_DISPLAY_TYPE_DPI;
 285        dssdev->owner = THIS_MODULE;
 286        dssdev->panel.timings = ddata->videomode;
 287        dssdev->phy.dpi.data_lines = ddata->data_lines;
 288
 289        r = omapdss_register_display(dssdev);
 290        if (r) {
 291                dev_err(&pdev->dev, "Failed to register panel\n");
 292                goto err_reg;
 293        }
 294
 295        return 0;
 296
 297err_reg:
 298        omap_dss_put_device(ddata->in);
 299        return r;
 300}
 301
 302static int __exit sharp_ls_remove(struct platform_device *pdev)
 303{
 304        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 305        struct omap_dss_device *dssdev = &ddata->dssdev;
 306        struct omap_dss_device *in = ddata->in;
 307
 308        omapdss_unregister_display(dssdev);
 309
 310        sharp_ls_disable(dssdev);
 311        sharp_ls_disconnect(dssdev);
 312
 313        omap_dss_put_device(in);
 314
 315        return 0;
 316}
 317
 318static const struct of_device_id sharp_ls_of_match[] = {
 319        { .compatible = "omapdss,sharp,ls037v7dw01", },
 320        {},
 321};
 322
 323MODULE_DEVICE_TABLE(of, sharp_ls_of_match);
 324
 325static struct platform_driver sharp_ls_driver = {
 326        .probe = sharp_ls_probe,
 327        .remove = __exit_p(sharp_ls_remove),
 328        .driver = {
 329                .name = "panel-sharp-ls037v7dw01",
 330                .of_match_table = sharp_ls_of_match,
 331                .suppress_bind_attrs = true,
 332        },
 333};
 334
 335module_platform_driver(sharp_ls_driver);
 336
 337MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 338MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver");
 339MODULE_LICENSE("GPL");
 340