linux/drivers/gpu/drm/omapdrm/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_of(struct platform_device *pdev)
 240{
 241        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 242        struct device_node *node = pdev->dev.of_node;
 243        struct omap_dss_device *in;
 244        struct device_node *adapter_node;
 245        struct i2c_adapter *adapter;
 246
 247        in = omapdss_of_find_source_for_first_ep(node);
 248        if (IS_ERR(in)) {
 249                dev_err(&pdev->dev, "failed to find video source\n");
 250                return PTR_ERR(in);
 251        }
 252
 253        ddata->in = in;
 254
 255        adapter_node = of_parse_phandle(node, "ddc-i2c-bus", 0);
 256        if (adapter_node) {
 257                adapter = of_get_i2c_adapter_by_node(adapter_node);
 258                if (adapter == NULL) {
 259                        dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n");
 260                        omap_dss_put_device(ddata->in);
 261                        return -EPROBE_DEFER;
 262                }
 263
 264                ddata->i2c_adapter = adapter;
 265        }
 266
 267        return 0;
 268}
 269
 270static int dvic_probe(struct platform_device *pdev)
 271{
 272        struct panel_drv_data *ddata;
 273        struct omap_dss_device *dssdev;
 274        int r;
 275
 276        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
 277        if (!ddata)
 278                return -ENOMEM;
 279
 280        platform_set_drvdata(pdev, ddata);
 281
 282        if (!pdev->dev.of_node)
 283                return -ENODEV;
 284
 285        r = dvic_probe_of(pdev);
 286        if (r)
 287                return r;
 288
 289        ddata->timings = dvic_default_timings;
 290
 291        dssdev = &ddata->dssdev;
 292        dssdev->driver = &dvic_driver;
 293        dssdev->dev = &pdev->dev;
 294        dssdev->type = OMAP_DISPLAY_TYPE_DVI;
 295        dssdev->owner = THIS_MODULE;
 296        dssdev->panel.timings = dvic_default_timings;
 297
 298        r = omapdss_register_display(dssdev);
 299        if (r) {
 300                dev_err(&pdev->dev, "Failed to register panel\n");
 301                goto err_reg;
 302        }
 303
 304        return 0;
 305
 306err_reg:
 307        omap_dss_put_device(ddata->in);
 308
 309        i2c_put_adapter(ddata->i2c_adapter);
 310
 311        return r;
 312}
 313
 314static int __exit dvic_remove(struct platform_device *pdev)
 315{
 316        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 317        struct omap_dss_device *dssdev = &ddata->dssdev;
 318        struct omap_dss_device *in = ddata->in;
 319
 320        omapdss_unregister_display(&ddata->dssdev);
 321
 322        dvic_disable(dssdev);
 323        dvic_disconnect(dssdev);
 324
 325        omap_dss_put_device(in);
 326
 327        i2c_put_adapter(ddata->i2c_adapter);
 328
 329        return 0;
 330}
 331
 332static const struct of_device_id dvic_of_match[] = {
 333        { .compatible = "omapdss,dvi-connector", },
 334        {},
 335};
 336
 337MODULE_DEVICE_TABLE(of, dvic_of_match);
 338
 339static struct platform_driver dvi_connector_driver = {
 340        .probe  = dvic_probe,
 341        .remove = __exit_p(dvic_remove),
 342        .driver = {
 343                .name   = "connector-dvi",
 344                .of_match_table = dvic_of_match,
 345                .suppress_bind_attrs = true,
 346        },
 347};
 348
 349module_platform_driver(dvi_connector_driver);
 350
 351MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 352MODULE_DESCRIPTION("Generic DVI Connector driver");
 353MODULE_LICENSE("GPL");
 354