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