linux/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * LG.Philips LB035Q02 LCD Panel driver
   4 *
   5 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
   6 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
   7 * Based on a driver by: Steve Sakoman <steve@sakoman.com>
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/delay.h>
  12#include <linux/spi/spi.h>
  13#include <linux/mutex.h>
  14#include <linux/gpio.h>
  15#include <linux/gpio/consumer.h>
  16
  17#include "../dss/omapdss.h"
  18
  19static const struct videomode lb035q02_vm = {
  20        .hactive = 320,
  21        .vactive = 240,
  22
  23        .pixelclock     = 6500000,
  24
  25        .hsync_len      = 2,
  26        .hfront_porch   = 20,
  27        .hback_porch    = 68,
  28
  29        .vsync_len      = 2,
  30        .vfront_porch   = 4,
  31        .vback_porch    = 18,
  32
  33        .flags          = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
  34};
  35
  36struct panel_drv_data {
  37        struct omap_dss_device dssdev;
  38
  39        struct spi_device *spi;
  40
  41        struct videomode vm;
  42
  43        struct gpio_desc *enable_gpio;
  44};
  45
  46#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
  47
  48static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val)
  49{
  50        struct spi_message msg;
  51        struct spi_transfer index_xfer = {
  52                .len            = 3,
  53                .cs_change      = 1,
  54        };
  55        struct spi_transfer value_xfer = {
  56                .len            = 3,
  57        };
  58        u8      buffer[16];
  59
  60        spi_message_init(&msg);
  61
  62        /* register index */
  63        buffer[0] = 0x70;
  64        buffer[1] = 0x00;
  65        buffer[2] = reg & 0x7f;
  66        index_xfer.tx_buf = buffer;
  67        spi_message_add_tail(&index_xfer, &msg);
  68
  69        /* register value */
  70        buffer[4] = 0x72;
  71        buffer[5] = val >> 8;
  72        buffer[6] = val;
  73        value_xfer.tx_buf = buffer + 4;
  74        spi_message_add_tail(&value_xfer, &msg);
  75
  76        return spi_sync(spi, &msg);
  77}
  78
  79static void init_lb035q02_panel(struct spi_device *spi)
  80{
  81        /* Init sequence from page 28 of the lb035q02 spec */
  82        lb035q02_write_reg(spi, 0x01, 0x6300);
  83        lb035q02_write_reg(spi, 0x02, 0x0200);
  84        lb035q02_write_reg(spi, 0x03, 0x0177);
  85        lb035q02_write_reg(spi, 0x04, 0x04c7);
  86        lb035q02_write_reg(spi, 0x05, 0xffc0);
  87        lb035q02_write_reg(spi, 0x06, 0xe806);
  88        lb035q02_write_reg(spi, 0x0a, 0x4008);
  89        lb035q02_write_reg(spi, 0x0b, 0x0000);
  90        lb035q02_write_reg(spi, 0x0d, 0x0030);
  91        lb035q02_write_reg(spi, 0x0e, 0x2800);
  92        lb035q02_write_reg(spi, 0x0f, 0x0000);
  93        lb035q02_write_reg(spi, 0x16, 0x9f80);
  94        lb035q02_write_reg(spi, 0x17, 0x0a0f);
  95        lb035q02_write_reg(spi, 0x1e, 0x00c1);
  96        lb035q02_write_reg(spi, 0x30, 0x0300);
  97        lb035q02_write_reg(spi, 0x31, 0x0007);
  98        lb035q02_write_reg(spi, 0x32, 0x0000);
  99        lb035q02_write_reg(spi, 0x33, 0x0000);
 100        lb035q02_write_reg(spi, 0x34, 0x0707);
 101        lb035q02_write_reg(spi, 0x35, 0x0004);
 102        lb035q02_write_reg(spi, 0x36, 0x0302);
 103        lb035q02_write_reg(spi, 0x37, 0x0202);
 104        lb035q02_write_reg(spi, 0x3a, 0x0a0d);
 105        lb035q02_write_reg(spi, 0x3b, 0x0806);
 106}
 107
 108static int lb035q02_connect(struct omap_dss_device *src,
 109                            struct omap_dss_device *dst)
 110{
 111        struct panel_drv_data *ddata = to_panel_data(dst);
 112
 113        init_lb035q02_panel(ddata->spi);
 114
 115        return 0;
 116}
 117
 118static void lb035q02_disconnect(struct omap_dss_device *src,
 119                                struct omap_dss_device *dst)
 120{
 121}
 122
 123static void lb035q02_enable(struct omap_dss_device *dssdev)
 124{
 125        struct panel_drv_data *ddata = to_panel_data(dssdev);
 126
 127        if (ddata->enable_gpio)
 128                gpiod_set_value_cansleep(ddata->enable_gpio, 1);
 129}
 130
 131static void lb035q02_disable(struct omap_dss_device *dssdev)
 132{
 133        struct panel_drv_data *ddata = to_panel_data(dssdev);
 134
 135        if (ddata->enable_gpio)
 136                gpiod_set_value_cansleep(ddata->enable_gpio, 0);
 137}
 138
 139static int lb035q02_get_modes(struct omap_dss_device *dssdev,
 140                              struct drm_connector *connector)
 141{
 142        struct panel_drv_data *ddata = to_panel_data(dssdev);
 143
 144        return omapdss_display_get_modes(connector, &ddata->vm);
 145}
 146
 147static const struct omap_dss_device_ops lb035q02_ops = {
 148        .connect        = lb035q02_connect,
 149        .disconnect     = lb035q02_disconnect,
 150
 151        .enable         = lb035q02_enable,
 152        .disable        = lb035q02_disable,
 153
 154        .get_modes      = lb035q02_get_modes,
 155};
 156
 157static int lb035q02_probe_of(struct spi_device *spi)
 158{
 159        struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
 160        struct gpio_desc *gpio;
 161
 162        gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
 163        if (IS_ERR(gpio)) {
 164                dev_err(&spi->dev, "failed to parse enable gpio\n");
 165                return PTR_ERR(gpio);
 166        }
 167
 168        ddata->enable_gpio = gpio;
 169
 170        return 0;
 171}
 172
 173static int lb035q02_panel_spi_probe(struct spi_device *spi)
 174{
 175        struct panel_drv_data *ddata;
 176        struct omap_dss_device *dssdev;
 177        int r;
 178
 179        ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
 180        if (ddata == NULL)
 181                return -ENOMEM;
 182
 183        dev_set_drvdata(&spi->dev, ddata);
 184
 185        ddata->spi = spi;
 186
 187        r = lb035q02_probe_of(spi);
 188        if (r)
 189                return r;
 190
 191        ddata->vm = lb035q02_vm;
 192
 193        dssdev = &ddata->dssdev;
 194        dssdev->dev = &spi->dev;
 195        dssdev->ops = &lb035q02_ops;
 196        dssdev->type = OMAP_DISPLAY_TYPE_DPI;
 197        dssdev->display = true;
 198        dssdev->owner = THIS_MODULE;
 199        dssdev->of_ports = BIT(0);
 200        dssdev->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
 201
 202        /*
 203         * Note: According to the panel documentation:
 204         * DE is active LOW
 205         * DATA needs to be driven on the FALLING edge
 206         */
 207        dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH
 208                          | DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE
 209                          | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
 210
 211        omapdss_display_init(dssdev);
 212        omapdss_device_register(dssdev);
 213
 214        return 0;
 215}
 216
 217static int lb035q02_panel_spi_remove(struct spi_device *spi)
 218{
 219        struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
 220        struct omap_dss_device *dssdev = &ddata->dssdev;
 221
 222        omapdss_device_unregister(dssdev);
 223
 224        lb035q02_disable(dssdev);
 225
 226        return 0;
 227}
 228
 229static const struct of_device_id lb035q02_of_match[] = {
 230        { .compatible = "omapdss,lgphilips,lb035q02", },
 231        {},
 232};
 233
 234MODULE_DEVICE_TABLE(of, lb035q02_of_match);
 235
 236static struct spi_driver lb035q02_spi_driver = {
 237        .probe          = lb035q02_panel_spi_probe,
 238        .remove         = lb035q02_panel_spi_remove,
 239        .driver         = {
 240                .name   = "panel_lgphilips_lb035q02",
 241                .of_match_table = lb035q02_of_match,
 242                .suppress_bind_attrs = true,
 243        },
 244};
 245
 246module_spi_driver(lb035q02_spi_driver);
 247
 248MODULE_ALIAS("spi:lgphilips,lb035q02");
 249MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 250MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
 251MODULE_LICENSE("GPL");
 252