linux/drivers/video/omap2/displays-new/encoder-tpd12s015.c
<<
>>
Prefs
   1/*
   2 * TPD12S015 HDMI ESD protection & level shifter chip 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/completion.h>
  13#include <linux/delay.h>
  14#include <linux/module.h>
  15#include <linux/slab.h>
  16#include <linux/gpio.h>
  17#include <linux/platform_device.h>
  18
  19#include <video/omapdss.h>
  20#include <video/omap-panel-data.h>
  21
  22struct panel_drv_data {
  23        struct omap_dss_device dssdev;
  24        struct omap_dss_device *in;
  25
  26        int ct_cp_hpd_gpio;
  27        int ls_oe_gpio;
  28        int hpd_gpio;
  29
  30        struct omap_video_timings timings;
  31
  32        struct completion hpd_completion;
  33};
  34
  35#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
  36
  37static irqreturn_t tpd_hpd_irq_handler(int irq, void *data)
  38{
  39        struct panel_drv_data *ddata = data;
  40        bool hpd;
  41
  42        hpd = gpio_get_value_cansleep(ddata->hpd_gpio);
  43
  44        dev_dbg(ddata->dssdev.dev, "hpd %d\n", hpd);
  45
  46        if (gpio_is_valid(ddata->ls_oe_gpio)) {
  47                if (hpd)
  48                        gpio_set_value_cansleep(ddata->ls_oe_gpio, 1);
  49                else
  50                        gpio_set_value_cansleep(ddata->ls_oe_gpio, 0);
  51        }
  52
  53        complete_all(&ddata->hpd_completion);
  54
  55        return IRQ_HANDLED;
  56}
  57
  58static int tpd_connect(struct omap_dss_device *dssdev,
  59                struct omap_dss_device *dst)
  60{
  61        struct panel_drv_data *ddata = to_panel_data(dssdev);
  62        struct omap_dss_device *in = ddata->in;
  63        int r;
  64
  65        r = in->ops.hdmi->connect(in, dssdev);
  66        if (r)
  67                return r;
  68
  69        dst->output = dssdev;
  70        dssdev->device = dst;
  71
  72        INIT_COMPLETION(ddata->hpd_completion);
  73
  74        gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
  75        /* DC-DC converter needs at max 300us to get to 90% of 5V */
  76        udelay(300);
  77
  78        /*
  79         * If there's a cable connected, wait for the hpd irq to trigger,
  80         * which turns on the level shifters.
  81         */
  82        if (gpio_get_value_cansleep(ddata->hpd_gpio)) {
  83                unsigned long to;
  84                to = wait_for_completion_timeout(&ddata->hpd_completion,
  85                                msecs_to_jiffies(250));
  86                WARN_ON_ONCE(to == 0);
  87        }
  88
  89        return 0;
  90}
  91
  92static void tpd_disconnect(struct omap_dss_device *dssdev,
  93                struct omap_dss_device *dst)
  94{
  95        struct panel_drv_data *ddata = to_panel_data(dssdev);
  96        struct omap_dss_device *in = ddata->in;
  97
  98        WARN_ON(dst != dssdev->device);
  99
 100        if (dst != dssdev->device)
 101                return;
 102
 103        gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
 104
 105        dst->output = NULL;
 106        dssdev->device = NULL;
 107
 108        in->ops.hdmi->disconnect(in, &ddata->dssdev);
 109}
 110
 111static int tpd_enable(struct omap_dss_device *dssdev)
 112{
 113        struct panel_drv_data *ddata = to_panel_data(dssdev);
 114        struct omap_dss_device *in = ddata->in;
 115        int r;
 116
 117        if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
 118                return 0;
 119
 120        in->ops.hdmi->set_timings(in, &ddata->timings);
 121
 122        r = in->ops.hdmi->enable(in);
 123        if (r)
 124                return r;
 125
 126        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 127
 128        return r;
 129}
 130
 131static void tpd_disable(struct omap_dss_device *dssdev)
 132{
 133        struct panel_drv_data *ddata = to_panel_data(dssdev);
 134        struct omap_dss_device *in = ddata->in;
 135
 136        if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
 137                return;
 138
 139        in->ops.hdmi->disable(in);
 140
 141        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 142}
 143
 144static void tpd_set_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        ddata->timings = *timings;
 151        dssdev->panel.timings = *timings;
 152
 153        in->ops.hdmi->set_timings(in, timings);
 154}
 155
 156static void tpd_get_timings(struct omap_dss_device *dssdev,
 157                struct omap_video_timings *timings)
 158{
 159        struct panel_drv_data *ddata = to_panel_data(dssdev);
 160
 161        *timings = ddata->timings;
 162}
 163
 164static int tpd_check_timings(struct omap_dss_device *dssdev,
 165                struct omap_video_timings *timings)
 166{
 167        struct panel_drv_data *ddata = to_panel_data(dssdev);
 168        struct omap_dss_device *in = ddata->in;
 169        int r;
 170
 171        r = in->ops.hdmi->check_timings(in, timings);
 172
 173        return r;
 174}
 175
 176static int tpd_read_edid(struct omap_dss_device *dssdev,
 177                u8 *edid, int len)
 178{
 179        struct panel_drv_data *ddata = to_panel_data(dssdev);
 180        struct omap_dss_device *in = ddata->in;
 181
 182        if (!gpio_get_value_cansleep(ddata->hpd_gpio))
 183                return -ENODEV;
 184
 185        return in->ops.hdmi->read_edid(in, edid, len);
 186}
 187
 188static bool tpd_detect(struct omap_dss_device *dssdev)
 189{
 190        struct panel_drv_data *ddata = to_panel_data(dssdev);
 191
 192        return gpio_get_value_cansleep(ddata->hpd_gpio);
 193}
 194
 195static int tpd_audio_enable(struct omap_dss_device *dssdev)
 196{
 197        struct panel_drv_data *ddata = to_panel_data(dssdev);
 198        struct omap_dss_device *in = ddata->in;
 199
 200        return in->ops.hdmi->audio_enable(in);
 201}
 202
 203static void tpd_audio_disable(struct omap_dss_device *dssdev)
 204{
 205        struct panel_drv_data *ddata = to_panel_data(dssdev);
 206        struct omap_dss_device *in = ddata->in;
 207
 208        in->ops.hdmi->audio_disable(in);
 209}
 210
 211static int tpd_audio_start(struct omap_dss_device *dssdev)
 212{
 213        struct panel_drv_data *ddata = to_panel_data(dssdev);
 214        struct omap_dss_device *in = ddata->in;
 215
 216        return in->ops.hdmi->audio_start(in);
 217}
 218
 219static void tpd_audio_stop(struct omap_dss_device *dssdev)
 220{
 221        struct panel_drv_data *ddata = to_panel_data(dssdev);
 222        struct omap_dss_device *in = ddata->in;
 223
 224        in->ops.hdmi->audio_stop(in);
 225}
 226
 227static bool tpd_audio_supported(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        return in->ops.hdmi->audio_supported(in);
 233}
 234
 235static int tpd_audio_config(struct omap_dss_device *dssdev,
 236                struct omap_dss_audio *audio)
 237{
 238        struct panel_drv_data *ddata = to_panel_data(dssdev);
 239        struct omap_dss_device *in = ddata->in;
 240
 241        return in->ops.hdmi->audio_config(in, audio);
 242}
 243
 244static const struct omapdss_hdmi_ops tpd_hdmi_ops = {
 245        .connect                = tpd_connect,
 246        .disconnect             = tpd_disconnect,
 247
 248        .enable                 = tpd_enable,
 249        .disable                = tpd_disable,
 250
 251        .check_timings          = tpd_check_timings,
 252        .set_timings            = tpd_set_timings,
 253        .get_timings            = tpd_get_timings,
 254
 255        .read_edid              = tpd_read_edid,
 256        .detect                 = tpd_detect,
 257
 258        .audio_enable           = tpd_audio_enable,
 259        .audio_disable          = tpd_audio_disable,
 260        .audio_start            = tpd_audio_start,
 261        .audio_stop             = tpd_audio_stop,
 262        .audio_supported        = tpd_audio_supported,
 263        .audio_config           = tpd_audio_config,
 264};
 265
 266static int tpd_probe_pdata(struct platform_device *pdev)
 267{
 268        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 269        struct encoder_tpd12s015_platform_data *pdata;
 270        struct omap_dss_device *dssdev, *in;
 271
 272        pdata = dev_get_platdata(&pdev->dev);
 273
 274        ddata->ct_cp_hpd_gpio = pdata->ct_cp_hpd_gpio;
 275        ddata->ls_oe_gpio = pdata->ls_oe_gpio;
 276        ddata->hpd_gpio = pdata->hpd_gpio;
 277
 278        in = omap_dss_find_output(pdata->source);
 279        if (in == NULL) {
 280                dev_err(&pdev->dev, "Failed to find video source\n");
 281                return -ENODEV;
 282        }
 283
 284        ddata->in = in;
 285
 286        dssdev = &ddata->dssdev;
 287        dssdev->name = pdata->name;
 288
 289        return 0;
 290}
 291
 292static int tpd_probe(struct platform_device *pdev)
 293{
 294        struct omap_dss_device *in, *dssdev;
 295        struct panel_drv_data *ddata;
 296        int r;
 297
 298        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
 299        if (!ddata)
 300                return -ENOMEM;
 301
 302        platform_set_drvdata(pdev, ddata);
 303
 304        init_completion(&ddata->hpd_completion);
 305
 306        if (dev_get_platdata(&pdev->dev)) {
 307                r = tpd_probe_pdata(pdev);
 308                if (r)
 309                        return r;
 310        } else {
 311                return -ENODEV;
 312        }
 313
 314        r = devm_gpio_request_one(&pdev->dev, ddata->ct_cp_hpd_gpio,
 315                        GPIOF_OUT_INIT_LOW, "hdmi_ct_cp_hpd");
 316        if (r)
 317                goto err_gpio;
 318
 319        if (gpio_is_valid(ddata->ls_oe_gpio)) {
 320                r = devm_gpio_request_one(&pdev->dev, ddata->ls_oe_gpio,
 321                                GPIOF_OUT_INIT_LOW, "hdmi_ls_oe");
 322                if (r)
 323                        goto err_gpio;
 324        }
 325
 326        r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
 327                        GPIOF_DIR_IN, "hdmi_hpd");
 328        if (r)
 329                goto err_gpio;
 330
 331        r = devm_request_threaded_irq(&pdev->dev, gpio_to_irq(ddata->hpd_gpio),
 332                                 NULL, tpd_hpd_irq_handler,
 333                                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
 334                                 IRQF_ONESHOT, "hpd", ddata);
 335        if (r)
 336                goto err_irq;
 337
 338        dssdev = &ddata->dssdev;
 339        dssdev->ops.hdmi = &tpd_hdmi_ops;
 340        dssdev->dev = &pdev->dev;
 341        dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
 342        dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI;
 343        dssdev->owner = THIS_MODULE;
 344
 345        in = ddata->in;
 346
 347        r = omapdss_register_output(dssdev);
 348        if (r) {
 349                dev_err(&pdev->dev, "Failed to register output\n");
 350                goto err_reg;
 351        }
 352
 353        return 0;
 354err_reg:
 355err_irq:
 356err_gpio:
 357        omap_dss_put_device(ddata->in);
 358        return r;
 359}
 360
 361static int __exit tpd_remove(struct platform_device *pdev)
 362{
 363        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 364        struct omap_dss_device *dssdev = &ddata->dssdev;
 365        struct omap_dss_device *in = ddata->in;
 366
 367        omapdss_unregister_output(&ddata->dssdev);
 368
 369        WARN_ON(omapdss_device_is_enabled(dssdev));
 370        if (omapdss_device_is_enabled(dssdev))
 371                tpd_disable(dssdev);
 372
 373        WARN_ON(omapdss_device_is_connected(dssdev));
 374        if (omapdss_device_is_connected(dssdev))
 375                tpd_disconnect(dssdev, dssdev->device);
 376
 377        omap_dss_put_device(in);
 378
 379        return 0;
 380}
 381
 382static struct platform_driver tpd_driver = {
 383        .probe  = tpd_probe,
 384        .remove = __exit_p(tpd_remove),
 385        .driver = {
 386                .name   = "tpd12s015",
 387                .owner  = THIS_MODULE,
 388        },
 389};
 390
 391module_platform_driver(tpd_driver);
 392
 393MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 394MODULE_DESCRIPTION("TPD12S015 driver");
 395MODULE_LICENSE("GPL");
 396