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