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/consumer.h> 10 #include <linux/module.h> 11 #include <linux/platform_device.h> 12 #include <linux/slab.h> 13 #include <linux/of.h> 14 15 #include <video/omapfb_dss.h> 16 #include <video/of_display_timing.h> 17 18 struct panel_drv_data { 19 struct omap_dss_device dssdev; 20 struct omap_dss_device *in; 21 22 int data_lines; 23 24 struct omap_video_timings videomode; 25 26 struct gpio_desc *enable_gpio; 27 }; 28 29 #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) 30 31 static int panel_dpi_connect(struct omap_dss_device *dssdev) 32 { 33 struct panel_drv_data *ddata = to_panel_data(dssdev); 34 struct omap_dss_device *in = ddata->in; 35 36 if (omapdss_device_is_connected(dssdev)) 37 return 0; 38 39 return in->ops.dpi->connect(in, dssdev); 40 } 41 42 static void panel_dpi_disconnect(struct omap_dss_device *dssdev) 43 { 44 struct panel_drv_data *ddata = to_panel_data(dssdev); 45 struct omap_dss_device *in = ddata->in; 46 47 if (!omapdss_device_is_connected(dssdev)) 48 return; 49 50 in->ops.dpi->disconnect(in, dssdev); 51 } 52 53 static int panel_dpi_enable(struct omap_dss_device *dssdev) 54 { 55 struct panel_drv_data *ddata = to_panel_data(dssdev); 56 struct omap_dss_device *in = ddata->in; 57 int r; 58 59 if (!omapdss_device_is_connected(dssdev)) 60 return -ENODEV; 61 62 if (omapdss_device_is_enabled(dssdev)) 63 return 0; 64 65 if (ddata->data_lines) 66 in->ops.dpi->set_data_lines(in, ddata->data_lines); 67 in->ops.dpi->set_timings(in, &ddata->videomode); 68 69 r = in->ops.dpi->enable(in); 70 if (r) 71 return r; 72 73 gpiod_set_value_cansleep(ddata->enable_gpio, 1); 74 75 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 76 77 return 0; 78 } 79 80 static void panel_dpi_disable(struct omap_dss_device *dssdev) 81 { 82 struct panel_drv_data *ddata = to_panel_data(dssdev); 83 struct omap_dss_device *in = ddata->in; 84 85 if (!omapdss_device_is_enabled(dssdev)) 86 return; 87 88 gpiod_set_value_cansleep(ddata->enable_gpio, 0); 89 90 in->ops.dpi->disable(in); 91 92 dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 93 } 94 95 static void panel_dpi_set_timings(struct omap_dss_device *dssdev, 96 struct omap_video_timings *timings) 97 { 98 struct panel_drv_data *ddata = to_panel_data(dssdev); 99 struct omap_dss_device *in = ddata->in; 100 101 ddata->videomode = *timings; 102 dssdev->panel.timings = *timings; 103 104 in->ops.dpi->set_timings(in, timings); 105 } 106 107 static void panel_dpi_get_timings(struct omap_dss_device *dssdev, 108 struct omap_video_timings *timings) 109 { 110 struct panel_drv_data *ddata = to_panel_data(dssdev); 111 112 *timings = ddata->videomode; 113 } 114 115 static int panel_dpi_check_timings(struct omap_dss_device *dssdev, 116 struct omap_video_timings *timings) 117 { 118 struct panel_drv_data *ddata = to_panel_data(dssdev); 119 struct omap_dss_device *in = ddata->in; 120 121 return in->ops.dpi->check_timings(in, timings); 122 } 123 124 static struct omap_dss_driver panel_dpi_ops = { 125 .connect = panel_dpi_connect, 126 .disconnect = panel_dpi_disconnect, 127 128 .enable = panel_dpi_enable, 129 .disable = panel_dpi_disable, 130 131 .set_timings = panel_dpi_set_timings, 132 .get_timings = panel_dpi_get_timings, 133 .check_timings = panel_dpi_check_timings, 134 135 .get_resolution = omapdss_default_get_resolution, 136 }; 137 138 static int panel_dpi_probe_of(struct platform_device *pdev) 139 { 140 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 141 struct device_node *node = pdev->dev.of_node; 142 struct omap_dss_device *in; 143 int r; 144 struct display_timing timing; 145 struct videomode vm; 146 struct gpio_desc *gpio; 147 148 gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW); 149 if (IS_ERR(gpio)) 150 return PTR_ERR(gpio); 151 152 ddata->enable_gpio = gpio; 153 154 r = of_get_display_timing(node, "panel-timing", &timing); 155 if (r) { 156 dev_err(&pdev->dev, "failed to get video timing\n"); 157 return r; 158 } 159 160 videomode_from_timing(&timing, &vm); 161 videomode_to_omap_video_timings(&vm, &ddata->videomode); 162 163 in = omapdss_of_find_source_for_first_ep(node); 164 if (IS_ERR(in)) { 165 dev_err(&pdev->dev, "failed to find video source\n"); 166 return PTR_ERR(in); 167 } 168 169 ddata->in = in; 170 171 return 0; 172 } 173 174 static int panel_dpi_probe(struct platform_device *pdev) 175 { 176 struct panel_drv_data *ddata; 177 struct omap_dss_device *dssdev; 178 int r; 179 180 if (!pdev->dev.of_node) 181 return -ENODEV; 182 183 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 184 if (ddata == NULL) 185 return -ENOMEM; 186 187 platform_set_drvdata(pdev, ddata); 188 189 r = panel_dpi_probe_of(pdev); 190 if (r) 191 return r; 192 193 dssdev = &ddata->dssdev; 194 dssdev->dev = &pdev->dev; 195 dssdev->driver = &panel_dpi_ops; 196 dssdev->type = OMAP_DISPLAY_TYPE_DPI; 197 dssdev->owner = THIS_MODULE; 198 dssdev->panel.timings = ddata->videomode; 199 dssdev->phy.dpi.data_lines = ddata->data_lines; 200 201 r = omapdss_register_display(dssdev); 202 if (r) { 203 dev_err(&pdev->dev, "Failed to register panel\n"); 204 goto err_reg; 205 } 206 207 return 0; 208 209 err_reg: 210 omap_dss_put_device(ddata->in); 211 return r; 212 } 213 214 static int __exit panel_dpi_remove(struct platform_device *pdev) 215 { 216 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 217 struct omap_dss_device *dssdev = &ddata->dssdev; 218 struct omap_dss_device *in = ddata->in; 219 220 omapdss_unregister_display(dssdev); 221 222 panel_dpi_disable(dssdev); 223 panel_dpi_disconnect(dssdev); 224 225 omap_dss_put_device(in); 226 227 return 0; 228 } 229 230 static const struct of_device_id panel_dpi_of_match[] = { 231 { .compatible = "omapdss,panel-dpi", }, 232 {}, 233 }; 234 235 MODULE_DEVICE_TABLE(of, panel_dpi_of_match); 236 237 static struct platform_driver panel_dpi_driver = { 238 .probe = panel_dpi_probe, 239 .remove = __exit_p(panel_dpi_remove), 240 .driver = { 241 .name = "panel-dpi", 242 .of_match_table = panel_dpi_of_match, 243 .suppress_bind_attrs = true, 244 }, 245 }; 246 247 module_platform_driver(panel_dpi_driver); 248 249 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 250 MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver"); 251 MODULE_LICENSE("GPL"); 252