Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Sharp LS037V7DW01 LCD Panel Driver
0004  *
0005  * Copyright (C) 2019 Texas Instruments Incorporated
0006  *
0007  * Based on the omapdrm-specific panel-sharp-ls037v7dw01 driver
0008  *
0009  * Copyright (C) 2013 Texas Instruments Incorporated
0010  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
0011  */
0012 
0013 #include <linux/delay.h>
0014 #include <linux/gpio/consumer.h>
0015 #include <linux/module.h>
0016 #include <linux/of.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/regulator/consumer.h>
0019 
0020 #include <drm/drm_connector.h>
0021 #include <drm/drm_modes.h>
0022 #include <drm/drm_panel.h>
0023 
0024 struct ls037v7dw01_panel {
0025     struct drm_panel panel;
0026     struct platform_device *pdev;
0027 
0028     struct regulator *vdd;
0029     struct gpio_desc *resb_gpio;    /* low = reset active min 20 us */
0030     struct gpio_desc *ini_gpio; /* high = power on */
0031     struct gpio_desc *mo_gpio;  /* low = 480x640, high = 240x320 */
0032     struct gpio_desc *lr_gpio;  /* high = conventional horizontal scanning */
0033     struct gpio_desc *ud_gpio;  /* high = conventional vertical scanning */
0034 };
0035 
0036 #define to_ls037v7dw01_device(p) \
0037     container_of(p, struct ls037v7dw01_panel, panel)
0038 
0039 static int ls037v7dw01_disable(struct drm_panel *panel)
0040 {
0041     struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
0042 
0043     gpiod_set_value_cansleep(lcd->ini_gpio, 0);
0044     gpiod_set_value_cansleep(lcd->resb_gpio, 0);
0045 
0046     /* Wait at least 5 vsyncs after disabling the LCD. */
0047     msleep(100);
0048 
0049     return 0;
0050 }
0051 
0052 static int ls037v7dw01_unprepare(struct drm_panel *panel)
0053 {
0054     struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
0055 
0056     regulator_disable(lcd->vdd);
0057     return 0;
0058 }
0059 
0060 static int ls037v7dw01_prepare(struct drm_panel *panel)
0061 {
0062     struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
0063     int ret;
0064 
0065     ret = regulator_enable(lcd->vdd);
0066     if (ret < 0)
0067         dev_err(&lcd->pdev->dev, "%s: failed to enable regulator\n",
0068             __func__);
0069 
0070     return ret;
0071 }
0072 
0073 static int ls037v7dw01_enable(struct drm_panel *panel)
0074 {
0075     struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
0076 
0077     /* Wait couple of vsyncs before enabling the LCD. */
0078     msleep(50);
0079 
0080     gpiod_set_value_cansleep(lcd->resb_gpio, 1);
0081     gpiod_set_value_cansleep(lcd->ini_gpio, 1);
0082 
0083     return 0;
0084 }
0085 
0086 static const struct drm_display_mode ls037v7dw01_mode = {
0087     .clock = 19200,
0088     .hdisplay = 480,
0089     .hsync_start = 480 + 1,
0090     .hsync_end = 480 + 1 + 2,
0091     .htotal = 480 + 1 + 2 + 28,
0092     .vdisplay = 640,
0093     .vsync_start = 640 + 1,
0094     .vsync_end = 640 + 1 + 1,
0095     .vtotal = 640 + 1 + 1 + 1,
0096     .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
0097     .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
0098     .width_mm = 56,
0099     .height_mm = 75,
0100 };
0101 
0102 static int ls037v7dw01_get_modes(struct drm_panel *panel,
0103                  struct drm_connector *connector)
0104 {
0105     struct drm_display_mode *mode;
0106 
0107     mode = drm_mode_duplicate(connector->dev, &ls037v7dw01_mode);
0108     if (!mode)
0109         return -ENOMEM;
0110 
0111     drm_mode_set_name(mode);
0112     drm_mode_probed_add(connector, mode);
0113 
0114     connector->display_info.width_mm = ls037v7dw01_mode.width_mm;
0115     connector->display_info.height_mm = ls037v7dw01_mode.height_mm;
0116     /*
0117      * FIXME: According to the datasheet pixel data is sampled on the
0118      * rising edge of the clock, but the code running on the SDP3430
0119      * indicates sampling on the negative edge. This should be tested on a
0120      * real device.
0121      */
0122     connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
0123                       | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE
0124                       | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
0125 
0126     return 1;
0127 }
0128 
0129 static const struct drm_panel_funcs ls037v7dw01_funcs = {
0130     .disable = ls037v7dw01_disable,
0131     .unprepare = ls037v7dw01_unprepare,
0132     .prepare = ls037v7dw01_prepare,
0133     .enable = ls037v7dw01_enable,
0134     .get_modes = ls037v7dw01_get_modes,
0135 };
0136 
0137 static int ls037v7dw01_probe(struct platform_device *pdev)
0138 {
0139     struct ls037v7dw01_panel *lcd;
0140 
0141     lcd = devm_kzalloc(&pdev->dev, sizeof(*lcd), GFP_KERNEL);
0142     if (!lcd)
0143         return -ENOMEM;
0144 
0145     platform_set_drvdata(pdev, lcd);
0146     lcd->pdev = pdev;
0147 
0148     lcd->vdd = devm_regulator_get(&pdev->dev, "envdd");
0149     if (IS_ERR(lcd->vdd))
0150         return dev_err_probe(&pdev->dev, PTR_ERR(lcd->vdd),
0151                      "failed to get regulator\n");
0152 
0153     lcd->ini_gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW);
0154     if (IS_ERR(lcd->ini_gpio))
0155         return dev_err_probe(&pdev->dev, PTR_ERR(lcd->ini_gpio),
0156                      "failed to get enable gpio\n");
0157 
0158     lcd->resb_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
0159     if (IS_ERR(lcd->resb_gpio))
0160         return dev_err_probe(&pdev->dev, PTR_ERR(lcd->resb_gpio),
0161                      "failed to get reset gpio\n");
0162 
0163     lcd->mo_gpio = devm_gpiod_get_index(&pdev->dev, "mode", 0,
0164                         GPIOD_OUT_LOW);
0165     if (IS_ERR(lcd->mo_gpio)) {
0166         dev_err(&pdev->dev, "failed to get mode[0] gpio\n");
0167         return PTR_ERR(lcd->mo_gpio);
0168     }
0169 
0170     lcd->lr_gpio = devm_gpiod_get_index(&pdev->dev, "mode", 1,
0171                         GPIOD_OUT_LOW);
0172     if (IS_ERR(lcd->lr_gpio)) {
0173         dev_err(&pdev->dev, "failed to get mode[1] gpio\n");
0174         return PTR_ERR(lcd->lr_gpio);
0175     }
0176 
0177     lcd->ud_gpio = devm_gpiod_get_index(&pdev->dev, "mode", 2,
0178                         GPIOD_OUT_LOW);
0179     if (IS_ERR(lcd->ud_gpio)) {
0180         dev_err(&pdev->dev, "failed to get mode[2] gpio\n");
0181         return PTR_ERR(lcd->ud_gpio);
0182     }
0183 
0184     drm_panel_init(&lcd->panel, &pdev->dev, &ls037v7dw01_funcs,
0185                DRM_MODE_CONNECTOR_DPI);
0186 
0187     drm_panel_add(&lcd->panel);
0188 
0189     return 0;
0190 }
0191 
0192 static int ls037v7dw01_remove(struct platform_device *pdev)
0193 {
0194     struct ls037v7dw01_panel *lcd = platform_get_drvdata(pdev);
0195 
0196     drm_panel_remove(&lcd->panel);
0197     drm_panel_disable(&lcd->panel);
0198     drm_panel_unprepare(&lcd->panel);
0199 
0200     return 0;
0201 }
0202 
0203 static const struct of_device_id ls037v7dw01_of_match[] = {
0204     { .compatible = "sharp,ls037v7dw01", },
0205     { /* sentinel */ },
0206 };
0207 
0208 MODULE_DEVICE_TABLE(of, ls037v7dw01_of_match);
0209 
0210 static struct platform_driver ls037v7dw01_driver = {
0211     .probe      = ls037v7dw01_probe,
0212     .remove     = ls037v7dw01_remove,
0213     .driver     = {
0214         .name = "panel-sharp-ls037v7dw01",
0215         .of_match_table = ls037v7dw01_of_match,
0216     },
0217 };
0218 
0219 module_platform_driver(ls037v7dw01_driver);
0220 
0221 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
0222 MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver");
0223 MODULE_LICENSE("GPL");