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