connector-hdmi.c (6934B)
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 19static 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 36struct 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 49static 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 54 dev_dbg(ddata->dev, "connect\n"); 55 56 if (omapdss_device_is_connected(dssdev)) 57 return 0; 58 59 return in->ops.hdmi->connect(in, dssdev); 60} 61 62static void hdmic_disconnect(struct omap_dss_device *dssdev) 63{ 64 struct panel_drv_data *ddata = to_panel_data(dssdev); 65 struct omap_dss_device *in = ddata->in; 66 67 dev_dbg(ddata->dev, "disconnect\n"); 68 69 if (!omapdss_device_is_connected(dssdev)) 70 return; 71 72 in->ops.hdmi->disconnect(in, dssdev); 73} 74 75static int hdmic_enable(struct omap_dss_device *dssdev) 76{ 77 struct panel_drv_data *ddata = to_panel_data(dssdev); 78 struct omap_dss_device *in = ddata->in; 79 int r; 80 81 dev_dbg(ddata->dev, "enable\n"); 82 83 if (!omapdss_device_is_connected(dssdev)) 84 return -ENODEV; 85 86 if (omapdss_device_is_enabled(dssdev)) 87 return 0; 88 89 in->ops.hdmi->set_timings(in, &ddata->timings); 90 91 r = in->ops.hdmi->enable(in); 92 if (r) 93 return r; 94 95 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 96 97 return r; 98} 99 100static void hdmic_disable(struct omap_dss_device *dssdev) 101{ 102 struct panel_drv_data *ddata = to_panel_data(dssdev); 103 struct omap_dss_device *in = ddata->in; 104 105 dev_dbg(ddata->dev, "disable\n"); 106 107 if (!omapdss_device_is_enabled(dssdev)) 108 return; 109 110 in->ops.hdmi->disable(in); 111 112 dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 113} 114 115static void hdmic_set_timings(struct omap_dss_device *dssdev, 116 struct omap_video_timings *timings) 117{ 118 struct panel_drv_data *ddata = to_panel_data(dssdev); 119 struct omap_dss_device *in = ddata->in; 120 121 ddata->timings = *timings; 122 dssdev->panel.timings = *timings; 123 124 in->ops.hdmi->set_timings(in, timings); 125} 126 127static void hdmic_get_timings(struct omap_dss_device *dssdev, 128 struct omap_video_timings *timings) 129{ 130 struct panel_drv_data *ddata = to_panel_data(dssdev); 131 132 *timings = ddata->timings; 133} 134 135static int hdmic_check_timings(struct omap_dss_device *dssdev, 136 struct omap_video_timings *timings) 137{ 138 struct panel_drv_data *ddata = to_panel_data(dssdev); 139 struct omap_dss_device *in = ddata->in; 140 141 return in->ops.hdmi->check_timings(in, timings); 142} 143 144static int hdmic_read_edid(struct omap_dss_device *dssdev, 145 u8 *edid, int len) 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->read_edid(in, edid, len); 151} 152 153static bool hdmic_detect(struct omap_dss_device *dssdev) 154{ 155 struct panel_drv_data *ddata = to_panel_data(dssdev); 156 struct omap_dss_device *in = ddata->in; 157 158 if (gpio_is_valid(ddata->hpd_gpio)) 159 return gpio_get_value_cansleep(ddata->hpd_gpio); 160 else 161 return in->ops.hdmi->detect(in); 162} 163 164static int hdmic_set_hdmi_mode(struct omap_dss_device *dssdev, bool hdmi_mode) 165{ 166 struct panel_drv_data *ddata = to_panel_data(dssdev); 167 struct omap_dss_device *in = ddata->in; 168 169 return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode); 170} 171 172static int hdmic_set_infoframe(struct omap_dss_device *dssdev, 173 const struct hdmi_avi_infoframe *avi) 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_infoframe(in, avi); 179} 180 181static struct omap_dss_driver hdmic_driver = { 182 .connect = hdmic_connect, 183 .disconnect = hdmic_disconnect, 184 185 .enable = hdmic_enable, 186 .disable = hdmic_disable, 187 188 .set_timings = hdmic_set_timings, 189 .get_timings = hdmic_get_timings, 190 .check_timings = hdmic_check_timings, 191 192 .get_resolution = omapdss_default_get_resolution, 193 194 .read_edid = hdmic_read_edid, 195 .detect = hdmic_detect, 196 .set_hdmi_mode = hdmic_set_hdmi_mode, 197 .set_hdmi_infoframe = hdmic_set_infoframe, 198}; 199 200static int hdmic_probe_of(struct platform_device *pdev) 201{ 202 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 203 struct device_node *node = pdev->dev.of_node; 204 struct omap_dss_device *in; 205 int gpio; 206 207 /* HPD GPIO */ 208 gpio = of_get_named_gpio(node, "hpd-gpios", 0); 209 if (gpio_is_valid(gpio)) 210 ddata->hpd_gpio = gpio; 211 else 212 ddata->hpd_gpio = -ENODEV; 213 214 in = omapdss_of_find_source_for_first_ep(node); 215 if (IS_ERR(in)) { 216 dev_err(&pdev->dev, "failed to find video source\n"); 217 return PTR_ERR(in); 218 } 219 220 ddata->in = in; 221 222 return 0; 223} 224 225static int hdmic_probe(struct platform_device *pdev) 226{ 227 struct panel_drv_data *ddata; 228 struct omap_dss_device *dssdev; 229 int r; 230 231 if (!pdev->dev.of_node) 232 return -ENODEV; 233 234 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 235 if (!ddata) 236 return -ENOMEM; 237 238 platform_set_drvdata(pdev, ddata); 239 ddata->dev = &pdev->dev; 240 241 r = hdmic_probe_of(pdev); 242 if (r) 243 return r; 244 245 if (gpio_is_valid(ddata->hpd_gpio)) { 246 r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio, 247 GPIOF_DIR_IN, "hdmi_hpd"); 248 if (r) 249 goto err_reg; 250 } 251 252 ddata->timings = hdmic_default_timings; 253 254 dssdev = &ddata->dssdev; 255 dssdev->driver = &hdmic_driver; 256 dssdev->dev = &pdev->dev; 257 dssdev->type = OMAP_DISPLAY_TYPE_HDMI; 258 dssdev->owner = THIS_MODULE; 259 dssdev->panel.timings = hdmic_default_timings; 260 261 r = omapdss_register_display(dssdev); 262 if (r) { 263 dev_err(&pdev->dev, "Failed to register panel\n"); 264 goto err_reg; 265 } 266 267 return 0; 268err_reg: 269 omap_dss_put_device(ddata->in); 270 return r; 271} 272 273static int __exit hdmic_remove(struct platform_device *pdev) 274{ 275 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 276 struct omap_dss_device *dssdev = &ddata->dssdev; 277 struct omap_dss_device *in = ddata->in; 278 279 omapdss_unregister_display(&ddata->dssdev); 280 281 hdmic_disable(dssdev); 282 hdmic_disconnect(dssdev); 283 284 omap_dss_put_device(in); 285 286 return 0; 287} 288 289static const struct of_device_id hdmic_of_match[] = { 290 { .compatible = "omapdss,hdmi-connector", }, 291 {}, 292}; 293 294MODULE_DEVICE_TABLE(of, hdmic_of_match); 295 296static struct platform_driver hdmi_connector_driver = { 297 .probe = hdmic_probe, 298 .remove = __exit_p(hdmic_remove), 299 .driver = { 300 .name = "connector-hdmi", 301 .of_match_table = hdmic_of_match, 302 .suppress_bind_attrs = true, 303 }, 304}; 305 306module_platform_driver(hdmi_connector_driver); 307 308MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 309MODULE_DESCRIPTION("HDMI Connector driver"); 310MODULE_LICENSE("GPL");