287 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * HDMI Connector driver4 *5 * Copyright (C) 2013 Texas Instruments6 * 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 20static 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 37struct 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 50static 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 63static 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 76static 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 101static 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 116static 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 128static 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 136static 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 145static 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 154static 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 else162 return in->ops.hdmi->detect(in);163}164 165static 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 173static 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 182static 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 201static 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;247err_reg:248 omap_dss_put_device(ddata->in);249 return r;250}251 252static void 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 266static const struct of_device_id hdmic_of_match[] = {267 { .compatible = "omapdss,hdmi-connector", },268 {},269};270 271MODULE_DEVICE_TABLE(of, hdmic_of_match);272 273static struct platform_driver hdmi_connector_driver = {274 .probe = hdmic_probe,275 .remove = hdmic_remove,276 .driver = {277 .name = "connector-hdmi",278 .of_match_table = hdmic_of_match,279 },280};281 282module_platform_driver(hdmi_connector_driver);283 284MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");285MODULE_DESCRIPTION("HDMI Connector driver");286MODULE_LICENSE("GPL");287