linux/drivers/video/fbdev/omap2/omapfb/displays/connector-dvi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Generic DVI Connector driver
   4 *
   5 * Copyright (C) 2013 Texas Instruments
   6 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
   7 */
   8
   9#include <linux/i2c.h>
  10#include <linux/module.h>
  11#include <linux/platform_device.h>
  12#include <linux/slab.h>
  13
  14#include <drm/drm_edid.h>
  15
  16#include <video/omapfb_dss.h>
  17
  18static const struct omap_video_timings dvic_default_timings = {
  19        .x_res          = 640,
  20        .y_res          = 480,
  21
  22        .pixelclock     = 23500000,
  23
  24        .hfp            = 48,
  25        .hsw            = 32,
  26        .hbp            = 80,
  27
  28        .vfp            = 3,
  29        .vsw            = 4,
  30        .vbp            = 7,
  31
  32        .vsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
  33        .hsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
  34        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
  35        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
  36        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
  37};
  38
  39struct panel_drv_data {
  40        struct omap_dss_device dssdev;
  41        struct omap_dss_device *in;
  42
  43        struct omap_video_timings timings;
  44
  45        struct i2c_adapter *i2c_adapter;
  46};
  47
  48#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
  49
  50static int dvic_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
  55        if (omapdss_device_is_connected(dssdev))
  56                return 0;
  57
  58        return in->ops.dvi->connect(in, dssdev);
  59}
  60
  61static void dvic_disconnect(struct omap_dss_device *dssdev)
  62{
  63        struct panel_drv_data *ddata = to_panel_data(dssdev);
  64        struct omap_dss_device *in = ddata->in;
  65
  66        if (!omapdss_device_is_connected(dssdev))
  67                return;
  68
  69        in->ops.dvi->disconnect(in, dssdev);
  70}
  71
  72static int dvic_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.dvi->set_timings(in, &ddata->timings);
  85
  86        r = in->ops.dvi->enable(in);
  87        if (r)
  88                return r;
  89
  90        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
  91
  92        return 0;
  93}
  94
  95static void dvic_disable(struct omap_dss_device *dssdev)
  96{
  97        struct panel_drv_data *ddata = to_panel_data(dssdev);
  98        struct omap_dss_device *in = ddata->in;
  99
 100        if (!omapdss_device_is_enabled(dssdev))
 101                return;
 102
 103        in->ops.dvi->disable(in);
 104
 105        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 106}
 107
 108static void dvic_set_timings(struct omap_dss_device *dssdev,
 109                struct omap_video_timings *timings)
 110{
 111        struct panel_drv_data *ddata = to_panel_data(dssdev);
 112        struct omap_dss_device *in = ddata->in;
 113
 114        ddata->timings = *timings;
 115        dssdev->panel.timings = *timings;
 116
 117        in->ops.dvi->set_timings(in, timings);
 118}
 119
 120static void dvic_get_timings(struct omap_dss_device *dssdev,
 121                struct omap_video_timings *timings)
 122{
 123        struct panel_drv_data *ddata = to_panel_data(dssdev);
 124
 125        *timings = ddata->timings;
 126}
 127
 128static int dvic_check_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        return in->ops.dvi->check_timings(in, timings);
 135}
 136
 137static int dvic_ddc_read(struct i2c_adapter *adapter,
 138                unsigned char *buf, u16 count, u8 offset)
 139{
 140        int r, retries;
 141
 142        for (retries = 3; retries > 0; retries--) {
 143                struct i2c_msg msgs[] = {
 144                        {
 145                                .addr   = DDC_ADDR,
 146                                .flags  = 0,
 147                                .len    = 1,
 148                                .buf    = &offset,
 149                        }, {
 150                                .addr   = DDC_ADDR,
 151                                .flags  = I2C_M_RD,
 152                                .len    = count,
 153                                .buf    = buf,
 154                        }
 155                };
 156
 157                r = i2c_transfer(adapter, msgs, 2);
 158                if (r == 2)
 159                        return 0;
 160
 161                if (r != -EAGAIN)
 162                        break;
 163        }
 164
 165        return r < 0 ? r : -EIO;
 166}
 167
 168static int dvic_read_edid(struct omap_dss_device *dssdev,
 169                u8 *edid, int len)
 170{
 171        struct panel_drv_data *ddata = to_panel_data(dssdev);
 172        int r, l, bytes_read;
 173
 174        if (!ddata->i2c_adapter)
 175                return -ENODEV;
 176
 177        l = min(EDID_LENGTH, len);
 178        r = dvic_ddc_read(ddata->i2c_adapter, edid, l, 0);
 179        if (r)
 180                return r;
 181
 182        bytes_read = l;
 183
 184        /* if there are extensions, read second block */
 185        if (len > EDID_LENGTH && edid[0x7e] > 0) {
 186                l = min(EDID_LENGTH, len - EDID_LENGTH);
 187
 188                r = dvic_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH,
 189                                l, EDID_LENGTH);
 190                if (r)
 191                        return r;
 192
 193                bytes_read += l;
 194        }
 195
 196        return bytes_read;
 197}
 198
 199static bool dvic_detect(struct omap_dss_device *dssdev)
 200{
 201        struct panel_drv_data *ddata = to_panel_data(dssdev);
 202        unsigned char out;
 203        int r;
 204
 205        if (!ddata->i2c_adapter)
 206                return true;
 207
 208        r = dvic_ddc_read(ddata->i2c_adapter, &out, 1, 0);
 209
 210        return r == 0;
 211}
 212
 213static struct omap_dss_driver dvic_driver = {
 214        .connect        = dvic_connect,
 215        .disconnect     = dvic_disconnect,
 216
 217        .enable         = dvic_enable,
 218        .disable        = dvic_disable,
 219
 220        .set_timings    = dvic_set_timings,
 221        .get_timings    = dvic_get_timings,
 222        .check_timings  = dvic_check_timings,
 223
 224        .get_resolution = omapdss_default_get_resolution,
 225
 226        .read_edid      = dvic_read_edid,
 227        .detect         = dvic_detect,
 228};
 229
 230static int dvic_probe_of(struct platform_device *pdev)
 231{
 232        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 233        struct device_node *node = pdev->dev.of_node;
 234        struct omap_dss_device *in;
 235        struct device_node *adapter_node;
 236        struct i2c_adapter *adapter;
 237
 238        in = omapdss_of_find_source_for_first_ep(node);
 239        if (IS_ERR(in)) {
 240                dev_err(&pdev->dev, "failed to find video source\n");
 241                return PTR_ERR(in);
 242        }
 243
 244        ddata->in = in;
 245
 246        adapter_node = of_parse_phandle(node, "ddc-i2c-bus", 0);
 247        if (adapter_node) {
 248                adapter = of_get_i2c_adapter_by_node(adapter_node);
 249                if (adapter == NULL) {
 250                        dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n");
 251                        omap_dss_put_device(ddata->in);
 252                        return -EPROBE_DEFER;
 253                }
 254
 255                ddata->i2c_adapter = adapter;
 256        }
 257
 258        return 0;
 259}
 260
 261static int dvic_probe(struct platform_device *pdev)
 262{
 263        struct panel_drv_data *ddata;
 264        struct omap_dss_device *dssdev;
 265        int r;
 266
 267        if (!pdev->dev.of_node)
 268                return -ENODEV;
 269
 270        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
 271        if (!ddata)
 272                return -ENOMEM;
 273
 274        platform_set_drvdata(pdev, ddata);
 275
 276        r = dvic_probe_of(pdev);
 277        if (r)
 278                return r;
 279
 280        ddata->timings = dvic_default_timings;
 281
 282        dssdev = &ddata->dssdev;
 283        dssdev->driver = &dvic_driver;
 284        dssdev->dev = &pdev->dev;
 285        dssdev->type = OMAP_DISPLAY_TYPE_DVI;
 286        dssdev->owner = THIS_MODULE;
 287        dssdev->panel.timings = dvic_default_timings;
 288
 289        r = omapdss_register_display(dssdev);
 290        if (r) {
 291                dev_err(&pdev->dev, "Failed to register panel\n");
 292                goto err_reg;
 293        }
 294
 295        return 0;
 296
 297err_reg:
 298        omap_dss_put_device(ddata->in);
 299
 300        i2c_put_adapter(ddata->i2c_adapter);
 301
 302        return r;
 303}
 304
 305static int __exit dvic_remove(struct platform_device *pdev)
 306{
 307        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 308        struct omap_dss_device *dssdev = &ddata->dssdev;
 309        struct omap_dss_device *in = ddata->in;
 310
 311        omapdss_unregister_display(&ddata->dssdev);
 312
 313        dvic_disable(dssdev);
 314        dvic_disconnect(dssdev);
 315
 316        omap_dss_put_device(in);
 317
 318        i2c_put_adapter(ddata->i2c_adapter);
 319
 320        return 0;
 321}
 322
 323static const struct of_device_id dvic_of_match[] = {
 324        { .compatible = "omapdss,dvi-connector", },
 325        {},
 326};
 327
 328MODULE_DEVICE_TABLE(of, dvic_of_match);
 329
 330static struct platform_driver dvi_connector_driver = {
 331        .probe  = dvic_probe,
 332        .remove = __exit_p(dvic_remove),
 333        .driver = {
 334                .name   = "connector-dvi",
 335                .of_match_table = dvic_of_match,
 336                .suppress_bind_attrs = true,
 337        },
 338};
 339
 340module_platform_driver(dvi_connector_driver);
 341
 342MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 343MODULE_DESCRIPTION("Generic DVI Connector driver");
 344MODULE_LICENSE("GPL");
 345