linux/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c
<<
>>
Prefs
   1/*
   2 * TFP410 DPI-to-DVI encoder 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/gpio/consumer.h>
  13#include <linux/module.h>
  14#include <linux/platform_device.h>
  15#include <linux/slab.h>
  16#include <linux/of_gpio.h>
  17
  18#include "../dss/omapdss.h"
  19
  20struct panel_drv_data {
  21        struct omap_dss_device dssdev;
  22        struct omap_dss_device *in;
  23
  24        int pd_gpio;
  25        int data_lines;
  26
  27        struct omap_video_timings timings;
  28};
  29
  30#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
  31
  32static int tfp410_connect(struct omap_dss_device *dssdev,
  33                struct omap_dss_device *dst)
  34{
  35        struct panel_drv_data *ddata = to_panel_data(dssdev);
  36        struct omap_dss_device *in = ddata->in;
  37        int r;
  38
  39        if (omapdss_device_is_connected(dssdev))
  40                return -EBUSY;
  41
  42        r = in->ops.dpi->connect(in, dssdev);
  43        if (r)
  44                return r;
  45
  46        dst->src = dssdev;
  47        dssdev->dst = dst;
  48
  49        return 0;
  50}
  51
  52static void tfp410_disconnect(struct omap_dss_device *dssdev,
  53                struct omap_dss_device *dst)
  54{
  55        struct panel_drv_data *ddata = to_panel_data(dssdev);
  56        struct omap_dss_device *in = ddata->in;
  57
  58        WARN_ON(!omapdss_device_is_connected(dssdev));
  59        if (!omapdss_device_is_connected(dssdev))
  60                return;
  61
  62        WARN_ON(dst != dssdev->dst);
  63        if (dst != dssdev->dst)
  64                return;
  65
  66        dst->src = NULL;
  67        dssdev->dst = NULL;
  68
  69        in->ops.dpi->disconnect(in, &ddata->dssdev);
  70}
  71
  72static int tfp410_enable(struct omap_dss_device *dssdev)
  73{
  74        struct panel_drv_data *ddata = to_panel_data(dssdev);
  75        struct omap_dss_device *in = ddata->in;
  76        int r;
  77
  78        if (!omapdss_device_is_connected(dssdev))
  79                return -ENODEV;
  80
  81        if (omapdss_device_is_enabled(dssdev))
  82                return 0;
  83
  84        in->ops.dpi->set_timings(in, &ddata->timings);
  85        if (ddata->data_lines)
  86                in->ops.dpi->set_data_lines(in, ddata->data_lines);
  87
  88        r = in->ops.dpi->enable(in);
  89        if (r)
  90                return r;
  91
  92        if (gpio_is_valid(ddata->pd_gpio))
  93                gpio_set_value_cansleep(ddata->pd_gpio, 1);
  94
  95        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
  96
  97        return 0;
  98}
  99
 100static void tfp410_disable(struct omap_dss_device *dssdev)
 101{
 102        struct panel_drv_data *ddata = to_panel_data(dssdev);
 103        struct omap_dss_device *in = ddata->in;
 104
 105        if (!omapdss_device_is_enabled(dssdev))
 106                return;
 107
 108        if (gpio_is_valid(ddata->pd_gpio))
 109                gpio_set_value_cansleep(ddata->pd_gpio, 0);
 110
 111        in->ops.dpi->disable(in);
 112
 113        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 114}
 115
 116static void tfp410_fix_timings(struct omap_video_timings *timings)
 117{
 118        timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
 119        timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
 120        timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
 121}
 122
 123static void tfp410_set_timings(struct omap_dss_device *dssdev,
 124                struct omap_video_timings *timings)
 125{
 126        struct panel_drv_data *ddata = to_panel_data(dssdev);
 127        struct omap_dss_device *in = ddata->in;
 128
 129        tfp410_fix_timings(timings);
 130
 131        ddata->timings = *timings;
 132        dssdev->panel.timings = *timings;
 133
 134        in->ops.dpi->set_timings(in, timings);
 135}
 136
 137static void tfp410_get_timings(struct omap_dss_device *dssdev,
 138                struct omap_video_timings *timings)
 139{
 140        struct panel_drv_data *ddata = to_panel_data(dssdev);
 141
 142        *timings = ddata->timings;
 143}
 144
 145static int tfp410_check_timings(struct omap_dss_device *dssdev,
 146                struct omap_video_timings *timings)
 147{
 148        struct panel_drv_data *ddata = to_panel_data(dssdev);
 149        struct omap_dss_device *in = ddata->in;
 150
 151        tfp410_fix_timings(timings);
 152
 153        return in->ops.dpi->check_timings(in, timings);
 154}
 155
 156static const struct omapdss_dvi_ops tfp410_dvi_ops = {
 157        .connect        = tfp410_connect,
 158        .disconnect     = tfp410_disconnect,
 159
 160        .enable         = tfp410_enable,
 161        .disable        = tfp410_disable,
 162
 163        .check_timings  = tfp410_check_timings,
 164        .set_timings    = tfp410_set_timings,
 165        .get_timings    = tfp410_get_timings,
 166};
 167
 168static int tfp410_probe_of(struct platform_device *pdev)
 169{
 170        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 171        struct device_node *node = pdev->dev.of_node;
 172        struct omap_dss_device *in;
 173        int gpio;
 174
 175        gpio = of_get_named_gpio(node, "powerdown-gpios", 0);
 176
 177        if (gpio_is_valid(gpio) || gpio == -ENOENT) {
 178                ddata->pd_gpio = gpio;
 179        } else {
 180                dev_err(&pdev->dev, "failed to parse PD gpio\n");
 181                return gpio;
 182        }
 183
 184        in = omapdss_of_find_source_for_first_ep(node);
 185        if (IS_ERR(in)) {
 186                dev_err(&pdev->dev, "failed to find video source\n");
 187                return PTR_ERR(in);
 188        }
 189
 190        ddata->in = in;
 191
 192        return 0;
 193}
 194
 195static int tfp410_probe(struct platform_device *pdev)
 196{
 197        struct panel_drv_data *ddata;
 198        struct omap_dss_device *dssdev;
 199        int r;
 200
 201        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
 202        if (!ddata)
 203                return -ENOMEM;
 204
 205        platform_set_drvdata(pdev, ddata);
 206
 207        if (!pdev->dev.of_node)
 208                return -ENODEV;
 209
 210        r = tfp410_probe_of(pdev);
 211        if (r)
 212                return r;
 213
 214        if (gpio_is_valid(ddata->pd_gpio)) {
 215                r = devm_gpio_request_one(&pdev->dev, ddata->pd_gpio,
 216                                GPIOF_OUT_INIT_LOW, "tfp410 PD");
 217                if (r) {
 218                        dev_err(&pdev->dev, "Failed to request PD GPIO %d\n",
 219                                        ddata->pd_gpio);
 220                        goto err_gpio;
 221                }
 222        }
 223
 224        dssdev = &ddata->dssdev;
 225        dssdev->ops.dvi = &tfp410_dvi_ops;
 226        dssdev->dev = &pdev->dev;
 227        dssdev->type = OMAP_DISPLAY_TYPE_DPI;
 228        dssdev->output_type = OMAP_DISPLAY_TYPE_DVI;
 229        dssdev->owner = THIS_MODULE;
 230        dssdev->phy.dpi.data_lines = ddata->data_lines;
 231        dssdev->port_num = 1;
 232
 233        r = omapdss_register_output(dssdev);
 234        if (r) {
 235                dev_err(&pdev->dev, "Failed to register output\n");
 236                goto err_reg;
 237        }
 238
 239        return 0;
 240err_reg:
 241err_gpio:
 242        omap_dss_put_device(ddata->in);
 243        return r;
 244}
 245
 246static int __exit tfp410_remove(struct platform_device *pdev)
 247{
 248        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 249        struct omap_dss_device *dssdev = &ddata->dssdev;
 250        struct omap_dss_device *in = ddata->in;
 251
 252        omapdss_unregister_output(&ddata->dssdev);
 253
 254        WARN_ON(omapdss_device_is_enabled(dssdev));
 255        if (omapdss_device_is_enabled(dssdev))
 256                tfp410_disable(dssdev);
 257
 258        WARN_ON(omapdss_device_is_connected(dssdev));
 259        if (omapdss_device_is_connected(dssdev))
 260                tfp410_disconnect(dssdev, dssdev->dst);
 261
 262        omap_dss_put_device(in);
 263
 264        return 0;
 265}
 266
 267static const struct of_device_id tfp410_of_match[] = {
 268        { .compatible = "omapdss,ti,tfp410", },
 269        {},
 270};
 271
 272MODULE_DEVICE_TABLE(of, tfp410_of_match);
 273
 274static struct platform_driver tfp410_driver = {
 275        .probe  = tfp410_probe,
 276        .remove = __exit_p(tfp410_remove),
 277        .driver = {
 278                .name   = "tfp410",
 279                .of_match_table = tfp410_of_match,
 280                .suppress_bind_attrs = true,
 281        },
 282};
 283
 284module_platform_driver(tfp410_driver);
 285
 286MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 287MODULE_DESCRIPTION("TFP410 DPI to DVI encoder driver");
 288MODULE_LICENSE("GPL");
 289