linux/drivers/video/omap2/displays/panel-tfp410.c
<<
>>
Prefs
   1/*
   2 * TFP410 DPI-to-DVI chip
   3 *
   4 * Copyright (C) 2011 Texas Instruments Inc
   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 * This program is distributed in the hope that it will be useful, but WITHOUT
  12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  14 * more details.
  15 *
  16 * You should have received a copy of the GNU General Public License along with
  17 * this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include <linux/module.h>
  21#include <linux/slab.h>
  22#include <video/omapdss.h>
  23#include <linux/i2c.h>
  24#include <linux/gpio.h>
  25#include <drm/drm_edid.h>
  26
  27#include <video/omap-panel-data.h>
  28
  29static const struct omap_video_timings tfp410_default_timings = {
  30        .x_res          = 640,
  31        .y_res          = 480,
  32
  33        .pixel_clock    = 23500,
  34
  35        .hfp            = 48,
  36        .hsw            = 32,
  37        .hbp            = 80,
  38
  39        .vfp            = 3,
  40        .vsw            = 4,
  41        .vbp            = 7,
  42
  43        .vsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
  44        .hsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
  45        .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
  46        .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
  47        .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
  48};
  49
  50struct panel_drv_data {
  51        struct omap_dss_device *dssdev;
  52
  53        struct mutex lock;
  54
  55        int pd_gpio;
  56
  57        struct i2c_adapter *i2c_adapter;
  58};
  59
  60static int tfp410_power_on(struct omap_dss_device *dssdev)
  61{
  62        struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
  63        int r;
  64
  65        if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
  66                return 0;
  67
  68        omapdss_dpi_set_timings(dssdev, &dssdev->panel.timings);
  69        omapdss_dpi_set_data_lines(dssdev, dssdev->phy.dpi.data_lines);
  70
  71        r = omapdss_dpi_display_enable(dssdev);
  72        if (r)
  73                goto err0;
  74
  75        if (gpio_is_valid(ddata->pd_gpio))
  76                gpio_set_value_cansleep(ddata->pd_gpio, 1);
  77
  78        return 0;
  79err0:
  80        return r;
  81}
  82
  83static void tfp410_power_off(struct omap_dss_device *dssdev)
  84{
  85        struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
  86
  87        if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
  88                return;
  89
  90        if (gpio_is_valid(ddata->pd_gpio))
  91                gpio_set_value_cansleep(ddata->pd_gpio, 0);
  92
  93        omapdss_dpi_display_disable(dssdev);
  94}
  95
  96static int tfp410_probe(struct omap_dss_device *dssdev)
  97{
  98        struct panel_drv_data *ddata;
  99        int r;
 100        int i2c_bus_num;
 101
 102        ddata = devm_kzalloc(&dssdev->dev, sizeof(*ddata), GFP_KERNEL);
 103        if (!ddata)
 104                return -ENOMEM;
 105
 106        dssdev->panel.timings = tfp410_default_timings;
 107
 108        ddata->dssdev = dssdev;
 109        mutex_init(&ddata->lock);
 110
 111        if (dssdev->data) {
 112                struct tfp410_platform_data *pdata = dssdev->data;
 113
 114                ddata->pd_gpio = pdata->power_down_gpio;
 115                i2c_bus_num = pdata->i2c_bus_num;
 116        } else {
 117                ddata->pd_gpio = -1;
 118                i2c_bus_num = -1;
 119        }
 120
 121        if (gpio_is_valid(ddata->pd_gpio)) {
 122                r = devm_gpio_request_one(&dssdev->dev, ddata->pd_gpio,
 123                                GPIOF_OUT_INIT_LOW, "tfp410 pd");
 124                if (r) {
 125                        dev_err(&dssdev->dev, "Failed to request PD GPIO %d\n",
 126                                        ddata->pd_gpio);
 127                        return r;
 128                }
 129        }
 130
 131        if (i2c_bus_num != -1) {
 132                struct i2c_adapter *adapter;
 133
 134                adapter = i2c_get_adapter(i2c_bus_num);
 135                if (!adapter) {
 136                        dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n",
 137                                        i2c_bus_num);
 138                        return -EPROBE_DEFER;
 139                }
 140
 141                ddata->i2c_adapter = adapter;
 142        }
 143
 144        dev_set_drvdata(&dssdev->dev, ddata);
 145
 146        return 0;
 147}
 148
 149static void __exit tfp410_remove(struct omap_dss_device *dssdev)
 150{
 151        struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
 152
 153        mutex_lock(&ddata->lock);
 154
 155        if (ddata->i2c_adapter)
 156                i2c_put_adapter(ddata->i2c_adapter);
 157
 158        dev_set_drvdata(&dssdev->dev, NULL);
 159
 160        mutex_unlock(&ddata->lock);
 161}
 162
 163static int tfp410_enable(struct omap_dss_device *dssdev)
 164{
 165        struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
 166        int r;
 167
 168        mutex_lock(&ddata->lock);
 169
 170        r = tfp410_power_on(dssdev);
 171        if (r == 0)
 172                dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 173
 174        mutex_unlock(&ddata->lock);
 175
 176        return r;
 177}
 178
 179static void tfp410_disable(struct omap_dss_device *dssdev)
 180{
 181        struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
 182
 183        mutex_lock(&ddata->lock);
 184
 185        tfp410_power_off(dssdev);
 186
 187        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 188
 189        mutex_unlock(&ddata->lock);
 190}
 191
 192static void tfp410_set_timings(struct omap_dss_device *dssdev,
 193                struct omap_video_timings *timings)
 194{
 195        struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
 196
 197        mutex_lock(&ddata->lock);
 198        omapdss_dpi_set_timings(dssdev, timings);
 199        dssdev->panel.timings = *timings;
 200        mutex_unlock(&ddata->lock);
 201}
 202
 203static void tfp410_get_timings(struct omap_dss_device *dssdev,
 204                struct omap_video_timings *timings)
 205{
 206        struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
 207
 208        mutex_lock(&ddata->lock);
 209        *timings = dssdev->panel.timings;
 210        mutex_unlock(&ddata->lock);
 211}
 212
 213static int tfp410_check_timings(struct omap_dss_device *dssdev,
 214                struct omap_video_timings *timings)
 215{
 216        struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
 217        int r;
 218
 219        mutex_lock(&ddata->lock);
 220        r = dpi_check_timings(dssdev, timings);
 221        mutex_unlock(&ddata->lock);
 222
 223        return r;
 224}
 225
 226
 227static int tfp410_ddc_read(struct i2c_adapter *adapter,
 228                unsigned char *buf, u16 count, u8 offset)
 229{
 230        int r, retries;
 231
 232        for (retries = 3; retries > 0; retries--) {
 233                struct i2c_msg msgs[] = {
 234                        {
 235                                .addr   = DDC_ADDR,
 236                                .flags  = 0,
 237                                .len    = 1,
 238                                .buf    = &offset,
 239                        }, {
 240                                .addr   = DDC_ADDR,
 241                                .flags  = I2C_M_RD,
 242                                .len    = count,
 243                                .buf    = buf,
 244                        }
 245                };
 246
 247                r = i2c_transfer(adapter, msgs, 2);
 248                if (r == 2)
 249                        return 0;
 250
 251                if (r != -EAGAIN)
 252                        break;
 253        }
 254
 255        return r < 0 ? r : -EIO;
 256}
 257
 258static int tfp410_read_edid(struct omap_dss_device *dssdev,
 259                u8 *edid, int len)
 260{
 261        struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
 262        int r, l, bytes_read;
 263
 264        mutex_lock(&ddata->lock);
 265
 266        if (!ddata->i2c_adapter) {
 267                r = -ENODEV;
 268                goto err;
 269        }
 270
 271        l = min(EDID_LENGTH, len);
 272        r = tfp410_ddc_read(ddata->i2c_adapter, edid, l, 0);
 273        if (r)
 274                goto err;
 275
 276        bytes_read = l;
 277
 278        /* if there are extensions, read second block */
 279        if (len > EDID_LENGTH && edid[0x7e] > 0) {
 280                l = min(EDID_LENGTH, len - EDID_LENGTH);
 281
 282                r = tfp410_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH,
 283                                l, EDID_LENGTH);
 284                if (r)
 285                        goto err;
 286
 287                bytes_read += l;
 288        }
 289
 290        mutex_unlock(&ddata->lock);
 291
 292        return bytes_read;
 293
 294err:
 295        mutex_unlock(&ddata->lock);
 296        return r;
 297}
 298
 299static bool tfp410_detect(struct omap_dss_device *dssdev)
 300{
 301        struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
 302        unsigned char out;
 303        int r;
 304
 305        mutex_lock(&ddata->lock);
 306
 307        if (!ddata->i2c_adapter)
 308                goto out;
 309
 310        r = tfp410_ddc_read(ddata->i2c_adapter, &out, 1, 0);
 311
 312        mutex_unlock(&ddata->lock);
 313
 314        return r == 0;
 315
 316out:
 317        mutex_unlock(&ddata->lock);
 318        return true;
 319}
 320
 321static struct omap_dss_driver tfp410_driver = {
 322        .probe          = tfp410_probe,
 323        .remove         = __exit_p(tfp410_remove),
 324
 325        .enable         = tfp410_enable,
 326        .disable        = tfp410_disable,
 327
 328        .set_timings    = tfp410_set_timings,
 329        .get_timings    = tfp410_get_timings,
 330        .check_timings  = tfp410_check_timings,
 331
 332        .read_edid      = tfp410_read_edid,
 333        .detect         = tfp410_detect,
 334
 335        .driver         = {
 336                .name   = "tfp410",
 337                .owner  = THIS_MODULE,
 338        },
 339};
 340
 341static int __init tfp410_init(void)
 342{
 343        return omap_dss_register_driver(&tfp410_driver);
 344}
 345
 346static void __exit tfp410_exit(void)
 347{
 348        omap_dss_unregister_driver(&tfp410_driver);
 349}
 350
 351module_init(tfp410_init);
 352module_exit(tfp410_exit);
 353MODULE_LICENSE("GPL");
 354