1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * TFP410 DPI-to-DVI encoder 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_gpio.h> 14 15 #include <video/omapfb_dss.h> 16 17 struct panel_drv_data { 18 struct omap_dss_device dssdev; 19 struct omap_dss_device *in; 20 21 int pd_gpio; 22 int data_lines; 23 24 struct omap_video_timings timings; 25 }; 26 27 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) 28 29 static int tfp410_connect(struct omap_dss_device *dssdev, 30 struct omap_dss_device *dst) 31 { 32 struct panel_drv_data *ddata = to_panel_data(dssdev); 33 struct omap_dss_device *in = ddata->in; 34 int r; 35 36 if (omapdss_device_is_connected(dssdev)) 37 return -EBUSY; 38 39 r = in->ops.dpi->connect(in, dssdev); 40 if (r) 41 return r; 42 43 dst->src = dssdev; 44 dssdev->dst = dst; 45 46 return 0; 47 } 48 49 static void tfp410_disconnect(struct omap_dss_device *dssdev, 50 struct omap_dss_device *dst) 51 { 52 struct panel_drv_data *ddata = to_panel_data(dssdev); 53 struct omap_dss_device *in = ddata->in; 54 55 WARN_ON(!omapdss_device_is_connected(dssdev)); 56 if (!omapdss_device_is_connected(dssdev)) 57 return; 58 59 WARN_ON(dst != dssdev->dst); 60 if (dst != dssdev->dst) 61 return; 62 63 dst->src = NULL; 64 dssdev->dst = NULL; 65 66 in->ops.dpi->disconnect(in, &ddata->dssdev); 67 } 68 69 static int tfp410_enable(struct omap_dss_device *dssdev) 70 { 71 struct panel_drv_data *ddata = to_panel_data(dssdev); 72 struct omap_dss_device *in = ddata->in; 73 int r; 74 75 if (!omapdss_device_is_connected(dssdev)) 76 return -ENODEV; 77 78 if (omapdss_device_is_enabled(dssdev)) 79 return 0; 80 81 in->ops.dpi->set_timings(in, &ddata->timings); 82 if (ddata->data_lines) 83 in->ops.dpi->set_data_lines(in, ddata->data_lines); 84 85 r = in->ops.dpi->enable(in); 86 if (r) 87 return r; 88 89 if (gpio_is_valid(ddata->pd_gpio)) 90 gpio_set_value_cansleep(ddata->pd_gpio, 1); 91 92 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 93 94 return 0; 95 } 96 97 static void tfp410_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 (gpio_is_valid(ddata->pd_gpio)) 106 gpio_set_value_cansleep(ddata->pd_gpio, 0); 107 108 in->ops.dpi->disable(in); 109 110 dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 111 } 112 113 static void tfp410_fix_timings(struct omap_video_timings *timings) 114 { 115 timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; 116 timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; 117 timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH; 118 } 119 120 static void tfp410_set_timings(struct omap_dss_device *dssdev, 121 struct omap_video_timings *timings) 122 { 123 struct panel_drv_data *ddata = to_panel_data(dssdev); 124 struct omap_dss_device *in = ddata->in; 125 126 tfp410_fix_timings(timings); 127 128 ddata->timings = *timings; 129 dssdev->panel.timings = *timings; 130 131 in->ops.dpi->set_timings(in, timings); 132 } 133 134 static void tfp410_get_timings(struct omap_dss_device *dssdev, 135 struct omap_video_timings *timings) 136 { 137 struct panel_drv_data *ddata = to_panel_data(dssdev); 138 139 *timings = ddata->timings; 140 } 141 142 static int tfp410_check_timings(struct omap_dss_device *dssdev, 143 struct omap_video_timings *timings) 144 { 145 struct panel_drv_data *ddata = to_panel_data(dssdev); 146 struct omap_dss_device *in = ddata->in; 147 148 tfp410_fix_timings(timings); 149 150 return in->ops.dpi->check_timings(in, timings); 151 } 152 153 static const struct omapdss_dvi_ops tfp410_dvi_ops = { 154 .connect = tfp410_connect, 155 .disconnect = tfp410_disconnect, 156 157 .enable = tfp410_enable, 158 .disable = tfp410_disable, 159 160 .check_timings = tfp410_check_timings, 161 .set_timings = tfp410_set_timings, 162 .get_timings = tfp410_get_timings, 163 }; 164 165 static int tfp410_probe_of(struct platform_device *pdev) 166 { 167 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 168 struct device_node *node = pdev->dev.of_node; 169 struct omap_dss_device *in; 170 int gpio; 171 172 gpio = of_get_named_gpio(node, "powerdown-gpios", 0); 173 174 if (gpio_is_valid(gpio) || gpio == -ENOENT) { 175 ddata->pd_gpio = gpio; 176 } else { 177 dev_err(&pdev->dev, "failed to parse PD gpio\n"); 178 return gpio; 179 } 180 181 in = omapdss_of_find_source_for_first_ep(node); 182 if (IS_ERR(in)) { 183 dev_err(&pdev->dev, "failed to find video source\n"); 184 return PTR_ERR(in); 185 } 186 187 ddata->in = in; 188 189 return 0; 190 } 191 192 static int tfp410_probe(struct platform_device *pdev) 193 { 194 struct panel_drv_data *ddata; 195 struct omap_dss_device *dssdev; 196 int r; 197 198 if (!pdev->dev.of_node) 199 return -ENODEV; 200 201 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 202 if (!ddata) 203 return -ENOMEM; 204 205 platform_set_drvdata(pdev, ddata); 206 207 r = tfp410_probe_of(pdev); 208 if (r) 209 return r; 210 211 if (gpio_is_valid(ddata->pd_gpio)) { 212 r = devm_gpio_request_one(&pdev->dev, ddata->pd_gpio, 213 GPIOF_OUT_INIT_LOW, "tfp410 PD"); 214 if (r) { 215 dev_err(&pdev->dev, "Failed to request PD GPIO %d\n", 216 ddata->pd_gpio); 217 goto err_gpio; 218 } 219 } 220 221 dssdev = &ddata->dssdev; 222 dssdev->ops.dvi = &tfp410_dvi_ops; 223 dssdev->dev = &pdev->dev; 224 dssdev->type = OMAP_DISPLAY_TYPE_DPI; 225 dssdev->output_type = OMAP_DISPLAY_TYPE_DVI; 226 dssdev->owner = THIS_MODULE; 227 dssdev->phy.dpi.data_lines = ddata->data_lines; 228 dssdev->port_num = 1; 229 230 r = omapdss_register_output(dssdev); 231 if (r) { 232 dev_err(&pdev->dev, "Failed to register output\n"); 233 goto err_reg; 234 } 235 236 return 0; 237 err_reg: 238 err_gpio: 239 omap_dss_put_device(ddata->in); 240 return r; 241 } 242 243 static int __exit tfp410_remove(struct platform_device *pdev) 244 { 245 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 246 struct omap_dss_device *dssdev = &ddata->dssdev; 247 struct omap_dss_device *in = ddata->in; 248 249 omapdss_unregister_output(&ddata->dssdev); 250 251 WARN_ON(omapdss_device_is_enabled(dssdev)); 252 if (omapdss_device_is_enabled(dssdev)) 253 tfp410_disable(dssdev); 254 255 WARN_ON(omapdss_device_is_connected(dssdev)); 256 if (omapdss_device_is_connected(dssdev)) 257 tfp410_disconnect(dssdev, dssdev->dst); 258 259 omap_dss_put_device(in); 260 261 return 0; 262 } 263 264 static const struct of_device_id tfp410_of_match[] = { 265 { .compatible = "omapdss,ti,tfp410", }, 266 {}, 267 }; 268 269 MODULE_DEVICE_TABLE(of, tfp410_of_match); 270 271 static struct platform_driver tfp410_driver = { 272 .probe = tfp410_probe, 273 .remove = __exit_p(tfp410_remove), 274 .driver = { 275 .name = "tfp410", 276 .of_match_table = tfp410_of_match, 277 .suppress_bind_attrs = true, 278 }, 279 }; 280 281 module_platform_driver(tfp410_driver); 282 283 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 284 MODULE_DESCRIPTION("TFP410 DPI to DVI encoder driver"); 285 MODULE_LICENSE("GPL"); 286