linux/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c
<<
>>
Prefs
   1/*
   2 * HDMI Connector driver
   3 *
   4 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
   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/gpio/consumer.h>
  13#include <linux/slab.h>
  14#include <linux/module.h>
  15#include <linux/platform_device.h>
  16#include <linux/of.h>
  17#include <linux/of_gpio.h>
  18#include <linux/mutex.h>
  19
  20#include <drm/drm_edid.h>
  21
  22#include "../dss/omapdss.h"
  23
  24static const struct videomode hdmic_default_vm = {
  25        .hactive        = 640,
  26        .vactive        = 480,
  27        .pixelclock     = 25175000,
  28        .hsync_len      = 96,
  29        .hfront_porch   = 16,
  30        .hback_porch    = 48,
  31        .vsync_len      = 2,
  32        .vfront_porch   = 11,
  33        .vback_porch    = 31,
  34
  35        .flags          = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
  36};
  37
  38struct panel_drv_data {
  39        struct omap_dss_device dssdev;
  40        struct omap_dss_device *in;
  41        void (*hpd_cb)(void *cb_data, enum drm_connector_status status);
  42        void *hpd_cb_data;
  43        bool hpd_enabled;
  44        struct mutex hpd_lock;
  45
  46        struct device *dev;
  47
  48        struct videomode vm;
  49
  50        int hpd_gpio;
  51};
  52
  53#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
  54
  55static int hdmic_connect(struct omap_dss_device *dssdev)
  56{
  57        struct panel_drv_data *ddata = to_panel_data(dssdev);
  58        struct omap_dss_device *in;
  59        int r;
  60
  61        dev_dbg(ddata->dev, "connect\n");
  62
  63        if (omapdss_device_is_connected(dssdev))
  64                return 0;
  65
  66        in = omapdss_of_find_source_for_first_ep(ddata->dev->of_node);
  67        if (IS_ERR(in)) {
  68                dev_err(ddata->dev, "failed to find video source\n");
  69                return PTR_ERR(in);
  70        }
  71
  72        r = in->ops.hdmi->connect(in, dssdev);
  73        if (r) {
  74                omap_dss_put_device(in);
  75                return r;
  76        }
  77
  78        ddata->in = in;
  79        return 0;
  80}
  81
  82static void hdmic_disconnect(struct omap_dss_device *dssdev)
  83{
  84        struct panel_drv_data *ddata = to_panel_data(dssdev);
  85        struct omap_dss_device *in = ddata->in;
  86
  87        dev_dbg(ddata->dev, "disconnect\n");
  88
  89        if (!omapdss_device_is_connected(dssdev))
  90                return;
  91
  92        in->ops.hdmi->disconnect(in, dssdev);
  93
  94        omap_dss_put_device(in);
  95        ddata->in = NULL;
  96}
  97
  98static int hdmic_enable(struct omap_dss_device *dssdev)
  99{
 100        struct panel_drv_data *ddata = to_panel_data(dssdev);
 101        struct omap_dss_device *in = ddata->in;
 102        int r;
 103
 104        dev_dbg(ddata->dev, "enable\n");
 105
 106        if (!omapdss_device_is_connected(dssdev))
 107                return -ENODEV;
 108
 109        if (omapdss_device_is_enabled(dssdev))
 110                return 0;
 111
 112        in->ops.hdmi->set_timings(in, &ddata->vm);
 113
 114        r = in->ops.hdmi->enable(in);
 115        if (r)
 116                return r;
 117
 118        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 119
 120        return r;
 121}
 122
 123static void hdmic_disable(struct omap_dss_device *dssdev)
 124{
 125        struct panel_drv_data *ddata = to_panel_data(dssdev);
 126        struct omap_dss_device *in = ddata->in;
 127
 128        dev_dbg(ddata->dev, "disable\n");
 129
 130        if (!omapdss_device_is_enabled(dssdev))
 131                return;
 132
 133        in->ops.hdmi->disable(in);
 134
 135        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 136}
 137
 138static void hdmic_set_timings(struct omap_dss_device *dssdev,
 139                              struct videomode *vm)
 140{
 141        struct panel_drv_data *ddata = to_panel_data(dssdev);
 142        struct omap_dss_device *in = ddata->in;
 143
 144        ddata->vm = *vm;
 145        dssdev->panel.vm = *vm;
 146
 147        in->ops.hdmi->set_timings(in, vm);
 148}
 149
 150static void hdmic_get_timings(struct omap_dss_device *dssdev,
 151                              struct videomode *vm)
 152{
 153        struct panel_drv_data *ddata = to_panel_data(dssdev);
 154
 155        *vm = ddata->vm;
 156}
 157
 158static int hdmic_check_timings(struct omap_dss_device *dssdev,
 159                               struct videomode *vm)
 160{
 161        struct panel_drv_data *ddata = to_panel_data(dssdev);
 162        struct omap_dss_device *in = ddata->in;
 163
 164        return in->ops.hdmi->check_timings(in, vm);
 165}
 166
 167static int hdmic_read_edid(struct omap_dss_device *dssdev,
 168                u8 *edid, int len)
 169{
 170        struct panel_drv_data *ddata = to_panel_data(dssdev);
 171        struct omap_dss_device *in = ddata->in;
 172
 173        return in->ops.hdmi->read_edid(in, edid, len);
 174}
 175
 176static bool hdmic_detect(struct omap_dss_device *dssdev)
 177{
 178        struct panel_drv_data *ddata = to_panel_data(dssdev);
 179        struct omap_dss_device *in = ddata->in;
 180        bool connected;
 181
 182        if (gpio_is_valid(ddata->hpd_gpio))
 183                connected = gpio_get_value_cansleep(ddata->hpd_gpio);
 184        else
 185                connected = in->ops.hdmi->detect(in);
 186        if (!connected && in->ops.hdmi->lost_hotplug)
 187                in->ops.hdmi->lost_hotplug(in);
 188        return connected;
 189}
 190
 191static int hdmic_register_hpd_cb(struct omap_dss_device *dssdev,
 192                                 void (*cb)(void *cb_data,
 193                                            enum drm_connector_status status),
 194                                 void *cb_data)
 195{
 196        struct panel_drv_data *ddata = to_panel_data(dssdev);
 197        struct omap_dss_device *in = ddata->in;
 198
 199        if (gpio_is_valid(ddata->hpd_gpio)) {
 200                mutex_lock(&ddata->hpd_lock);
 201                ddata->hpd_cb = cb;
 202                ddata->hpd_cb_data = cb_data;
 203                mutex_unlock(&ddata->hpd_lock);
 204                return 0;
 205        } else if (in->ops.hdmi->register_hpd_cb) {
 206                return in->ops.hdmi->register_hpd_cb(in, cb, cb_data);
 207        }
 208
 209        return -ENOTSUPP;
 210}
 211
 212static void hdmic_unregister_hpd_cb(struct omap_dss_device *dssdev)
 213{
 214        struct panel_drv_data *ddata = to_panel_data(dssdev);
 215        struct omap_dss_device *in = ddata->in;
 216
 217        if (gpio_is_valid(ddata->hpd_gpio)) {
 218                mutex_lock(&ddata->hpd_lock);
 219                ddata->hpd_cb = NULL;
 220                ddata->hpd_cb_data = NULL;
 221                mutex_unlock(&ddata->hpd_lock);
 222        } else if (in->ops.hdmi->unregister_hpd_cb) {
 223                in->ops.hdmi->unregister_hpd_cb(in);
 224        }
 225}
 226
 227static void hdmic_enable_hpd(struct omap_dss_device *dssdev)
 228{
 229        struct panel_drv_data *ddata = to_panel_data(dssdev);
 230        struct omap_dss_device *in = ddata->in;
 231
 232        if (gpio_is_valid(ddata->hpd_gpio)) {
 233                mutex_lock(&ddata->hpd_lock);
 234                ddata->hpd_enabled = true;
 235                mutex_unlock(&ddata->hpd_lock);
 236        } else if (in->ops.hdmi->enable_hpd) {
 237                in->ops.hdmi->enable_hpd(in);
 238        }
 239}
 240
 241static void hdmic_disable_hpd(struct omap_dss_device *dssdev)
 242{
 243        struct panel_drv_data *ddata = to_panel_data(dssdev);
 244        struct omap_dss_device *in = ddata->in;
 245
 246        if (gpio_is_valid(ddata->hpd_gpio)) {
 247                mutex_lock(&ddata->hpd_lock);
 248                ddata->hpd_enabled = false;
 249                mutex_unlock(&ddata->hpd_lock);
 250        } else if (in->ops.hdmi->disable_hpd) {
 251                in->ops.hdmi->disable_hpd(in);
 252        }
 253}
 254
 255static int hdmic_set_hdmi_mode(struct omap_dss_device *dssdev, bool hdmi_mode)
 256{
 257        struct panel_drv_data *ddata = to_panel_data(dssdev);
 258        struct omap_dss_device *in = ddata->in;
 259
 260        return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
 261}
 262
 263static int hdmic_set_infoframe(struct omap_dss_device *dssdev,
 264                const struct hdmi_avi_infoframe *avi)
 265{
 266        struct panel_drv_data *ddata = to_panel_data(dssdev);
 267        struct omap_dss_device *in = ddata->in;
 268
 269        return in->ops.hdmi->set_infoframe(in, avi);
 270}
 271
 272static struct omap_dss_driver hdmic_driver = {
 273        .connect                = hdmic_connect,
 274        .disconnect             = hdmic_disconnect,
 275
 276        .enable                 = hdmic_enable,
 277        .disable                = hdmic_disable,
 278
 279        .set_timings            = hdmic_set_timings,
 280        .get_timings            = hdmic_get_timings,
 281        .check_timings          = hdmic_check_timings,
 282
 283        .read_edid              = hdmic_read_edid,
 284        .detect                 = hdmic_detect,
 285        .register_hpd_cb        = hdmic_register_hpd_cb,
 286        .unregister_hpd_cb      = hdmic_unregister_hpd_cb,
 287        .enable_hpd             = hdmic_enable_hpd,
 288        .disable_hpd            = hdmic_disable_hpd,
 289        .set_hdmi_mode          = hdmic_set_hdmi_mode,
 290        .set_hdmi_infoframe     = hdmic_set_infoframe,
 291};
 292
 293static irqreturn_t hdmic_hpd_isr(int irq, void *data)
 294{
 295        struct panel_drv_data *ddata = data;
 296
 297        mutex_lock(&ddata->hpd_lock);
 298        if (ddata->hpd_enabled && ddata->hpd_cb) {
 299                enum drm_connector_status status;
 300
 301                if (hdmic_detect(&ddata->dssdev))
 302                        status = connector_status_connected;
 303                else
 304                        status = connector_status_disconnected;
 305
 306                ddata->hpd_cb(ddata->hpd_cb_data, status);
 307        }
 308        mutex_unlock(&ddata->hpd_lock);
 309
 310        return IRQ_HANDLED;
 311}
 312
 313static int hdmic_probe_of(struct platform_device *pdev)
 314{
 315        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 316        struct device_node *node = pdev->dev.of_node;
 317        int gpio;
 318
 319        /* HPD GPIO */
 320        gpio = of_get_named_gpio(node, "hpd-gpios", 0);
 321        if (gpio_is_valid(gpio))
 322                ddata->hpd_gpio = gpio;
 323        else
 324                ddata->hpd_gpio = -ENODEV;
 325
 326        return 0;
 327}
 328
 329static int hdmic_probe(struct platform_device *pdev)
 330{
 331        struct panel_drv_data *ddata;
 332        struct omap_dss_device *dssdev;
 333        int r;
 334
 335        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
 336        if (!ddata)
 337                return -ENOMEM;
 338
 339        platform_set_drvdata(pdev, ddata);
 340        ddata->dev = &pdev->dev;
 341
 342        r = hdmic_probe_of(pdev);
 343        if (r)
 344                return r;
 345
 346        mutex_init(&ddata->hpd_lock);
 347
 348        if (gpio_is_valid(ddata->hpd_gpio)) {
 349                r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
 350                                GPIOF_DIR_IN, "hdmi_hpd");
 351                if (r)
 352                        return r;
 353
 354                r = devm_request_threaded_irq(&pdev->dev,
 355                                gpio_to_irq(ddata->hpd_gpio),
 356                                NULL, hdmic_hpd_isr,
 357                                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
 358                                IRQF_ONESHOT,
 359                                "hdmic hpd", ddata);
 360                if (r)
 361                        return r;
 362        }
 363
 364        ddata->vm = hdmic_default_vm;
 365
 366        dssdev = &ddata->dssdev;
 367        dssdev->driver = &hdmic_driver;
 368        dssdev->dev = &pdev->dev;
 369        dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
 370        dssdev->owner = THIS_MODULE;
 371        dssdev->panel.vm = hdmic_default_vm;
 372
 373        r = omapdss_register_display(dssdev);
 374        if (r) {
 375                dev_err(&pdev->dev, "Failed to register panel\n");
 376                return r;
 377        }
 378
 379        return 0;
 380}
 381
 382static int __exit hdmic_remove(struct platform_device *pdev)
 383{
 384        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 385        struct omap_dss_device *dssdev = &ddata->dssdev;
 386
 387        omapdss_unregister_display(&ddata->dssdev);
 388
 389        hdmic_disable(dssdev);
 390        hdmic_disconnect(dssdev);
 391
 392        return 0;
 393}
 394
 395static const struct of_device_id hdmic_of_match[] = {
 396        { .compatible = "omapdss,hdmi-connector", },
 397        {},
 398};
 399
 400MODULE_DEVICE_TABLE(of, hdmic_of_match);
 401
 402static struct platform_driver hdmi_connector_driver = {
 403        .probe  = hdmic_probe,
 404        .remove = __exit_p(hdmic_remove),
 405        .driver = {
 406                .name   = "connector-hdmi",
 407                .of_match_table = hdmic_of_match,
 408                .suppress_bind_attrs = true,
 409        },
 410};
 411
 412module_platform_driver(hdmi_connector_driver);
 413
 414MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 415MODULE_DESCRIPTION("HDMI Connector driver");
 416MODULE_LICENSE("GPL");
 417