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