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