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