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/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 18 struct 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 34 static 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 57 static 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 68 static 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 79 static 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 121 static 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 147 static 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 159 static 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 167 static 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 176 static 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 190 static 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 205 static 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 253 static 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 290 err_reg: 291 omap_dss_put_device(ddata->in); 292 return r; 293 } 294 295 static int __exit 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 return 0; 309 } 310 311 static const struct of_device_id sharp_ls_of_match[] = { 312 { .compatible = "omapdss,sharp,ls037v7dw01", }, 313 {}, 314 }; 315 316 MODULE_DEVICE_TABLE(of, sharp_ls_of_match); 317 318 static struct platform_driver sharp_ls_driver = { 319 .probe = sharp_ls_probe, 320 .remove = __exit_p(sharp_ls_remove), 321 .driver = { 322 .name = "panel-sharp-ls037v7dw01", 323 .of_match_table = sharp_ls_of_match, 324 .suppress_bind_attrs = true, 325 }, 326 }; 327 328 module_platform_driver(sharp_ls_driver); 329 330 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 331 MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver"); 332 MODULE_LICENSE("GPL"); 333