linux/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c
<<
>>
Prefs
   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_of(struct platform_device *pdev)
 210{
 211        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 212        struct device_node *node = pdev->dev.of_node;
 213        struct omap_dss_device *in;
 214        int gpio;
 215
 216        /* HPD GPIO */
 217        gpio = of_get_named_gpio(node, "hpd-gpios", 0);
 218        if (gpio_is_valid(gpio))
 219                ddata->hpd_gpio = gpio;
 220        else
 221                ddata->hpd_gpio = -ENODEV;
 222
 223        in = omapdss_of_find_source_for_first_ep(node);
 224        if (IS_ERR(in)) {
 225                dev_err(&pdev->dev, "failed to find video source\n");
 226                return PTR_ERR(in);
 227        }
 228
 229        ddata->in = in;
 230
 231        return 0;
 232}
 233
 234static int hdmic_probe(struct platform_device *pdev)
 235{
 236        struct panel_drv_data *ddata;
 237        struct omap_dss_device *dssdev;
 238        int r;
 239
 240        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
 241        if (!ddata)
 242                return -ENOMEM;
 243
 244        platform_set_drvdata(pdev, ddata);
 245        ddata->dev = &pdev->dev;
 246
 247        if (!pdev->dev.of_node)
 248                return -ENODEV;
 249
 250        r = hdmic_probe_of(pdev);
 251        if (r)
 252                return r;
 253
 254        if (gpio_is_valid(ddata->hpd_gpio)) {
 255                r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
 256                                GPIOF_DIR_IN, "hdmi_hpd");
 257                if (r)
 258                        goto err_reg;
 259        }
 260
 261        ddata->timings = hdmic_default_timings;
 262
 263        dssdev = &ddata->dssdev;
 264        dssdev->driver = &hdmic_driver;
 265        dssdev->dev = &pdev->dev;
 266        dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
 267        dssdev->owner = THIS_MODULE;
 268        dssdev->panel.timings = hdmic_default_timings;
 269
 270        r = omapdss_register_display(dssdev);
 271        if (r) {
 272                dev_err(&pdev->dev, "Failed to register panel\n");
 273                goto err_reg;
 274        }
 275
 276        return 0;
 277err_reg:
 278        omap_dss_put_device(ddata->in);
 279        return r;
 280}
 281
 282static int __exit hdmic_remove(struct platform_device *pdev)
 283{
 284        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 285        struct omap_dss_device *dssdev = &ddata->dssdev;
 286        struct omap_dss_device *in = ddata->in;
 287
 288        omapdss_unregister_display(&ddata->dssdev);
 289
 290        hdmic_disable(dssdev);
 291        hdmic_disconnect(dssdev);
 292
 293        omap_dss_put_device(in);
 294
 295        return 0;
 296}
 297
 298static const struct of_device_id hdmic_of_match[] = {
 299        { .compatible = "omapdss,hdmi-connector", },
 300        {},
 301};
 302
 303MODULE_DEVICE_TABLE(of, hdmic_of_match);
 304
 305static struct platform_driver hdmi_connector_driver = {
 306        .probe  = hdmic_probe,
 307        .remove = __exit_p(hdmic_remove),
 308        .driver = {
 309                .name   = "connector-hdmi",
 310                .of_match_table = hdmic_of_match,
 311                .suppress_bind_attrs = true,
 312        },
 313};
 314
 315module_platform_driver(hdmi_connector_driver);
 316
 317MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 318MODULE_DESCRIPTION("HDMI Connector driver");
 319MODULE_LICENSE("GPL");
 320