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