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 19 struct 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 35 static 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 58 static 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 74 static 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 85 static 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 127 static 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 153 static 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 165 static 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 173 static 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 182 static 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 196 static 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 211 static 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 260 static 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 297 err_reg: 298 omap_dss_put_device(ddata->in); 299 return r; 300 } 301 302 static 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 318 static const struct of_device_id sharp_ls_of_match[] = { 319 { .compatible = "omapdss,sharp,ls037v7dw01", }, 320 {}, 321 }; 322 323 MODULE_DEVICE_TABLE(of, sharp_ls_of_match); 324 325 static 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 335 module_platform_driver(sharp_ls_driver); 336 337 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 338 MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver"); 339 MODULE_LICENSE("GPL"); 340