linux/drivers/gpu/drm/omapdrm/displays/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/platform_device.h>
  17#include <linux/gpio/consumer.h>
  18
  19#include "../dss/omapdss.h"
  20
  21struct panel_drv_data {
  22        struct omap_dss_device dssdev;
  23        struct omap_dss_device *in;
  24
  25        struct gpio_desc *ct_cp_hpd_gpio;
  26        struct gpio_desc *ls_oe_gpio;
  27        struct gpio_desc *hpd_gpio;
  28
  29        struct omap_video_timings timings;
  30};
  31
  32#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
  33
  34static int tpd_connect(struct omap_dss_device *dssdev,
  35                struct omap_dss_device *dst)
  36{
  37        struct panel_drv_data *ddata = to_panel_data(dssdev);
  38        struct omap_dss_device *in = ddata->in;
  39        int r;
  40
  41        r = in->ops.hdmi->connect(in, dssdev);
  42        if (r)
  43                return r;
  44
  45        dst->src = dssdev;
  46        dssdev->dst = dst;
  47
  48        gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
  49        /* DC-DC converter needs at max 300us to get to 90% of 5V */
  50        udelay(300);
  51
  52        return 0;
  53}
  54
  55static void tpd_disconnect(struct omap_dss_device *dssdev,
  56                struct omap_dss_device *dst)
  57{
  58        struct panel_drv_data *ddata = to_panel_data(dssdev);
  59        struct omap_dss_device *in = ddata->in;
  60
  61        WARN_ON(dst != dssdev->dst);
  62
  63        if (dst != dssdev->dst)
  64                return;
  65
  66        gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
  67
  68        dst->src = NULL;
  69        dssdev->dst = NULL;
  70
  71        in->ops.hdmi->disconnect(in, &ddata->dssdev);
  72}
  73
  74static int tpd_enable(struct omap_dss_device *dssdev)
  75{
  76        struct panel_drv_data *ddata = to_panel_data(dssdev);
  77        struct omap_dss_device *in = ddata->in;
  78        int r;
  79
  80        if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
  81                return 0;
  82
  83        in->ops.hdmi->set_timings(in, &ddata->timings);
  84
  85        r = in->ops.hdmi->enable(in);
  86        if (r)
  87                return r;
  88
  89        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
  90
  91        return r;
  92}
  93
  94static void tpd_disable(struct omap_dss_device *dssdev)
  95{
  96        struct panel_drv_data *ddata = to_panel_data(dssdev);
  97        struct omap_dss_device *in = ddata->in;
  98
  99        if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
 100                return;
 101
 102        in->ops.hdmi->disable(in);
 103
 104        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 105}
 106
 107static void tpd_set_timings(struct omap_dss_device *dssdev,
 108                struct omap_video_timings *timings)
 109{
 110        struct panel_drv_data *ddata = to_panel_data(dssdev);
 111        struct omap_dss_device *in = ddata->in;
 112
 113        ddata->timings = *timings;
 114        dssdev->panel.timings = *timings;
 115
 116        in->ops.hdmi->set_timings(in, timings);
 117}
 118
 119static void tpd_get_timings(struct omap_dss_device *dssdev,
 120                struct omap_video_timings *timings)
 121{
 122        struct panel_drv_data *ddata = to_panel_data(dssdev);
 123
 124        *timings = ddata->timings;
 125}
 126
 127static int tpd_check_timings(struct omap_dss_device *dssdev,
 128                struct omap_video_timings *timings)
 129{
 130        struct panel_drv_data *ddata = to_panel_data(dssdev);
 131        struct omap_dss_device *in = ddata->in;
 132        int r;
 133
 134        r = in->ops.hdmi->check_timings(in, timings);
 135
 136        return r;
 137}
 138
 139static int tpd_read_edid(struct omap_dss_device *dssdev,
 140                u8 *edid, int len)
 141{
 142        struct panel_drv_data *ddata = to_panel_data(dssdev);
 143        struct omap_dss_device *in = ddata->in;
 144        int r;
 145
 146        if (!gpiod_get_value_cansleep(ddata->hpd_gpio))
 147                return -ENODEV;
 148
 149        gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1);
 150
 151        r = in->ops.hdmi->read_edid(in, edid, len);
 152
 153        gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0);
 154
 155        return r;
 156}
 157
 158static bool tpd_detect(struct omap_dss_device *dssdev)
 159{
 160        struct panel_drv_data *ddata = to_panel_data(dssdev);
 161
 162        return gpiod_get_value_cansleep(ddata->hpd_gpio);
 163}
 164
 165static int tpd_set_infoframe(struct omap_dss_device *dssdev,
 166                const struct hdmi_avi_infoframe *avi)
 167{
 168        struct panel_drv_data *ddata = to_panel_data(dssdev);
 169        struct omap_dss_device *in = ddata->in;
 170
 171        return in->ops.hdmi->set_infoframe(in, avi);
 172}
 173
 174static int tpd_set_hdmi_mode(struct omap_dss_device *dssdev,
 175                bool hdmi_mode)
 176{
 177        struct panel_drv_data *ddata = to_panel_data(dssdev);
 178        struct omap_dss_device *in = ddata->in;
 179
 180        return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
 181}
 182
 183static const struct omapdss_hdmi_ops tpd_hdmi_ops = {
 184        .connect                = tpd_connect,
 185        .disconnect             = tpd_disconnect,
 186
 187        .enable                 = tpd_enable,
 188        .disable                = tpd_disable,
 189
 190        .check_timings          = tpd_check_timings,
 191        .set_timings            = tpd_set_timings,
 192        .get_timings            = tpd_get_timings,
 193
 194        .read_edid              = tpd_read_edid,
 195        .detect                 = tpd_detect,
 196        .set_infoframe          = tpd_set_infoframe,
 197        .set_hdmi_mode          = tpd_set_hdmi_mode,
 198};
 199
 200static int tpd_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
 206        in = omapdss_of_find_source_for_first_ep(node);
 207        if (IS_ERR(in)) {
 208                dev_err(&pdev->dev, "failed to find video source\n");
 209                return PTR_ERR(in);
 210        }
 211
 212        ddata->in = in;
 213
 214        return 0;
 215}
 216
 217static int tpd_probe(struct platform_device *pdev)
 218{
 219        struct omap_dss_device *in, *dssdev;
 220        struct panel_drv_data *ddata;
 221        int r;
 222        struct gpio_desc *gpio;
 223
 224        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
 225        if (!ddata)
 226                return -ENOMEM;
 227
 228        platform_set_drvdata(pdev, ddata);
 229
 230        if (!pdev->dev.of_node)
 231                return -ENODEV;
 232
 233        r = tpd_probe_of(pdev);
 234        if (r)
 235                return r;
 236
 237
 238        gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
 239                 GPIOD_OUT_LOW);
 240        if (IS_ERR(gpio))
 241                goto err_gpio;
 242
 243        ddata->ct_cp_hpd_gpio = gpio;
 244
 245        gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
 246                 GPIOD_OUT_LOW);
 247        if (IS_ERR(gpio))
 248                goto err_gpio;
 249
 250        ddata->ls_oe_gpio = gpio;
 251
 252        gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2,
 253                GPIOD_IN);
 254        if (IS_ERR(gpio))
 255                goto err_gpio;
 256
 257        ddata->hpd_gpio = gpio;
 258
 259        dssdev = &ddata->dssdev;
 260        dssdev->ops.hdmi = &tpd_hdmi_ops;
 261        dssdev->dev = &pdev->dev;
 262        dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
 263        dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI;
 264        dssdev->owner = THIS_MODULE;
 265        dssdev->port_num = 1;
 266
 267        in = ddata->in;
 268
 269        r = omapdss_register_output(dssdev);
 270        if (r) {
 271                dev_err(&pdev->dev, "Failed to register output\n");
 272                goto err_reg;
 273        }
 274
 275        return 0;
 276err_reg:
 277err_gpio:
 278        omap_dss_put_device(ddata->in);
 279        return r;
 280}
 281
 282static int __exit tpd_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_output(&ddata->dssdev);
 289
 290        WARN_ON(omapdss_device_is_enabled(dssdev));
 291        if (omapdss_device_is_enabled(dssdev))
 292                tpd_disable(dssdev);
 293
 294        WARN_ON(omapdss_device_is_connected(dssdev));
 295        if (omapdss_device_is_connected(dssdev))
 296                tpd_disconnect(dssdev, dssdev->dst);
 297
 298        omap_dss_put_device(in);
 299
 300        return 0;
 301}
 302
 303static const struct of_device_id tpd_of_match[] = {
 304        { .compatible = "omapdss,ti,tpd12s015", },
 305        {},
 306};
 307
 308MODULE_DEVICE_TABLE(of, tpd_of_match);
 309
 310static struct platform_driver tpd_driver = {
 311        .probe  = tpd_probe,
 312        .remove = __exit_p(tpd_remove),
 313        .driver = {
 314                .name   = "tpd12s015",
 315                .of_match_table = tpd_of_match,
 316                .suppress_bind_attrs = true,
 317        },
 318};
 319
 320module_platform_driver(tpd_driver);
 321
 322MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 323MODULE_DESCRIPTION("TPD12S015 driver");
 324MODULE_LICENSE("GPL");
 325