1 /* 2 * TPD12S015 HDMI ESD protection & level shifter chip 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/completion.h> 13 #include <linux/delay.h> 14 #include <linux/module.h> 15 #include <linux/slab.h> 16 #include <linux/platform_device.h> 17 #include <linux/gpio/consumer.h> 18 19 #include <video/omapfb_dss.h> 20 21 struct panel_drv_data { 22 struct omap_dss_device dssdev; 23 struct omap_dss_device *in; 24 25 struct gpio_desc *ct_cp_hpd_gpio; 26 struct gpio_desc *ls_oe_gpio; 27 struct gpio_desc *hpd_gpio; 28 29 struct omap_video_timings timings; 30 }; 31 32 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) 33 34 static int tpd_connect(struct omap_dss_device *dssdev, 35 struct omap_dss_device *dst) 36 { 37 struct panel_drv_data *ddata = to_panel_data(dssdev); 38 struct omap_dss_device *in = ddata->in; 39 int r; 40 41 r = in->ops.hdmi->connect(in, dssdev); 42 if (r) 43 return r; 44 45 dst->src = dssdev; 46 dssdev->dst = dst; 47 48 if (ddata->ct_cp_hpd_gpio) { 49 gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1); 50 /* DC-DC converter needs at max 300us to get to 90% of 5V */ 51 udelay(300); 52 } 53 54 return 0; 55 } 56 57 static void tpd_disconnect(struct omap_dss_device *dssdev, 58 struct omap_dss_device *dst) 59 { 60 struct panel_drv_data *ddata = to_panel_data(dssdev); 61 struct omap_dss_device *in = ddata->in; 62 63 WARN_ON(dst != dssdev->dst); 64 65 if (dst != dssdev->dst) 66 return; 67 68 gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0); 69 70 dst->src = NULL; 71 dssdev->dst = NULL; 72 73 in->ops.hdmi->disconnect(in, &ddata->dssdev); 74 } 75 76 static int tpd_enable(struct omap_dss_device *dssdev) 77 { 78 struct panel_drv_data *ddata = to_panel_data(dssdev); 79 struct omap_dss_device *in = ddata->in; 80 int r; 81 82 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) 83 return 0; 84 85 in->ops.hdmi->set_timings(in, &ddata->timings); 86 87 r = in->ops.hdmi->enable(in); 88 if (r) 89 return r; 90 91 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 92 93 return r; 94 } 95 96 static void tpd_disable(struct omap_dss_device *dssdev) 97 { 98 struct panel_drv_data *ddata = to_panel_data(dssdev); 99 struct omap_dss_device *in = ddata->in; 100 101 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) 102 return; 103 104 in->ops.hdmi->disable(in); 105 106 dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 107 } 108 109 static void tpd_set_timings(struct omap_dss_device *dssdev, 110 struct omap_video_timings *timings) 111 { 112 struct panel_drv_data *ddata = to_panel_data(dssdev); 113 struct omap_dss_device *in = ddata->in; 114 115 ddata->timings = *timings; 116 dssdev->panel.timings = *timings; 117 118 in->ops.hdmi->set_timings(in, timings); 119 } 120 121 static void tpd_get_timings(struct omap_dss_device *dssdev, 122 struct omap_video_timings *timings) 123 { 124 struct panel_drv_data *ddata = to_panel_data(dssdev); 125 126 *timings = ddata->timings; 127 } 128 129 static int tpd_check_timings(struct omap_dss_device *dssdev, 130 struct omap_video_timings *timings) 131 { 132 struct panel_drv_data *ddata = to_panel_data(dssdev); 133 struct omap_dss_device *in = ddata->in; 134 int r; 135 136 r = in->ops.hdmi->check_timings(in, timings); 137 138 return r; 139 } 140 141 static int tpd_read_edid(struct omap_dss_device *dssdev, 142 u8 *edid, int len) 143 { 144 struct panel_drv_data *ddata = to_panel_data(dssdev); 145 struct omap_dss_device *in = ddata->in; 146 int r; 147 148 if (!gpiod_get_value_cansleep(ddata->hpd_gpio)) 149 return -ENODEV; 150 151 gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1); 152 153 r = in->ops.hdmi->read_edid(in, edid, len); 154 155 gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0); 156 157 return r; 158 } 159 160 static bool tpd_detect(struct omap_dss_device *dssdev) 161 { 162 struct panel_drv_data *ddata = to_panel_data(dssdev); 163 164 return gpiod_get_value_cansleep(ddata->hpd_gpio); 165 } 166 167 static int tpd_set_infoframe(struct omap_dss_device *dssdev, 168 const struct hdmi_avi_infoframe *avi) 169 { 170 struct panel_drv_data *ddata = to_panel_data(dssdev); 171 struct omap_dss_device *in = ddata->in; 172 173 return in->ops.hdmi->set_infoframe(in, avi); 174 } 175 176 static int tpd_set_hdmi_mode(struct omap_dss_device *dssdev, 177 bool hdmi_mode) 178 { 179 struct panel_drv_data *ddata = to_panel_data(dssdev); 180 struct omap_dss_device *in = ddata->in; 181 182 return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode); 183 } 184 185 static const struct omapdss_hdmi_ops tpd_hdmi_ops = { 186 .connect = tpd_connect, 187 .disconnect = tpd_disconnect, 188 189 .enable = tpd_enable, 190 .disable = tpd_disable, 191 192 .check_timings = tpd_check_timings, 193 .set_timings = tpd_set_timings, 194 .get_timings = tpd_get_timings, 195 196 .read_edid = tpd_read_edid, 197 .detect = tpd_detect, 198 .set_infoframe = tpd_set_infoframe, 199 .set_hdmi_mode = tpd_set_hdmi_mode, 200 }; 201 202 static int tpd_probe_of(struct platform_device *pdev) 203 { 204 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 205 struct device_node *node = pdev->dev.of_node; 206 struct omap_dss_device *in; 207 208 in = omapdss_of_find_source_for_first_ep(node); 209 if (IS_ERR(in)) { 210 dev_err(&pdev->dev, "failed to find video source\n"); 211 return PTR_ERR(in); 212 } 213 214 ddata->in = in; 215 216 return 0; 217 } 218 219 static int tpd_probe(struct platform_device *pdev) 220 { 221 struct omap_dss_device *in, *dssdev; 222 struct panel_drv_data *ddata; 223 int r; 224 struct gpio_desc *gpio; 225 226 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 227 if (!ddata) 228 return -ENOMEM; 229 230 platform_set_drvdata(pdev, ddata); 231 232 if (pdev->dev.of_node) { 233 r = tpd_probe_of(pdev); 234 if (r) 235 return r; 236 } else { 237 return -ENODEV; 238 } 239 240 241 gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0, 242 GPIOD_OUT_LOW); 243 if (IS_ERR(gpio)) 244 goto err_gpio; 245 246 ddata->ct_cp_hpd_gpio = gpio; 247 248 gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1, 249 GPIOD_OUT_LOW); 250 if (IS_ERR(gpio)) 251 goto err_gpio; 252 253 ddata->ls_oe_gpio = gpio; 254 255 gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2, 256 GPIOD_IN); 257 if (IS_ERR(gpio)) 258 goto err_gpio; 259 260 ddata->hpd_gpio = gpio; 261 262 dssdev = &ddata->dssdev; 263 dssdev->ops.hdmi = &tpd_hdmi_ops; 264 dssdev->dev = &pdev->dev; 265 dssdev->type = OMAP_DISPLAY_TYPE_HDMI; 266 dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI; 267 dssdev->owner = THIS_MODULE; 268 dssdev->port_num = 1; 269 270 in = ddata->in; 271 272 r = omapdss_register_output(dssdev); 273 if (r) { 274 dev_err(&pdev->dev, "Failed to register output\n"); 275 goto err_reg; 276 } 277 278 return 0; 279 err_reg: 280 err_gpio: 281 omap_dss_put_device(ddata->in); 282 return r; 283 } 284 285 static int __exit tpd_remove(struct platform_device *pdev) 286 { 287 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 288 struct omap_dss_device *dssdev = &ddata->dssdev; 289 struct omap_dss_device *in = ddata->in; 290 291 omapdss_unregister_output(&ddata->dssdev); 292 293 WARN_ON(omapdss_device_is_enabled(dssdev)); 294 if (omapdss_device_is_enabled(dssdev)) 295 tpd_disable(dssdev); 296 297 WARN_ON(omapdss_device_is_connected(dssdev)); 298 if (omapdss_device_is_connected(dssdev)) 299 tpd_disconnect(dssdev, dssdev->dst); 300 301 omap_dss_put_device(in); 302 303 return 0; 304 } 305 306 static const struct of_device_id tpd_of_match[] = { 307 { .compatible = "omapdss,ti,tpd12s015", }, 308 {}, 309 }; 310 311 MODULE_DEVICE_TABLE(of, tpd_of_match); 312 313 static struct platform_driver tpd_driver = { 314 .probe = tpd_probe, 315 .remove = __exit_p(tpd_remove), 316 .driver = { 317 .name = "tpd12s015", 318 .of_match_table = tpd_of_match, 319 .suppress_bind_attrs = true, 320 }, 321 }; 322 323 module_platform_driver(tpd_driver); 324 325 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 326 MODULE_DESCRIPTION("TPD12S015 driver"); 327 MODULE_LICENSE("GPL"); 328