linux/drivers/gpu/drm/bridge/ti-tfp410.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2016 Texas Instruments
   4 * Author: Jyri Sarha <jsarha@ti.com>
   5 */
   6
   7#include <linux/delay.h>
   8#include <linux/fwnode.h>
   9#include <linux/gpio/consumer.h>
  10#include <linux/irq.h>
  11#include <linux/module.h>
  12#include <linux/of_graph.h>
  13#include <linux/platform_device.h>
  14#include <linux/i2c.h>
  15
  16#include <drm/drmP.h>
  17#include <drm/drm_atomic_helper.h>
  18#include <drm/drm_crtc.h>
  19#include <drm/drm_probe_helper.h>
  20
  21#define HOTPLUG_DEBOUNCE_MS             1100
  22
  23struct tfp410 {
  24        struct drm_bridge       bridge;
  25        struct drm_connector    connector;
  26        unsigned int            connector_type;
  27
  28        u32                     bus_format;
  29        struct i2c_adapter      *ddc;
  30        struct gpio_desc        *hpd;
  31        int                     hpd_irq;
  32        struct delayed_work     hpd_work;
  33        struct gpio_desc        *powerdown;
  34
  35        struct drm_bridge_timings timings;
  36
  37        struct device *dev;
  38};
  39
  40static inline struct tfp410 *
  41drm_bridge_to_tfp410(struct drm_bridge *bridge)
  42{
  43        return container_of(bridge, struct tfp410, bridge);
  44}
  45
  46static inline struct tfp410 *
  47drm_connector_to_tfp410(struct drm_connector *connector)
  48{
  49        return container_of(connector, struct tfp410, connector);
  50}
  51
  52static int tfp410_get_modes(struct drm_connector *connector)
  53{
  54        struct tfp410 *dvi = drm_connector_to_tfp410(connector);
  55        struct edid *edid;
  56        int ret;
  57
  58        if (!dvi->ddc)
  59                goto fallback;
  60
  61        edid = drm_get_edid(connector, dvi->ddc);
  62        if (!edid) {
  63                DRM_INFO("EDID read failed. Fallback to standard modes\n");
  64                goto fallback;
  65        }
  66
  67        drm_connector_update_edid_property(connector, edid);
  68
  69        return drm_add_edid_modes(connector, edid);
  70fallback:
  71        /* No EDID, fallback on the XGA standard modes */
  72        ret = drm_add_modes_noedid(connector, 1920, 1200);
  73
  74        /* And prefer a mode pretty much anything can handle */
  75        drm_set_preferred_mode(connector, 1024, 768);
  76
  77        return ret;
  78}
  79
  80static const struct drm_connector_helper_funcs tfp410_con_helper_funcs = {
  81        .get_modes      = tfp410_get_modes,
  82};
  83
  84static enum drm_connector_status
  85tfp410_connector_detect(struct drm_connector *connector, bool force)
  86{
  87        struct tfp410 *dvi = drm_connector_to_tfp410(connector);
  88
  89        if (dvi->hpd) {
  90                if (gpiod_get_value_cansleep(dvi->hpd))
  91                        return connector_status_connected;
  92                else
  93                        return connector_status_disconnected;
  94        }
  95
  96        if (dvi->ddc) {
  97                if (drm_probe_ddc(dvi->ddc))
  98                        return connector_status_connected;
  99                else
 100                        return connector_status_disconnected;
 101        }
 102
 103        return connector_status_unknown;
 104}
 105
 106static const struct drm_connector_funcs tfp410_con_funcs = {
 107        .detect                 = tfp410_connector_detect,
 108        .fill_modes             = drm_helper_probe_single_connector_modes,
 109        .destroy                = drm_connector_cleanup,
 110        .reset                  = drm_atomic_helper_connector_reset,
 111        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 112        .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
 113};
 114
 115static int tfp410_attach(struct drm_bridge *bridge)
 116{
 117        struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
 118        int ret;
 119
 120        if (!bridge->encoder) {
 121                dev_err(dvi->dev, "Missing encoder\n");
 122                return -ENODEV;
 123        }
 124
 125        if (dvi->hpd_irq >= 0)
 126                dvi->connector.polled = DRM_CONNECTOR_POLL_HPD;
 127        else
 128                dvi->connector.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
 129
 130        drm_connector_helper_add(&dvi->connector,
 131                                 &tfp410_con_helper_funcs);
 132        ret = drm_connector_init(bridge->dev, &dvi->connector,
 133                                 &tfp410_con_funcs, dvi->connector_type);
 134        if (ret) {
 135                dev_err(dvi->dev, "drm_connector_init() failed: %d\n", ret);
 136                return ret;
 137        }
 138
 139        drm_display_info_set_bus_formats(&dvi->connector.display_info,
 140                                         &dvi->bus_format, 1);
 141
 142        drm_connector_attach_encoder(&dvi->connector,
 143                                          bridge->encoder);
 144
 145        return 0;
 146}
 147
 148static void tfp410_enable(struct drm_bridge *bridge)
 149{
 150        struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
 151
 152        gpiod_set_value_cansleep(dvi->powerdown, 0);
 153}
 154
 155static void tfp410_disable(struct drm_bridge *bridge)
 156{
 157        struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
 158
 159        gpiod_set_value_cansleep(dvi->powerdown, 1);
 160}
 161
 162static const struct drm_bridge_funcs tfp410_bridge_funcs = {
 163        .attach         = tfp410_attach,
 164        .enable         = tfp410_enable,
 165        .disable        = tfp410_disable,
 166};
 167
 168static void tfp410_hpd_work_func(struct work_struct *work)
 169{
 170        struct tfp410 *dvi;
 171
 172        dvi = container_of(work, struct tfp410, hpd_work.work);
 173
 174        if (dvi->bridge.dev)
 175                drm_helper_hpd_irq_event(dvi->bridge.dev);
 176}
 177
 178static irqreturn_t tfp410_hpd_irq_thread(int irq, void *arg)
 179{
 180        struct tfp410 *dvi = arg;
 181
 182        mod_delayed_work(system_wq, &dvi->hpd_work,
 183                        msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
 184
 185        return IRQ_HANDLED;
 186}
 187
 188static const struct drm_bridge_timings tfp410_default_timings = {
 189        .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE
 190                         | DRM_BUS_FLAG_DE_HIGH,
 191        .setup_time_ps = 1200,
 192        .hold_time_ps = 1300,
 193};
 194
 195static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c)
 196{
 197        struct drm_bridge_timings *timings = &dvi->timings;
 198        struct device_node *ep;
 199        u32 pclk_sample = 0;
 200        u32 bus_width = 24;
 201        s32 deskew = 0;
 202
 203        /* Start with defaults. */
 204        *timings = tfp410_default_timings;
 205
 206        if (i2c)
 207                /*
 208                 * In I2C mode timings are configured through the I2C interface.
 209                 * As the driver doesn't support I2C configuration yet, we just
 210                 * go with the defaults (BSEL=1, DSEL=1, DKEN=0, EDGE=1).
 211                 */
 212                return 0;
 213
 214        /*
 215         * In non-I2C mode, timings are configured through the BSEL, DSEL, DKEN
 216         * and EDGE pins. They are specified in DT through endpoint properties
 217         * and vendor-specific properties.
 218         */
 219        ep = of_graph_get_endpoint_by_regs(dvi->dev->of_node, 0, 0);
 220        if (!ep)
 221                return -EINVAL;
 222
 223        /* Get the sampling edge from the endpoint. */
 224        of_property_read_u32(ep, "pclk-sample", &pclk_sample);
 225        of_property_read_u32(ep, "bus-width", &bus_width);
 226        of_node_put(ep);
 227
 228        timings->input_bus_flags = DRM_BUS_FLAG_DE_HIGH;
 229
 230        switch (pclk_sample) {
 231        case 0:
 232                timings->input_bus_flags |= DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE
 233                                         |  DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE;
 234                break;
 235        case 1:
 236                timings->input_bus_flags |= DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE
 237                                         |  DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE;
 238                break;
 239        default:
 240                return -EINVAL;
 241        }
 242
 243        switch (bus_width) {
 244        case 12:
 245                dvi->bus_format = MEDIA_BUS_FMT_RGB888_2X12_LE;
 246                break;
 247        case 24:
 248                dvi->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
 249                break;
 250        default:
 251                return -EINVAL;
 252        }
 253
 254        /* Get the setup and hold time from vendor-specific properties. */
 255        of_property_read_u32(dvi->dev->of_node, "ti,deskew", (u32 *)&deskew);
 256        if (deskew < -4 || deskew > 3)
 257                return -EINVAL;
 258
 259        timings->setup_time_ps = min(0, 1200 - 350 * deskew);
 260        timings->hold_time_ps = min(0, 1300 + 350 * deskew);
 261
 262        return 0;
 263}
 264
 265static int tfp410_get_connector_properties(struct tfp410 *dvi)
 266{
 267        struct device_node *connector_node, *ddc_phandle;
 268        int ret = 0;
 269
 270        /* port@1 is the connector node */
 271        connector_node = of_graph_get_remote_node(dvi->dev->of_node, 1, -1);
 272        if (!connector_node)
 273                return -ENODEV;
 274
 275        if (of_device_is_compatible(connector_node, "hdmi-connector"))
 276                dvi->connector_type = DRM_MODE_CONNECTOR_HDMIA;
 277        else
 278                dvi->connector_type = DRM_MODE_CONNECTOR_DVID;
 279
 280        dvi->hpd = fwnode_get_named_gpiod(&connector_node->fwnode,
 281                                        "hpd-gpios", 0, GPIOD_IN, "hpd");
 282        if (IS_ERR(dvi->hpd)) {
 283                ret = PTR_ERR(dvi->hpd);
 284                dvi->hpd = NULL;
 285                if (ret == -ENOENT)
 286                        ret = 0;
 287                else
 288                        goto fail;
 289        }
 290
 291        ddc_phandle = of_parse_phandle(connector_node, "ddc-i2c-bus", 0);
 292        if (!ddc_phandle)
 293                goto fail;
 294
 295        dvi->ddc = of_get_i2c_adapter_by_node(ddc_phandle);
 296        if (dvi->ddc)
 297                dev_info(dvi->dev, "Connector's ddc i2c bus found\n");
 298        else
 299                ret = -EPROBE_DEFER;
 300
 301        of_node_put(ddc_phandle);
 302
 303fail:
 304        of_node_put(connector_node);
 305        return ret;
 306}
 307
 308static int tfp410_init(struct device *dev, bool i2c)
 309{
 310        struct tfp410 *dvi;
 311        int ret;
 312
 313        if (!dev->of_node) {
 314                dev_err(dev, "device-tree data is missing\n");
 315                return -ENXIO;
 316        }
 317
 318        dvi = devm_kzalloc(dev, sizeof(*dvi), GFP_KERNEL);
 319        if (!dvi)
 320                return -ENOMEM;
 321        dev_set_drvdata(dev, dvi);
 322
 323        dvi->bridge.funcs = &tfp410_bridge_funcs;
 324        dvi->bridge.of_node = dev->of_node;
 325        dvi->bridge.timings = &dvi->timings;
 326        dvi->dev = dev;
 327
 328        ret = tfp410_parse_timings(dvi, i2c);
 329        if (ret)
 330                goto fail;
 331
 332        ret = tfp410_get_connector_properties(dvi);
 333        if (ret)
 334                goto fail;
 335
 336        dvi->powerdown = devm_gpiod_get_optional(dev, "powerdown",
 337                                                 GPIOD_OUT_HIGH);
 338        if (IS_ERR(dvi->powerdown)) {
 339                dev_err(dev, "failed to parse powerdown gpio\n");
 340                return PTR_ERR(dvi->powerdown);
 341        }
 342
 343        if (dvi->hpd)
 344                dvi->hpd_irq = gpiod_to_irq(dvi->hpd);
 345        else
 346                dvi->hpd_irq = -ENXIO;
 347
 348        if (dvi->hpd_irq >= 0) {
 349                INIT_DELAYED_WORK(&dvi->hpd_work, tfp410_hpd_work_func);
 350
 351                ret = devm_request_threaded_irq(dev, dvi->hpd_irq,
 352                        NULL, tfp410_hpd_irq_thread, IRQF_TRIGGER_RISING |
 353                        IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
 354                        "hdmi-hpd", dvi);
 355                if (ret) {
 356                        DRM_ERROR("failed to register hpd interrupt\n");
 357                        goto fail;
 358                }
 359        }
 360
 361        drm_bridge_add(&dvi->bridge);
 362
 363        return 0;
 364fail:
 365        i2c_put_adapter(dvi->ddc);
 366        if (dvi->hpd)
 367                gpiod_put(dvi->hpd);
 368        return ret;
 369}
 370
 371static int tfp410_fini(struct device *dev)
 372{
 373        struct tfp410 *dvi = dev_get_drvdata(dev);
 374
 375        cancel_delayed_work_sync(&dvi->hpd_work);
 376
 377        drm_bridge_remove(&dvi->bridge);
 378
 379        if (dvi->ddc)
 380                i2c_put_adapter(dvi->ddc);
 381        if (dvi->hpd)
 382                gpiod_put(dvi->hpd);
 383
 384        return 0;
 385}
 386
 387static int tfp410_probe(struct platform_device *pdev)
 388{
 389        return tfp410_init(&pdev->dev, false);
 390}
 391
 392static int tfp410_remove(struct platform_device *pdev)
 393{
 394        return tfp410_fini(&pdev->dev);
 395}
 396
 397static const struct of_device_id tfp410_match[] = {
 398        { .compatible = "ti,tfp410" },
 399        {},
 400};
 401MODULE_DEVICE_TABLE(of, tfp410_match);
 402
 403static struct platform_driver tfp410_platform_driver = {
 404        .probe  = tfp410_probe,
 405        .remove = tfp410_remove,
 406        .driver = {
 407                .name           = "tfp410-bridge",
 408                .of_match_table = tfp410_match,
 409        },
 410};
 411
 412#if IS_ENABLED(CONFIG_I2C)
 413/* There is currently no i2c functionality. */
 414static int tfp410_i2c_probe(struct i2c_client *client,
 415                            const struct i2c_device_id *id)
 416{
 417        int reg;
 418
 419        if (!client->dev.of_node ||
 420            of_property_read_u32(client->dev.of_node, "reg", &reg)) {
 421                dev_err(&client->dev,
 422                        "Can't get i2c reg property from device-tree\n");
 423                return -ENXIO;
 424        }
 425
 426        return tfp410_init(&client->dev, true);
 427}
 428
 429static int tfp410_i2c_remove(struct i2c_client *client)
 430{
 431        return tfp410_fini(&client->dev);
 432}
 433
 434static const struct i2c_device_id tfp410_i2c_ids[] = {
 435        { "tfp410", 0 },
 436        { }
 437};
 438MODULE_DEVICE_TABLE(i2c, tfp410_i2c_ids);
 439
 440static struct i2c_driver tfp410_i2c_driver = {
 441        .driver = {
 442                .name   = "tfp410",
 443                .of_match_table = of_match_ptr(tfp410_match),
 444        },
 445        .id_table       = tfp410_i2c_ids,
 446        .probe          = tfp410_i2c_probe,
 447        .remove         = tfp410_i2c_remove,
 448};
 449#endif /* IS_ENABLED(CONFIG_I2C) */
 450
 451static struct {
 452        uint i2c:1;
 453        uint platform:1;
 454}  tfp410_registered_driver;
 455
 456static int __init tfp410_module_init(void)
 457{
 458        int ret;
 459
 460#if IS_ENABLED(CONFIG_I2C)
 461        ret = i2c_add_driver(&tfp410_i2c_driver);
 462        if (ret)
 463                pr_err("%s: registering i2c driver failed: %d",
 464                       __func__, ret);
 465        else
 466                tfp410_registered_driver.i2c = 1;
 467#endif
 468
 469        ret = platform_driver_register(&tfp410_platform_driver);
 470        if (ret)
 471                pr_err("%s: registering platform driver failed: %d",
 472                       __func__, ret);
 473        else
 474                tfp410_registered_driver.platform = 1;
 475
 476        if (tfp410_registered_driver.i2c ||
 477            tfp410_registered_driver.platform)
 478                return 0;
 479
 480        return ret;
 481}
 482module_init(tfp410_module_init);
 483
 484static void __exit tfp410_module_exit(void)
 485{
 486#if IS_ENABLED(CONFIG_I2C)
 487        if (tfp410_registered_driver.i2c)
 488                i2c_del_driver(&tfp410_i2c_driver);
 489#endif
 490        if (tfp410_registered_driver.platform)
 491                platform_driver_unregister(&tfp410_platform_driver);
 492}
 493module_exit(tfp410_module_exit);
 494
 495MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
 496MODULE_DESCRIPTION("TI TFP410 DVI bridge driver");
 497MODULE_LICENSE("GPL");
 498