linux/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c
<<
>>
Prefs
   1/*
   2 * Analog TV Connector 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/slab.h>
  13#include <linux/module.h>
  14#include <linux/platform_device.h>
  15#include <linux/of.h>
  16
  17#include <video/omap-panel-data.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 device *dev;
  26
  27        struct omap_video_timings timings;
  28
  29        bool invert_polarity;
  30};
  31
  32static const struct omap_video_timings tvc_pal_timings = {
  33        .x_res          = 720,
  34        .y_res          = 574,
  35        .pixelclock     = 13500000,
  36        .hsw            = 64,
  37        .hfp            = 12,
  38        .hbp            = 68,
  39        .vsw            = 5,
  40        .vfp            = 5,
  41        .vbp            = 41,
  42
  43        .interlace      = true,
  44};
  45
  46static const struct of_device_id tvc_of_match[];
  47
  48#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
  49
  50static int tvc_connect(struct omap_dss_device *dssdev)
  51{
  52        struct panel_drv_data *ddata = to_panel_data(dssdev);
  53        struct omap_dss_device *in = ddata->in;
  54        int r;
  55
  56        dev_dbg(ddata->dev, "connect\n");
  57
  58        if (omapdss_device_is_connected(dssdev))
  59                return 0;
  60
  61        r = in->ops.atv->connect(in, dssdev);
  62        if (r)
  63                return r;
  64
  65        return 0;
  66}
  67
  68static void tvc_disconnect(struct omap_dss_device *dssdev)
  69{
  70        struct panel_drv_data *ddata = to_panel_data(dssdev);
  71        struct omap_dss_device *in = ddata->in;
  72
  73        dev_dbg(ddata->dev, "disconnect\n");
  74
  75        if (!omapdss_device_is_connected(dssdev))
  76                return;
  77
  78        in->ops.atv->disconnect(in, dssdev);
  79}
  80
  81static int tvc_enable(struct omap_dss_device *dssdev)
  82{
  83        struct panel_drv_data *ddata = to_panel_data(dssdev);
  84        struct omap_dss_device *in = ddata->in;
  85        int r;
  86
  87        dev_dbg(ddata->dev, "enable\n");
  88
  89        if (!omapdss_device_is_connected(dssdev))
  90                return -ENODEV;
  91
  92        if (omapdss_device_is_enabled(dssdev))
  93                return 0;
  94
  95        in->ops.atv->set_timings(in, &ddata->timings);
  96
  97        if (!ddata->dev->of_node) {
  98                in->ops.atv->set_type(in, OMAP_DSS_VENC_TYPE_COMPOSITE);
  99
 100                in->ops.atv->invert_vid_out_polarity(in,
 101                        ddata->invert_polarity);
 102        }
 103
 104        r = in->ops.atv->enable(in);
 105        if (r)
 106                return r;
 107
 108        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 109
 110        return r;
 111}
 112
 113static void tvc_disable(struct omap_dss_device *dssdev)
 114{
 115        struct panel_drv_data *ddata = to_panel_data(dssdev);
 116        struct omap_dss_device *in = ddata->in;
 117
 118        dev_dbg(ddata->dev, "disable\n");
 119
 120        if (!omapdss_device_is_enabled(dssdev))
 121                return;
 122
 123        in->ops.atv->disable(in);
 124
 125        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 126}
 127
 128static void tvc_set_timings(struct omap_dss_device *dssdev,
 129                struct omap_video_timings *timings)
 130{
 131        struct panel_drv_data *ddata = to_panel_data(dssdev);
 132        struct omap_dss_device *in = ddata->in;
 133
 134        ddata->timings = *timings;
 135        dssdev->panel.timings = *timings;
 136
 137        in->ops.atv->set_timings(in, timings);
 138}
 139
 140static void tvc_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        *timings = ddata->timings;
 146}
 147
 148static int tvc_check_timings(struct omap_dss_device *dssdev,
 149                struct omap_video_timings *timings)
 150{
 151        struct panel_drv_data *ddata = to_panel_data(dssdev);
 152        struct omap_dss_device *in = ddata->in;
 153
 154        return in->ops.atv->check_timings(in, timings);
 155}
 156
 157static u32 tvc_get_wss(struct omap_dss_device *dssdev)
 158{
 159        struct panel_drv_data *ddata = to_panel_data(dssdev);
 160        struct omap_dss_device *in = ddata->in;
 161
 162        return in->ops.atv->get_wss(in);
 163}
 164
 165static int tvc_set_wss(struct omap_dss_device *dssdev, u32 wss)
 166{
 167        struct panel_drv_data *ddata = to_panel_data(dssdev);
 168        struct omap_dss_device *in = ddata->in;
 169
 170        return in->ops.atv->set_wss(in, wss);
 171}
 172
 173static struct omap_dss_driver tvc_driver = {
 174        .connect                = tvc_connect,
 175        .disconnect             = tvc_disconnect,
 176
 177        .enable                 = tvc_enable,
 178        .disable                = tvc_disable,
 179
 180        .set_timings            = tvc_set_timings,
 181        .get_timings            = tvc_get_timings,
 182        .check_timings          = tvc_check_timings,
 183
 184        .get_resolution         = omapdss_default_get_resolution,
 185
 186        .get_wss                = tvc_get_wss,
 187        .set_wss                = tvc_set_wss,
 188};
 189
 190static int tvc_probe_pdata(struct platform_device *pdev)
 191{
 192        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 193        struct connector_atv_platform_data *pdata;
 194        struct omap_dss_device *in, *dssdev;
 195
 196        pdata = dev_get_platdata(&pdev->dev);
 197
 198        in = omap_dss_find_output(pdata->source);
 199        if (in == NULL) {
 200                dev_err(&pdev->dev, "Failed to find video source\n");
 201                return -EPROBE_DEFER;
 202        }
 203
 204        ddata->in = in;
 205
 206        ddata->invert_polarity = pdata->invert_polarity;
 207
 208        dssdev = &ddata->dssdev;
 209        dssdev->name = pdata->name;
 210
 211        return 0;
 212}
 213
 214static int tvc_probe_of(struct platform_device *pdev)
 215{
 216        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 217        struct device_node *node = pdev->dev.of_node;
 218        struct omap_dss_device *in;
 219
 220        in = omapdss_of_find_source_for_first_ep(node);
 221        if (IS_ERR(in)) {
 222                dev_err(&pdev->dev, "failed to find video source\n");
 223                return PTR_ERR(in);
 224        }
 225
 226        ddata->in = in;
 227
 228        return 0;
 229}
 230
 231static int tvc_probe(struct platform_device *pdev)
 232{
 233        struct panel_drv_data *ddata;
 234        struct omap_dss_device *dssdev;
 235        int r;
 236
 237        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
 238        if (!ddata)
 239                return -ENOMEM;
 240
 241        platform_set_drvdata(pdev, ddata);
 242        ddata->dev = &pdev->dev;
 243
 244        if (dev_get_platdata(&pdev->dev)) {
 245                r = tvc_probe_pdata(pdev);
 246                if (r)
 247                        return r;
 248        } else if (pdev->dev.of_node) {
 249                r = tvc_probe_of(pdev);
 250                if (r)
 251                        return r;
 252        } else {
 253                return -ENODEV;
 254        }
 255
 256        ddata->timings = tvc_pal_timings;
 257
 258        dssdev = &ddata->dssdev;
 259        dssdev->driver = &tvc_driver;
 260        dssdev->dev = &pdev->dev;
 261        dssdev->type = OMAP_DISPLAY_TYPE_VENC;
 262        dssdev->owner = THIS_MODULE;
 263        dssdev->panel.timings = tvc_pal_timings;
 264
 265        r = omapdss_register_display(dssdev);
 266        if (r) {
 267                dev_err(&pdev->dev, "Failed to register panel\n");
 268                goto err_reg;
 269        }
 270
 271        return 0;
 272err_reg:
 273        omap_dss_put_device(ddata->in);
 274        return r;
 275}
 276
 277static int __exit tvc_remove(struct platform_device *pdev)
 278{
 279        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 280        struct omap_dss_device *dssdev = &ddata->dssdev;
 281        struct omap_dss_device *in = ddata->in;
 282
 283        omapdss_unregister_display(&ddata->dssdev);
 284
 285        tvc_disable(dssdev);
 286        tvc_disconnect(dssdev);
 287
 288        omap_dss_put_device(in);
 289
 290        return 0;
 291}
 292
 293static const struct of_device_id tvc_of_match[] = {
 294        { .compatible = "omapdss,svideo-connector", },
 295        { .compatible = "omapdss,composite-video-connector", },
 296        {},
 297};
 298
 299MODULE_DEVICE_TABLE(of, tvc_of_match);
 300
 301static struct platform_driver tvc_connector_driver = {
 302        .probe  = tvc_probe,
 303        .remove = __exit_p(tvc_remove),
 304        .driver = {
 305                .name   = "connector-analog-tv",
 306                .of_match_table = tvc_of_match,
 307                .suppress_bind_attrs = true,
 308        },
 309};
 310
 311module_platform_driver(tvc_connector_driver);
 312
 313MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 314MODULE_DESCRIPTION("Analog TV Connector driver");
 315MODULE_LICENSE("GPL");
 316