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