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