1 /* 2 * Generic MIPI DPI Panel Driver 3 * 4 * Copyright (C) 2013 Texas Instruments 5 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 as published by 9 * the Free Software Foundation. 10 */ 11 12 #include <linux/gpio.h> 13 #include <linux/module.h> 14 #include <linux/platform_device.h> 15 #include <linux/slab.h> 16 #include <linux/of.h> 17 #include <linux/of_gpio.h> 18 19 #include <video/omapfb_dss.h> 20 #include <video/omap-panel-data.h> 21 #include <video/of_display_timing.h> 22 23 struct panel_drv_data { 24 struct omap_dss_device dssdev; 25 struct omap_dss_device *in; 26 27 int data_lines; 28 29 struct omap_video_timings videomode; 30 31 /* used for non-DT boot, to be removed */ 32 int backlight_gpio; 33 34 struct gpio_desc *enable_gpio; 35 }; 36 37 #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) 38 39 static int panel_dpi_connect(struct omap_dss_device *dssdev) 40 { 41 struct panel_drv_data *ddata = to_panel_data(dssdev); 42 struct omap_dss_device *in = ddata->in; 43 int r; 44 45 if (omapdss_device_is_connected(dssdev)) 46 return 0; 47 48 r = in->ops.dpi->connect(in, dssdev); 49 if (r) 50 return r; 51 52 return 0; 53 } 54 55 static void panel_dpi_disconnect(struct omap_dss_device *dssdev) 56 { 57 struct panel_drv_data *ddata = to_panel_data(dssdev); 58 struct omap_dss_device *in = ddata->in; 59 60 if (!omapdss_device_is_connected(dssdev)) 61 return; 62 63 in->ops.dpi->disconnect(in, dssdev); 64 } 65 66 static int panel_dpi_enable(struct omap_dss_device *dssdev) 67 { 68 struct panel_drv_data *ddata = to_panel_data(dssdev); 69 struct omap_dss_device *in = ddata->in; 70 int r; 71 72 if (!omapdss_device_is_connected(dssdev)) 73 return -ENODEV; 74 75 if (omapdss_device_is_enabled(dssdev)) 76 return 0; 77 78 if (ddata->data_lines) 79 in->ops.dpi->set_data_lines(in, ddata->data_lines); 80 in->ops.dpi->set_timings(in, &ddata->videomode); 81 82 r = in->ops.dpi->enable(in); 83 if (r) 84 return r; 85 86 gpiod_set_value_cansleep(ddata->enable_gpio, 1); 87 88 if (gpio_is_valid(ddata->backlight_gpio)) 89 gpio_set_value_cansleep(ddata->backlight_gpio, 1); 90 91 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 92 93 return 0; 94 } 95 96 static void panel_dpi_disable(struct omap_dss_device *dssdev) 97 { 98 struct panel_drv_data *ddata = to_panel_data(dssdev); 99 struct omap_dss_device *in = ddata->in; 100 101 if (!omapdss_device_is_enabled(dssdev)) 102 return; 103 104 if (gpio_is_valid(ddata->backlight_gpio)) 105 gpio_set_value_cansleep(ddata->backlight_gpio, 0); 106 107 gpiod_set_value_cansleep(ddata->enable_gpio, 0); 108 109 in->ops.dpi->disable(in); 110 111 dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 112 } 113 114 static void panel_dpi_set_timings(struct omap_dss_device *dssdev, 115 struct omap_video_timings *timings) 116 { 117 struct panel_drv_data *ddata = to_panel_data(dssdev); 118 struct omap_dss_device *in = ddata->in; 119 120 ddata->videomode = *timings; 121 dssdev->panel.timings = *timings; 122 123 in->ops.dpi->set_timings(in, timings); 124 } 125 126 static void panel_dpi_get_timings(struct omap_dss_device *dssdev, 127 struct omap_video_timings *timings) 128 { 129 struct panel_drv_data *ddata = to_panel_data(dssdev); 130 131 *timings = ddata->videomode; 132 } 133 134 static int panel_dpi_check_timings(struct omap_dss_device *dssdev, 135 struct omap_video_timings *timings) 136 { 137 struct panel_drv_data *ddata = to_panel_data(dssdev); 138 struct omap_dss_device *in = ddata->in; 139 140 return in->ops.dpi->check_timings(in, timings); 141 } 142 143 static struct omap_dss_driver panel_dpi_ops = { 144 .connect = panel_dpi_connect, 145 .disconnect = panel_dpi_disconnect, 146 147 .enable = panel_dpi_enable, 148 .disable = panel_dpi_disable, 149 150 .set_timings = panel_dpi_set_timings, 151 .get_timings = panel_dpi_get_timings, 152 .check_timings = panel_dpi_check_timings, 153 154 .get_resolution = omapdss_default_get_resolution, 155 }; 156 157 static int panel_dpi_probe_pdata(struct platform_device *pdev) 158 { 159 const struct panel_dpi_platform_data *pdata; 160 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 161 struct omap_dss_device *dssdev, *in; 162 struct videomode vm; 163 int r; 164 165 pdata = dev_get_platdata(&pdev->dev); 166 167 in = omap_dss_find_output(pdata->source); 168 if (in == NULL) { 169 dev_err(&pdev->dev, "failed to find video source '%s'\n", 170 pdata->source); 171 return -EPROBE_DEFER; 172 } 173 174 ddata->in = in; 175 176 ddata->data_lines = pdata->data_lines; 177 178 videomode_from_timing(pdata->display_timing, &vm); 179 videomode_to_omap_video_timings(&vm, &ddata->videomode); 180 181 dssdev = &ddata->dssdev; 182 dssdev->name = pdata->name; 183 184 r = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio, 185 GPIOF_OUT_INIT_LOW, "panel enable"); 186 if (r) 187 goto err_gpio; 188 189 ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio); 190 191 ddata->backlight_gpio = pdata->backlight_gpio; 192 193 return 0; 194 195 err_gpio: 196 omap_dss_put_device(ddata->in); 197 return r; 198 } 199 200 static int panel_dpi_probe_of(struct platform_device *pdev) 201 { 202 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 203 struct device_node *node = pdev->dev.of_node; 204 struct omap_dss_device *in; 205 int r; 206 struct display_timing timing; 207 struct videomode vm; 208 struct gpio_desc *gpio; 209 210 gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW); 211 if (IS_ERR(gpio)) 212 return PTR_ERR(gpio); 213 214 ddata->enable_gpio = gpio; 215 216 ddata->backlight_gpio = -ENOENT; 217 218 r = of_get_display_timing(node, "panel-timing", &timing); 219 if (r) { 220 dev_err(&pdev->dev, "failed to get video timing\n"); 221 return r; 222 } 223 224 videomode_from_timing(&timing, &vm); 225 videomode_to_omap_video_timings(&vm, &ddata->videomode); 226 227 in = omapdss_of_find_source_for_first_ep(node); 228 if (IS_ERR(in)) { 229 dev_err(&pdev->dev, "failed to find video source\n"); 230 return PTR_ERR(in); 231 } 232 233 ddata->in = in; 234 235 return 0; 236 } 237 238 static int panel_dpi_probe(struct platform_device *pdev) 239 { 240 struct panel_drv_data *ddata; 241 struct omap_dss_device *dssdev; 242 int r; 243 244 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 245 if (ddata == NULL) 246 return -ENOMEM; 247 248 platform_set_drvdata(pdev, ddata); 249 250 if (dev_get_platdata(&pdev->dev)) { 251 r = panel_dpi_probe_pdata(pdev); 252 if (r) 253 return r; 254 } else if (pdev->dev.of_node) { 255 r = panel_dpi_probe_of(pdev); 256 if (r) 257 return r; 258 } else { 259 return -ENODEV; 260 } 261 262 if (gpio_is_valid(ddata->backlight_gpio)) { 263 r = devm_gpio_request_one(&pdev->dev, ddata->backlight_gpio, 264 GPIOF_OUT_INIT_LOW, "panel backlight"); 265 if (r) 266 goto err_gpio; 267 } 268 269 dssdev = &ddata->dssdev; 270 dssdev->dev = &pdev->dev; 271 dssdev->driver = &panel_dpi_ops; 272 dssdev->type = OMAP_DISPLAY_TYPE_DPI; 273 dssdev->owner = THIS_MODULE; 274 dssdev->panel.timings = ddata->videomode; 275 dssdev->phy.dpi.data_lines = ddata->data_lines; 276 277 r = omapdss_register_display(dssdev); 278 if (r) { 279 dev_err(&pdev->dev, "Failed to register panel\n"); 280 goto err_reg; 281 } 282 283 return 0; 284 285 err_reg: 286 err_gpio: 287 omap_dss_put_device(ddata->in); 288 return r; 289 } 290 291 static int __exit panel_dpi_remove(struct platform_device *pdev) 292 { 293 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 294 struct omap_dss_device *dssdev = &ddata->dssdev; 295 struct omap_dss_device *in = ddata->in; 296 297 omapdss_unregister_display(dssdev); 298 299 panel_dpi_disable(dssdev); 300 panel_dpi_disconnect(dssdev); 301 302 omap_dss_put_device(in); 303 304 return 0; 305 } 306 307 static const struct of_device_id panel_dpi_of_match[] = { 308 { .compatible = "omapdss,panel-dpi", }, 309 {}, 310 }; 311 312 MODULE_DEVICE_TABLE(of, panel_dpi_of_match); 313 314 static struct platform_driver panel_dpi_driver = { 315 .probe = panel_dpi_probe, 316 .remove = __exit_p(panel_dpi_remove), 317 .driver = { 318 .name = "panel-dpi", 319 .of_match_table = panel_dpi_of_match, 320 .suppress_bind_attrs = true, 321 }, 322 }; 323 324 module_platform_driver(panel_dpi_driver); 325 326 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 327 MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver"); 328 MODULE_LICENSE("GPL"); 329