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