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 20 struct 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 36 static 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 47 static 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 58 static 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 88 static 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 106 static 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 118 static 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 126 static 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 135 static 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 149 static 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 187 err_gpio: 188 omap_dss_put_device(ddata->in); 189 return r; 190 } 191 192 static 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 230 static 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 277 err_reg: 278 err_gpio: 279 omap_dss_put_device(ddata->in); 280 return r; 281 } 282 283 static 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 299 static const struct of_device_id panel_dpi_of_match[] = { 300 { .compatible = "omapdss,panel-dpi", }, 301 {}, 302 }; 303 304 MODULE_DEVICE_TABLE(of, panel_dpi_of_match); 305 306 static 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 316 module_platform_driver(panel_dpi_driver); 317 318 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 319 MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver"); 320 MODULE_LICENSE("GPL"); 321