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/omapdss.h> 20#include <video/omap-panel-data.h> 21#include <video/of_display_timing.h> 22 23struct 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 39static 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 55static 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 66static 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 if (ddata->enable_gpio) 87 gpiod_set_value_cansleep(ddata->enable_gpio, 1); 88 89 if (gpio_is_valid(ddata->backlight_gpio)) 90 gpio_set_value_cansleep(ddata->backlight_gpio, 1); 91 92 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 93 94 return 0; 95} 96 97static void panel_dpi_disable(struct omap_dss_device *dssdev) 98{ 99 struct panel_drv_data *ddata = to_panel_data(dssdev); 100 struct omap_dss_device *in = ddata->in; 101 102 if (!omapdss_device_is_enabled(dssdev)) 103 return; 104 105 if (ddata->enable_gpio) 106 gpiod_set_value_cansleep(ddata->enable_gpio, 0); 107 108 if (gpio_is_valid(ddata->backlight_gpio)) 109 gpio_set_value_cansleep(ddata->backlight_gpio, 0); 110 111 in->ops.dpi->disable(in); 112 113 dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 114} 115 116static void panel_dpi_set_timings(struct omap_dss_device *dssdev, 117 struct omap_video_timings *timings) 118{ 119 struct panel_drv_data *ddata = to_panel_data(dssdev); 120 struct omap_dss_device *in = ddata->in; 121 122 ddata->videomode = *timings; 123 dssdev->panel.timings = *timings; 124 125 in->ops.dpi->set_timings(in, timings); 126} 127 128static void panel_dpi_get_timings(struct omap_dss_device *dssdev, 129 struct omap_video_timings *timings) 130{ 131 struct panel_drv_data *ddata = to_panel_data(dssdev); 132 133 *timings = ddata->videomode; 134} 135 136static int panel_dpi_check_timings(struct omap_dss_device *dssdev, 137 struct omap_video_timings *timings) 138{ 139 struct panel_drv_data *ddata = to_panel_data(dssdev); 140 struct omap_dss_device *in = ddata->in; 141 142 return in->ops.dpi->check_timings(in, timings); 143} 144 145static struct omap_dss_driver panel_dpi_ops = { 146 .connect = panel_dpi_connect, 147 .disconnect = panel_dpi_disconnect, 148 149 .enable = panel_dpi_enable, 150 .disable = panel_dpi_disable, 151 152 .set_timings = panel_dpi_set_timings, 153 .get_timings = panel_dpi_get_timings, 154 .check_timings = panel_dpi_check_timings, 155 156 .get_resolution = omapdss_default_get_resolution, 157}; 158 159static int panel_dpi_probe_pdata(struct platform_device *pdev) 160{ 161 const struct panel_dpi_platform_data *pdata; 162 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 163 struct omap_dss_device *dssdev, *in; 164 struct videomode vm; 165 int r; 166 167 pdata = dev_get_platdata(&pdev->dev); 168 169 in = omap_dss_find_output(pdata->source); 170 if (in == NULL) { 171 dev_err(&pdev->dev, "failed to find video source '%s'\n", 172 pdata->source); 173 return -EPROBE_DEFER; 174 } 175 176 ddata->in = in; 177 178 ddata->data_lines = pdata->data_lines; 179 180 videomode_from_timing(pdata->display_timing, &vm); 181 videomode_to_omap_video_timings(&vm, &ddata->videomode); 182 183 dssdev = &ddata->dssdev; 184 dssdev->name = pdata->name; 185 186 r = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio, 187 GPIOF_OUT_INIT_LOW, "panel enable"); 188 if (r) 189 goto err_gpio; 190 191 ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio); 192 193 ddata->backlight_gpio = pdata->backlight_gpio; 194 195 return 0; 196 197err_gpio: 198 omap_dss_put_device(ddata->in); 199 return r; 200} 201 202static int panel_dpi_probe_of(struct platform_device *pdev) 203{ 204 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 205 struct device_node *node = pdev->dev.of_node; 206 struct omap_dss_device *in; 207 int r; 208 struct display_timing timing; 209 struct videomode vm; 210 struct gpio_desc *gpio; 211 212 gpio = devm_gpiod_get(&pdev->dev, "enable"); 213 214 if (IS_ERR(gpio)) { 215 if (PTR_ERR(gpio) != -ENOENT) 216 return PTR_ERR(gpio); 217 else 218 gpio = NULL; 219 } else { 220 gpiod_direction_output(gpio, 0); 221 } 222 223 ddata->enable_gpio = gpio; 224 225 ddata->backlight_gpio = -ENOENT; 226 227 r = of_get_display_timing(node, "panel-timing", &timing); 228 if (r) { 229 dev_err(&pdev->dev, "failed to get video timing\n"); 230 return r; 231 } 232 233 videomode_from_timing(&timing, &vm); 234 videomode_to_omap_video_timings(&vm, &ddata->videomode); 235 236 in = omapdss_of_find_source_for_first_ep(node); 237 if (IS_ERR(in)) { 238 dev_err(&pdev->dev, "failed to find video source\n"); 239 return PTR_ERR(in); 240 } 241 242 ddata->in = in; 243 244 return 0; 245} 246 247static int panel_dpi_probe(struct platform_device *pdev) 248{ 249 struct panel_drv_data *ddata; 250 struct omap_dss_device *dssdev; 251 int r; 252 253 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 254 if (ddata == NULL) 255 return -ENOMEM; 256 257 platform_set_drvdata(pdev, ddata); 258 259 if (dev_get_platdata(&pdev->dev)) { 260 r = panel_dpi_probe_pdata(pdev); 261 if (r) 262 return r; 263 } else if (pdev->dev.of_node) { 264 r = panel_dpi_probe_of(pdev); 265 if (r) 266 return r; 267 } else { 268 return -ENODEV; 269 } 270 271 if (gpio_is_valid(ddata->backlight_gpio)) { 272 r = devm_gpio_request_one(&pdev->dev, ddata->backlight_gpio, 273 GPIOF_OUT_INIT_LOW, "panel backlight"); 274 if (r) 275 goto err_gpio; 276 } 277 278 dssdev = &ddata->dssdev; 279 dssdev->dev = &pdev->dev; 280 dssdev->driver = &panel_dpi_ops; 281 dssdev->type = OMAP_DISPLAY_TYPE_DPI; 282 dssdev->owner = THIS_MODULE; 283 dssdev->panel.timings = ddata->videomode; 284 dssdev->phy.dpi.data_lines = ddata->data_lines; 285 286 r = omapdss_register_display(dssdev); 287 if (r) { 288 dev_err(&pdev->dev, "Failed to register panel\n"); 289 goto err_reg; 290 } 291 292 return 0; 293 294err_reg: 295err_gpio: 296 omap_dss_put_device(ddata->in); 297 return r; 298} 299 300static int __exit panel_dpi_remove(struct platform_device *pdev) 301{ 302 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 303 struct omap_dss_device *dssdev = &ddata->dssdev; 304 struct omap_dss_device *in = ddata->in; 305 306 omapdss_unregister_display(dssdev); 307 308 panel_dpi_disable(dssdev); 309 panel_dpi_disconnect(dssdev); 310 311 omap_dss_put_device(in); 312 313 return 0; 314} 315 316static const struct of_device_id panel_dpi_of_match[] = { 317 { .compatible = "omapdss,panel-dpi", }, 318 {}, 319}; 320 321MODULE_DEVICE_TABLE(of, panel_dpi_of_match); 322 323static struct platform_driver panel_dpi_driver = { 324 .probe = panel_dpi_probe, 325 .remove = __exit_p(panel_dpi_remove), 326 .driver = { 327 .name = "panel-dpi", 328 .of_match_table = panel_dpi_of_match, 329 .suppress_bind_attrs = true, 330 }, 331}; 332 333module_platform_driver(panel_dpi_driver); 334 335MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 336MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver"); 337MODULE_LICENSE("GPL"); 338