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