linux/drivers/video/fbdev/omap2/omapfb/displays/encoder-opa362.c
<<
>>
Prefs
   1/*
   2 * OPA362 analog video amplifier with output/power control
   3 *
   4 * Copyright (C) 2014 Golden Delicious Computers
   5 * Author: H. Nikolaus Schaller <hns@goldelico.com>
   6 *
   7 * based on encoder-tfp410
   8 *
   9 * Copyright (C) 2013 Texas Instruments
  10 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
  11 *
  12 * This program is free software; you can redistribute it and/or modify it
  13 * under the terms of the GNU General Public License version 2 as published by
  14 * the Free Software Foundation.
  15 */
  16
  17#include <linux/gpio.h>
  18#include <linux/module.h>
  19#include <linux/platform_device.h>
  20#include <linux/slab.h>
  21#include <linux/of_gpio.h>
  22
  23#include <video/omapdss.h>
  24
  25struct panel_drv_data {
  26        struct omap_dss_device dssdev;
  27        struct omap_dss_device *in;
  28
  29        struct gpio_desc *enable_gpio;
  30
  31        struct omap_video_timings timings;
  32};
  33
  34#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
  35
  36static int opa362_connect(struct omap_dss_device *dssdev,
  37                struct omap_dss_device *dst)
  38{
  39        struct panel_drv_data *ddata = to_panel_data(dssdev);
  40        struct omap_dss_device *in = ddata->in;
  41        int r;
  42
  43        dev_dbg(dssdev->dev, "connect\n");
  44
  45        if (omapdss_device_is_connected(dssdev))
  46                return -EBUSY;
  47
  48        r = in->ops.atv->connect(in, dssdev);
  49        if (r)
  50                return r;
  51
  52        dst->src = dssdev;
  53        dssdev->dst = dst;
  54
  55        return 0;
  56}
  57
  58static void opa362_disconnect(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
  64        dev_dbg(dssdev->dev, "disconnect\n");
  65
  66        WARN_ON(!omapdss_device_is_connected(dssdev));
  67        if (!omapdss_device_is_connected(dssdev))
  68                return;
  69
  70        WARN_ON(dst != dssdev->dst);
  71        if (dst != dssdev->dst)
  72                return;
  73
  74        dst->src = NULL;
  75        dssdev->dst = NULL;
  76
  77        in->ops.atv->disconnect(in, &ddata->dssdev);
  78}
  79
  80static int opa362_enable(struct omap_dss_device *dssdev)
  81{
  82        struct panel_drv_data *ddata = to_panel_data(dssdev);
  83        struct omap_dss_device *in = ddata->in;
  84        int r;
  85
  86        dev_dbg(dssdev->dev, "enable\n");
  87
  88        if (!omapdss_device_is_connected(dssdev))
  89                return -ENODEV;
  90
  91        if (omapdss_device_is_enabled(dssdev))
  92                return 0;
  93
  94        in->ops.atv->set_timings(in, &ddata->timings);
  95
  96        r = in->ops.atv->enable(in);
  97        if (r)
  98                return r;
  99
 100        if (ddata->enable_gpio)
 101                gpiod_set_value_cansleep(ddata->enable_gpio, 1);
 102
 103        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 104
 105        return 0;
 106}
 107
 108static void opa362_disable(struct omap_dss_device *dssdev)
 109{
 110        struct panel_drv_data *ddata = to_panel_data(dssdev);
 111        struct omap_dss_device *in = ddata->in;
 112
 113        dev_dbg(dssdev->dev, "disable\n");
 114
 115        if (!omapdss_device_is_enabled(dssdev))
 116                return;
 117
 118        if (ddata->enable_gpio)
 119                gpiod_set_value_cansleep(ddata->enable_gpio, 0);
 120
 121        in->ops.atv->disable(in);
 122
 123        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 124}
 125
 126static void opa362_set_timings(struct omap_dss_device *dssdev,
 127                struct omap_video_timings *timings)
 128{
 129        struct panel_drv_data *ddata = to_panel_data(dssdev);
 130        struct omap_dss_device *in = ddata->in;
 131
 132        dev_dbg(dssdev->dev, "set_timings\n");
 133
 134        ddata->timings = *timings;
 135        dssdev->panel.timings = *timings;
 136
 137        in->ops.atv->set_timings(in, timings);
 138}
 139
 140static void opa362_get_timings(struct omap_dss_device *dssdev,
 141                struct omap_video_timings *timings)
 142{
 143        struct panel_drv_data *ddata = to_panel_data(dssdev);
 144
 145        dev_dbg(dssdev->dev, "get_timings\n");
 146
 147        *timings = ddata->timings;
 148}
 149
 150static int opa362_check_timings(struct omap_dss_device *dssdev,
 151                struct omap_video_timings *timings)
 152{
 153        struct panel_drv_data *ddata = to_panel_data(dssdev);
 154        struct omap_dss_device *in = ddata->in;
 155
 156        dev_dbg(dssdev->dev, "check_timings\n");
 157
 158        return in->ops.atv->check_timings(in, timings);
 159}
 160
 161static void opa362_set_type(struct omap_dss_device *dssdev,
 162                enum omap_dss_venc_type type)
 163{
 164        /* we can only drive a COMPOSITE output */
 165        WARN_ON(type != OMAP_DSS_VENC_TYPE_COMPOSITE);
 166
 167}
 168
 169static const struct omapdss_atv_ops opa362_atv_ops = {
 170        .connect        = opa362_connect,
 171        .disconnect     = opa362_disconnect,
 172
 173        .enable         = opa362_enable,
 174        .disable        = opa362_disable,
 175
 176        .check_timings  = opa362_check_timings,
 177        .set_timings    = opa362_set_timings,
 178        .get_timings    = opa362_get_timings,
 179
 180        .set_type       = opa362_set_type,
 181};
 182
 183static int opa362_probe(struct platform_device *pdev)
 184{
 185        struct device_node *node = pdev->dev.of_node;
 186        struct panel_drv_data *ddata;
 187        struct omap_dss_device *dssdev, *in;
 188        struct gpio_desc *gpio;
 189        int r;
 190
 191        dev_dbg(&pdev->dev, "probe\n");
 192
 193        if (node == NULL) {
 194                dev_err(&pdev->dev, "Unable to find device tree\n");
 195                return -EINVAL;
 196        }
 197
 198        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
 199        if (!ddata)
 200                return -ENOMEM;
 201
 202        platform_set_drvdata(pdev, ddata);
 203
 204        gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
 205        if (IS_ERR(gpio))
 206                return PTR_ERR(gpio);
 207
 208        ddata->enable_gpio = gpio;
 209
 210        in = omapdss_of_find_source_for_first_ep(node);
 211        if (IS_ERR(in)) {
 212                dev_err(&pdev->dev, "failed to find video source\n");
 213                return PTR_ERR(in);
 214        }
 215
 216        ddata->in = in;
 217
 218        dssdev = &ddata->dssdev;
 219        dssdev->ops.atv = &opa362_atv_ops;
 220        dssdev->dev = &pdev->dev;
 221        dssdev->type = OMAP_DISPLAY_TYPE_VENC;
 222        dssdev->output_type = OMAP_DISPLAY_TYPE_VENC;
 223        dssdev->owner = THIS_MODULE;
 224
 225        r = omapdss_register_output(dssdev);
 226        if (r) {
 227                dev_err(&pdev->dev, "Failed to register output\n");
 228                goto err_reg;
 229        }
 230
 231        return 0;
 232err_reg:
 233        omap_dss_put_device(ddata->in);
 234        return r;
 235}
 236
 237static int __exit opa362_remove(struct platform_device *pdev)
 238{
 239        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 240        struct omap_dss_device *dssdev = &ddata->dssdev;
 241        struct omap_dss_device *in = ddata->in;
 242
 243        omapdss_unregister_output(&ddata->dssdev);
 244
 245        WARN_ON(omapdss_device_is_enabled(dssdev));
 246        if (omapdss_device_is_enabled(dssdev))
 247                opa362_disable(dssdev);
 248
 249        WARN_ON(omapdss_device_is_connected(dssdev));
 250        if (omapdss_device_is_connected(dssdev))
 251                opa362_disconnect(dssdev, dssdev->dst);
 252
 253        omap_dss_put_device(in);
 254
 255        return 0;
 256}
 257
 258static const struct of_device_id opa362_of_match[] = {
 259        { .compatible = "omapdss,ti,opa362", },
 260        {},
 261};
 262MODULE_DEVICE_TABLE(of, opa362_of_match);
 263
 264static struct platform_driver opa362_driver = {
 265        .probe  = opa362_probe,
 266        .remove = __exit_p(opa362_remove),
 267        .driver = {
 268                .name   = "amplifier-opa362",
 269                .of_match_table = opa362_of_match,
 270                .suppress_bind_attrs = true,
 271        },
 272};
 273
 274module_platform_driver(opa362_driver);
 275
 276MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
 277MODULE_DESCRIPTION("OPA362 analog video amplifier with output/power control");
 278MODULE_LICENSE("GPL v2");
 279