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/gpio.h> 17#include <linux/platform_device.h> 18#include <linux/of_gpio.h> 19 20#include <video/omapdss.h> 21#include <video/omap-panel-data.h> 22 23struct panel_drv_data { 24 struct omap_dss_device dssdev; 25 struct omap_dss_device *in; 26 27 int ct_cp_hpd_gpio; 28 int ls_oe_gpio; 29 int hpd_gpio; 30 31 struct omap_video_timings timings; 32}; 33 34#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) 35 36static int tpd_connect(struct omap_dss_device *dssdev, 37 struct omap_dss_device *dst) 38{ 39 struct panel_drv_data *ddata = to_panel_data(dssdev); 40 struct omap_dss_device *in = ddata->in; 41 int r; 42 43 r = in->ops.hdmi->connect(in, dssdev); 44 if (r) 45 return r; 46 47 dst->src = dssdev; 48 dssdev->dst = dst; 49 50 gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1); 51 /* DC-DC converter needs at max 300us to get to 90% of 5V */ 52 udelay(300); 53 54 return 0; 55} 56 57static 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 gpio_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 76static 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 96static 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 109static 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 121static 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 129static 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 141static 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 (!gpio_get_value_cansleep(ddata->hpd_gpio)) 149 return -ENODEV; 150 151 if (gpio_is_valid(ddata->ls_oe_gpio)) 152 gpio_set_value_cansleep(ddata->ls_oe_gpio, 1); 153 154 r = in->ops.hdmi->read_edid(in, edid, len); 155 156 if (gpio_is_valid(ddata->ls_oe_gpio)) 157 gpio_set_value_cansleep(ddata->ls_oe_gpio, 0); 158 159 return r; 160} 161 162static bool tpd_detect(struct omap_dss_device *dssdev) 163{ 164 struct panel_drv_data *ddata = to_panel_data(dssdev); 165 166 return gpio_get_value_cansleep(ddata->hpd_gpio); 167} 168 169static int tpd_set_infoframe(struct omap_dss_device *dssdev, 170 const struct hdmi_avi_infoframe *avi) 171{ 172 struct panel_drv_data *ddata = to_panel_data(dssdev); 173 struct omap_dss_device *in = ddata->in; 174 175 return in->ops.hdmi->set_infoframe(in, avi); 176} 177 178static int tpd_set_hdmi_mode(struct omap_dss_device *dssdev, 179 bool hdmi_mode) 180{ 181 struct panel_drv_data *ddata = to_panel_data(dssdev); 182 struct omap_dss_device *in = ddata->in; 183 184 return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode); 185} 186 187static const struct omapdss_hdmi_ops tpd_hdmi_ops = { 188 .connect = tpd_connect, 189 .disconnect = tpd_disconnect, 190 191 .enable = tpd_enable, 192 .disable = tpd_disable, 193 194 .check_timings = tpd_check_timings, 195 .set_timings = tpd_set_timings, 196 .get_timings = tpd_get_timings, 197 198 .read_edid = tpd_read_edid, 199 .detect = tpd_detect, 200 .set_infoframe = tpd_set_infoframe, 201 .set_hdmi_mode = tpd_set_hdmi_mode, 202}; 203 204static int tpd_probe_pdata(struct platform_device *pdev) 205{ 206 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 207 struct encoder_tpd12s015_platform_data *pdata; 208 struct omap_dss_device *dssdev, *in; 209 210 pdata = dev_get_platdata(&pdev->dev); 211 212 ddata->ct_cp_hpd_gpio = pdata->ct_cp_hpd_gpio; 213 ddata->ls_oe_gpio = pdata->ls_oe_gpio; 214 ddata->hpd_gpio = pdata->hpd_gpio; 215 216 in = omap_dss_find_output(pdata->source); 217 if (in == NULL) { 218 dev_err(&pdev->dev, "Failed to find video source\n"); 219 return -ENODEV; 220 } 221 222 ddata->in = in; 223 224 dssdev = &ddata->dssdev; 225 dssdev->name = pdata->name; 226 227 return 0; 228} 229 230static int tpd_probe_of(struct platform_device *pdev) 231{ 232 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 233 struct device_node *node = pdev->dev.of_node; 234 struct omap_dss_device *in; 235 int gpio; 236 237 /* CT CP HPD GPIO */ 238 gpio = of_get_gpio(node, 0); 239 if (!gpio_is_valid(gpio)) { 240 dev_err(&pdev->dev, "failed to parse CT CP HPD gpio\n"); 241 return gpio; 242 } 243 ddata->ct_cp_hpd_gpio = gpio; 244 245 /* LS OE GPIO */ 246 gpio = of_get_gpio(node, 1); 247 if (gpio_is_valid(gpio) || gpio == -ENOENT) { 248 ddata->ls_oe_gpio = gpio; 249 } else { 250 dev_err(&pdev->dev, "failed to parse LS OE gpio\n"); 251 return gpio; 252 } 253 254 /* HPD GPIO */ 255 gpio = of_get_gpio(node, 2); 256 if (!gpio_is_valid(gpio)) { 257 dev_err(&pdev->dev, "failed to parse HPD gpio\n"); 258 return gpio; 259 } 260 ddata->hpd_gpio = gpio; 261 262 in = omapdss_of_find_source_for_first_ep(node); 263 if (IS_ERR(in)) { 264 dev_err(&pdev->dev, "failed to find video source\n"); 265 return PTR_ERR(in); 266 } 267 268 ddata->in = in; 269 270 return 0; 271} 272 273static int tpd_probe(struct platform_device *pdev) 274{ 275 struct omap_dss_device *in, *dssdev; 276 struct panel_drv_data *ddata; 277 int r; 278 279 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 280 if (!ddata) 281 return -ENOMEM; 282 283 platform_set_drvdata(pdev, ddata); 284 285 if (dev_get_platdata(&pdev->dev)) { 286 r = tpd_probe_pdata(pdev); 287 if (r) 288 return r; 289 } else if (pdev->dev.of_node) { 290 r = tpd_probe_of(pdev); 291 if (r) 292 return r; 293 } else { 294 return -ENODEV; 295 } 296 297 r = devm_gpio_request_one(&pdev->dev, ddata->ct_cp_hpd_gpio, 298 GPIOF_OUT_INIT_LOW, "hdmi_ct_cp_hpd"); 299 if (r) 300 goto err_gpio; 301 302 if (gpio_is_valid(ddata->ls_oe_gpio)) { 303 r = devm_gpio_request_one(&pdev->dev, ddata->ls_oe_gpio, 304 GPIOF_OUT_INIT_LOW, "hdmi_ls_oe"); 305 if (r) 306 goto err_gpio; 307 } 308 309 r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio, 310 GPIOF_DIR_IN, "hdmi_hpd"); 311 if (r) 312 goto err_gpio; 313 314 dssdev = &ddata->dssdev; 315 dssdev->ops.hdmi = &tpd_hdmi_ops; 316 dssdev->dev = &pdev->dev; 317 dssdev->type = OMAP_DISPLAY_TYPE_HDMI; 318 dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI; 319 dssdev->owner = THIS_MODULE; 320 dssdev->port_num = 1; 321 322 in = ddata->in; 323 324 r = omapdss_register_output(dssdev); 325 if (r) { 326 dev_err(&pdev->dev, "Failed to register output\n"); 327 goto err_reg; 328 } 329 330 return 0; 331err_reg: 332err_gpio: 333 omap_dss_put_device(ddata->in); 334 return r; 335} 336 337static int __exit tpd_remove(struct platform_device *pdev) 338{ 339 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 340 struct omap_dss_device *dssdev = &ddata->dssdev; 341 struct omap_dss_device *in = ddata->in; 342 343 omapdss_unregister_output(&ddata->dssdev); 344 345 WARN_ON(omapdss_device_is_enabled(dssdev)); 346 if (omapdss_device_is_enabled(dssdev)) 347 tpd_disable(dssdev); 348 349 WARN_ON(omapdss_device_is_connected(dssdev)); 350 if (omapdss_device_is_connected(dssdev)) 351 tpd_disconnect(dssdev, dssdev->dst); 352 353 omap_dss_put_device(in); 354 355 return 0; 356} 357 358static const struct of_device_id tpd_of_match[] = { 359 { .compatible = "omapdss,ti,tpd12s015", }, 360 {}, 361}; 362 363MODULE_DEVICE_TABLE(of, tpd_of_match); 364 365static struct platform_driver tpd_driver = { 366 .probe = tpd_probe, 367 .remove = __exit_p(tpd_remove), 368 .driver = { 369 .name = "tpd12s015", 370 .of_match_table = tpd_of_match, 371 .suppress_bind_attrs = true, 372 }, 373}; 374 375module_platform_driver(tpd_driver); 376 377MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 378MODULE_DESCRIPTION("TPD12S015 driver"); 379MODULE_LICENSE("GPL"); 380