1/* 2 * HDMI Connector 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/slab.h> 13#include <linux/module.h> 14#include <linux/platform_device.h> 15#include <linux/of.h> 16#include <linux/of_gpio.h> 17 18#include <drm/drm_edid.h> 19 20#include <video/omapdss.h> 21#include <video/omap-panel-data.h> 22 23static const struct omap_video_timings hdmic_default_timings = { 24 .x_res = 640, 25 .y_res = 480, 26 .pixelclock = 25175000, 27 .hsw = 96, 28 .hfp = 16, 29 .hbp = 48, 30 .vsw = 2, 31 .vfp = 11, 32 .vbp = 31, 33 34 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, 35 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, 36 37 .interlace = false, 38}; 39 40struct panel_drv_data { 41 struct omap_dss_device dssdev; 42 struct omap_dss_device *in; 43 44 struct device *dev; 45 46 struct omap_video_timings timings; 47 48 int hpd_gpio; 49}; 50 51#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) 52 53static int hdmic_connect(struct omap_dss_device *dssdev) 54{ 55 struct panel_drv_data *ddata = to_panel_data(dssdev); 56 struct omap_dss_device *in = ddata->in; 57 int r; 58 59 dev_dbg(ddata->dev, "connect\n"); 60 61 if (omapdss_device_is_connected(dssdev)) 62 return 0; 63 64 r = in->ops.hdmi->connect(in, dssdev); 65 if (r) 66 return r; 67 68 return 0; 69} 70 71static void hdmic_disconnect(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 76 dev_dbg(ddata->dev, "disconnect\n"); 77 78 if (!omapdss_device_is_connected(dssdev)) 79 return; 80 81 in->ops.hdmi->disconnect(in, dssdev); 82} 83 84static int hdmic_enable(struct omap_dss_device *dssdev) 85{ 86 struct panel_drv_data *ddata = to_panel_data(dssdev); 87 struct omap_dss_device *in = ddata->in; 88 int r; 89 90 dev_dbg(ddata->dev, "enable\n"); 91 92 if (!omapdss_device_is_connected(dssdev)) 93 return -ENODEV; 94 95 if (omapdss_device_is_enabled(dssdev)) 96 return 0; 97 98 in->ops.hdmi->set_timings(in, &ddata->timings); 99 100 r = in->ops.hdmi->enable(in); 101 if (r) 102 return r; 103 104 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 105 106 return r; 107} 108 109static void hdmic_disable(struct omap_dss_device *dssdev) 110{ 111 struct panel_drv_data *ddata = to_panel_data(dssdev); 112 struct omap_dss_device *in = ddata->in; 113 114 dev_dbg(ddata->dev, "disable\n"); 115 116 if (!omapdss_device_is_enabled(dssdev)) 117 return; 118 119 in->ops.hdmi->disable(in); 120 121 dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 122} 123 124static void hdmic_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 ddata->timings = *timings; 131 dssdev->panel.timings = *timings; 132 133 in->ops.hdmi->set_timings(in, timings); 134} 135 136static void hdmic_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 144static int hdmic_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 return in->ops.hdmi->check_timings(in, timings); 151} 152 153static int hdmic_read_edid(struct omap_dss_device *dssdev, 154 u8 *edid, int len) 155{ 156 struct panel_drv_data *ddata = to_panel_data(dssdev); 157 struct omap_dss_device *in = ddata->in; 158 159 return in->ops.hdmi->read_edid(in, edid, len); 160} 161 162static bool hdmic_detect(struct omap_dss_device *dssdev) 163{ 164 struct panel_drv_data *ddata = to_panel_data(dssdev); 165 struct omap_dss_device *in = ddata->in; 166 167 if (gpio_is_valid(ddata->hpd_gpio)) 168 return gpio_get_value_cansleep(ddata->hpd_gpio); 169 else 170 return in->ops.hdmi->detect(in); 171} 172 173static int hdmic_set_hdmi_mode(struct omap_dss_device *dssdev, bool hdmi_mode) 174{ 175 struct panel_drv_data *ddata = to_panel_data(dssdev); 176 struct omap_dss_device *in = ddata->in; 177 178 return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode); 179} 180 181static int hdmic_set_infoframe(struct omap_dss_device *dssdev, 182 const struct hdmi_avi_infoframe *avi) 183{ 184 struct panel_drv_data *ddata = to_panel_data(dssdev); 185 struct omap_dss_device *in = ddata->in; 186 187 return in->ops.hdmi->set_infoframe(in, avi); 188} 189 190static struct omap_dss_driver hdmic_driver = { 191 .connect = hdmic_connect, 192 .disconnect = hdmic_disconnect, 193 194 .enable = hdmic_enable, 195 .disable = hdmic_disable, 196 197 .set_timings = hdmic_set_timings, 198 .get_timings = hdmic_get_timings, 199 .check_timings = hdmic_check_timings, 200 201 .get_resolution = omapdss_default_get_resolution, 202 203 .read_edid = hdmic_read_edid, 204 .detect = hdmic_detect, 205 .set_hdmi_mode = hdmic_set_hdmi_mode, 206 .set_hdmi_infoframe = hdmic_set_infoframe, 207}; 208 209static int hdmic_probe_pdata(struct platform_device *pdev) 210{ 211 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 212 struct connector_hdmi_platform_data *pdata; 213 struct omap_dss_device *in, *dssdev; 214 215 pdata = dev_get_platdata(&pdev->dev); 216 217 ddata->hpd_gpio = -ENODEV; 218 219 in = omap_dss_find_output(pdata->source); 220 if (in == NULL) { 221 dev_err(&pdev->dev, "Failed to find video source\n"); 222 return -EPROBE_DEFER; 223 } 224 225 ddata->in = in; 226 227 dssdev = &ddata->dssdev; 228 dssdev->name = pdata->name; 229 230 return 0; 231} 232 233static int hdmic_probe_of(struct platform_device *pdev) 234{ 235 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 236 struct device_node *node = pdev->dev.of_node; 237 struct omap_dss_device *in; 238 int gpio; 239 240 /* HPD GPIO */ 241 gpio = of_get_named_gpio(node, "hpd-gpios", 0); 242 if (gpio_is_valid(gpio)) 243 ddata->hpd_gpio = gpio; 244 else 245 ddata->hpd_gpio = -ENODEV; 246 247 in = omapdss_of_find_source_for_first_ep(node); 248 if (IS_ERR(in)) { 249 dev_err(&pdev->dev, "failed to find video source\n"); 250 return PTR_ERR(in); 251 } 252 253 ddata->in = in; 254 255 return 0; 256} 257 258static int hdmic_probe(struct platform_device *pdev) 259{ 260 struct panel_drv_data *ddata; 261 struct omap_dss_device *dssdev; 262 int r; 263 264 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 265 if (!ddata) 266 return -ENOMEM; 267 268 platform_set_drvdata(pdev, ddata); 269 ddata->dev = &pdev->dev; 270 271 if (dev_get_platdata(&pdev->dev)) { 272 r = hdmic_probe_pdata(pdev); 273 if (r) 274 return r; 275 } else if (pdev->dev.of_node) { 276 r = hdmic_probe_of(pdev); 277 if (r) 278 return r; 279 } else { 280 return -ENODEV; 281 } 282 283 if (gpio_is_valid(ddata->hpd_gpio)) { 284 r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio, 285 GPIOF_DIR_IN, "hdmi_hpd"); 286 if (r) 287 goto err_reg; 288 } 289 290 ddata->timings = hdmic_default_timings; 291 292 dssdev = &ddata->dssdev; 293 dssdev->driver = &hdmic_driver; 294 dssdev->dev = &pdev->dev; 295 dssdev->type = OMAP_DISPLAY_TYPE_HDMI; 296 dssdev->owner = THIS_MODULE; 297 dssdev->panel.timings = hdmic_default_timings; 298 299 r = omapdss_register_display(dssdev); 300 if (r) { 301 dev_err(&pdev->dev, "Failed to register panel\n"); 302 goto err_reg; 303 } 304 305 return 0; 306err_reg: 307 omap_dss_put_device(ddata->in); 308 return r; 309} 310 311static int __exit hdmic_remove(struct platform_device *pdev) 312{ 313 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 314 struct omap_dss_device *dssdev = &ddata->dssdev; 315 struct omap_dss_device *in = ddata->in; 316 317 omapdss_unregister_display(&ddata->dssdev); 318 319 hdmic_disable(dssdev); 320 hdmic_disconnect(dssdev); 321 322 omap_dss_put_device(in); 323 324 return 0; 325} 326 327static const struct of_device_id hdmic_of_match[] = { 328 { .compatible = "omapdss,hdmi-connector", }, 329 {}, 330}; 331 332MODULE_DEVICE_TABLE(of, hdmic_of_match); 333 334static struct platform_driver hdmi_connector_driver = { 335 .probe = hdmic_probe, 336 .remove = __exit_p(hdmic_remove), 337 .driver = { 338 .name = "connector-hdmi", 339 .of_match_table = hdmic_of_match, 340 .suppress_bind_attrs = true, 341 }, 342}; 343 344module_platform_driver(hdmi_connector_driver); 345 346MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 347MODULE_DESCRIPTION("HDMI Connector driver"); 348MODULE_LICENSE("GPL"); 349