linux/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2012 Texas Instruments
   4 * Author: Rob Clark <robdclark@gmail.com>
   5 */
   6
   7#include <linux/gpio.h>
   8#include <linux/mod_devicetable.h>
   9#include <linux/of_gpio.h>
  10#include <linux/pinctrl/consumer.h>
  11#include <linux/platform_device.h>
  12
  13#include <drm/drm_atomic_helper.h>
  14#include <drm/drm_encoder.h>
  15#include <drm/drm_modeset_helper_vtables.h>
  16#include <drm/drm_probe_helper.h>
  17
  18#include "tilcdc_drv.h"
  19#include "tilcdc_tfp410.h"
  20
  21struct tfp410_module {
  22        struct tilcdc_module base;
  23        struct i2c_adapter *i2c;
  24        int gpio;
  25};
  26#define to_tfp410_module(x) container_of(x, struct tfp410_module, base)
  27
  28
  29static const struct tilcdc_panel_info dvi_info = {
  30                .ac_bias                = 255,
  31                .ac_bias_intrpt         = 0,
  32                .dma_burst_sz           = 16,
  33                .bpp                    = 16,
  34                .fdd                    = 0x80,
  35                .tft_alt_mode           = 0,
  36                .sync_edge              = 0,
  37                .sync_ctrl              = 1,
  38                .raster_order           = 0,
  39};
  40
  41/*
  42 * Encoder:
  43 */
  44
  45struct tfp410_encoder {
  46        struct drm_encoder base;
  47        struct tfp410_module *mod;
  48        int dpms;
  49};
  50#define to_tfp410_encoder(x) container_of(x, struct tfp410_encoder, base)
  51
  52static void tfp410_encoder_dpms(struct drm_encoder *encoder, int mode)
  53{
  54        struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder);
  55
  56        if (tfp410_encoder->dpms == mode)
  57                return;
  58
  59        if (mode == DRM_MODE_DPMS_ON) {
  60                DBG("Power on");
  61                gpio_direction_output(tfp410_encoder->mod->gpio, 1);
  62        } else {
  63                DBG("Power off");
  64                gpio_direction_output(tfp410_encoder->mod->gpio, 0);
  65        }
  66
  67        tfp410_encoder->dpms = mode;
  68}
  69
  70static void tfp410_encoder_prepare(struct drm_encoder *encoder)
  71{
  72        tfp410_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
  73}
  74
  75static void tfp410_encoder_commit(struct drm_encoder *encoder)
  76{
  77        tfp410_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
  78}
  79
  80static void tfp410_encoder_mode_set(struct drm_encoder *encoder,
  81                struct drm_display_mode *mode,
  82                struct drm_display_mode *adjusted_mode)
  83{
  84        /* nothing needed */
  85}
  86
  87static const struct drm_encoder_funcs tfp410_encoder_funcs = {
  88                .destroy        = drm_encoder_cleanup,
  89};
  90
  91static const struct drm_encoder_helper_funcs tfp410_encoder_helper_funcs = {
  92                .dpms           = tfp410_encoder_dpms,
  93                .prepare        = tfp410_encoder_prepare,
  94                .commit         = tfp410_encoder_commit,
  95                .mode_set       = tfp410_encoder_mode_set,
  96};
  97
  98static struct drm_encoder *tfp410_encoder_create(struct drm_device *dev,
  99                struct tfp410_module *mod)
 100{
 101        struct tfp410_encoder *tfp410_encoder;
 102        struct drm_encoder *encoder;
 103        int ret;
 104
 105        tfp410_encoder = devm_kzalloc(dev->dev, sizeof(*tfp410_encoder),
 106                                      GFP_KERNEL);
 107        if (!tfp410_encoder)
 108                return NULL;
 109
 110        tfp410_encoder->dpms = DRM_MODE_DPMS_OFF;
 111        tfp410_encoder->mod = mod;
 112
 113        encoder = &tfp410_encoder->base;
 114        encoder->possible_crtcs = 1;
 115
 116        ret = drm_encoder_init(dev, encoder, &tfp410_encoder_funcs,
 117                        DRM_MODE_ENCODER_TMDS, NULL);
 118        if (ret < 0)
 119                goto fail;
 120
 121        drm_encoder_helper_add(encoder, &tfp410_encoder_helper_funcs);
 122
 123        return encoder;
 124
 125fail:
 126        drm_encoder_cleanup(encoder);
 127        return NULL;
 128}
 129
 130/*
 131 * Connector:
 132 */
 133
 134struct tfp410_connector {
 135        struct drm_connector base;
 136
 137        struct drm_encoder *encoder;  /* our connected encoder */
 138        struct tfp410_module *mod;
 139};
 140#define to_tfp410_connector(x) container_of(x, struct tfp410_connector, base)
 141
 142
 143static void tfp410_connector_destroy(struct drm_connector *connector)
 144{
 145        drm_connector_unregister(connector);
 146        drm_connector_cleanup(connector);
 147}
 148
 149static enum drm_connector_status tfp410_connector_detect(
 150                struct drm_connector *connector,
 151                bool force)
 152{
 153        struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector);
 154
 155        if (drm_probe_ddc(tfp410_connector->mod->i2c))
 156                return connector_status_connected;
 157
 158        return connector_status_unknown;
 159}
 160
 161static int tfp410_connector_get_modes(struct drm_connector *connector)
 162{
 163        struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector);
 164        struct edid *edid;
 165        int ret = 0;
 166
 167        edid = drm_get_edid(connector, tfp410_connector->mod->i2c);
 168
 169        drm_connector_update_edid_property(connector, edid);
 170
 171        if (edid) {
 172                ret = drm_add_edid_modes(connector, edid);
 173                kfree(edid);
 174        }
 175
 176        return ret;
 177}
 178
 179static struct drm_encoder *tfp410_connector_best_encoder(
 180                struct drm_connector *connector)
 181{
 182        struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector);
 183        return tfp410_connector->encoder;
 184}
 185
 186static const struct drm_connector_funcs tfp410_connector_funcs = {
 187        .destroy            = tfp410_connector_destroy,
 188        .detect             = tfp410_connector_detect,
 189        .fill_modes         = drm_helper_probe_single_connector_modes,
 190        .reset              = drm_atomic_helper_connector_reset,
 191        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 192        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 193};
 194
 195static const struct drm_connector_helper_funcs tfp410_connector_helper_funcs = {
 196        .get_modes          = tfp410_connector_get_modes,
 197        .best_encoder       = tfp410_connector_best_encoder,
 198};
 199
 200static struct drm_connector *tfp410_connector_create(struct drm_device *dev,
 201                struct tfp410_module *mod, struct drm_encoder *encoder)
 202{
 203        struct tfp410_connector *tfp410_connector;
 204        struct drm_connector *connector;
 205        int ret;
 206
 207        tfp410_connector = devm_kzalloc(dev->dev, sizeof(*tfp410_connector),
 208                                        GFP_KERNEL);
 209        if (!tfp410_connector)
 210                return NULL;
 211
 212        tfp410_connector->encoder = encoder;
 213        tfp410_connector->mod = mod;
 214
 215        connector = &tfp410_connector->base;
 216
 217        drm_connector_init(dev, connector, &tfp410_connector_funcs,
 218                        DRM_MODE_CONNECTOR_DVID);
 219        drm_connector_helper_add(connector, &tfp410_connector_helper_funcs);
 220
 221        connector->polled = DRM_CONNECTOR_POLL_CONNECT |
 222                        DRM_CONNECTOR_POLL_DISCONNECT;
 223
 224        connector->interlace_allowed = 0;
 225        connector->doublescan_allowed = 0;
 226
 227        ret = drm_connector_attach_encoder(connector, encoder);
 228        if (ret)
 229                goto fail;
 230
 231        return connector;
 232
 233fail:
 234        tfp410_connector_destroy(connector);
 235        return NULL;
 236}
 237
 238/*
 239 * Module:
 240 */
 241
 242static int tfp410_modeset_init(struct tilcdc_module *mod, struct drm_device *dev)
 243{
 244        struct tfp410_module *tfp410_mod = to_tfp410_module(mod);
 245        struct tilcdc_drm_private *priv = dev->dev_private;
 246        struct drm_encoder *encoder;
 247        struct drm_connector *connector;
 248
 249        encoder = tfp410_encoder_create(dev, tfp410_mod);
 250        if (!encoder)
 251                return -ENOMEM;
 252
 253        connector = tfp410_connector_create(dev, tfp410_mod, encoder);
 254        if (!connector)
 255                return -ENOMEM;
 256
 257        priv->encoders[priv->num_encoders++] = encoder;
 258        priv->connectors[priv->num_connectors++] = connector;
 259
 260        tilcdc_crtc_set_panel_info(priv->crtc, &dvi_info);
 261        return 0;
 262}
 263
 264static const struct tilcdc_module_ops tfp410_module_ops = {
 265                .modeset_init = tfp410_modeset_init,
 266};
 267
 268/*
 269 * Device:
 270 */
 271
 272static int tfp410_probe(struct platform_device *pdev)
 273{
 274        struct device_node *node = pdev->dev.of_node;
 275        struct device_node *i2c_node;
 276        struct tfp410_module *tfp410_mod;
 277        struct tilcdc_module *mod;
 278        struct pinctrl *pinctrl;
 279        uint32_t i2c_phandle;
 280        int ret = -EINVAL;
 281
 282        /* bail out early if no DT data: */
 283        if (!node) {
 284                dev_err(&pdev->dev, "device-tree data is missing\n");
 285                return -ENXIO;
 286        }
 287
 288        tfp410_mod = devm_kzalloc(&pdev->dev, sizeof(*tfp410_mod), GFP_KERNEL);
 289        if (!tfp410_mod)
 290                return -ENOMEM;
 291
 292        mod = &tfp410_mod->base;
 293        pdev->dev.platform_data = mod;
 294
 295        tilcdc_module_init(mod, "tfp410", &tfp410_module_ops);
 296
 297        pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
 298        if (IS_ERR(pinctrl))
 299                dev_warn(&pdev->dev, "pins are not configured\n");
 300
 301        if (of_property_read_u32(node, "i2c", &i2c_phandle)) {
 302                dev_err(&pdev->dev, "could not get i2c bus phandle\n");
 303                goto fail;
 304        }
 305
 306        i2c_node = of_find_node_by_phandle(i2c_phandle);
 307        if (!i2c_node) {
 308                dev_err(&pdev->dev, "could not get i2c bus node\n");
 309                goto fail;
 310        }
 311
 312        tfp410_mod->i2c = of_find_i2c_adapter_by_node(i2c_node);
 313        if (!tfp410_mod->i2c) {
 314                dev_err(&pdev->dev, "could not get i2c\n");
 315                of_node_put(i2c_node);
 316                goto fail;
 317        }
 318
 319        of_node_put(i2c_node);
 320
 321        tfp410_mod->gpio = of_get_named_gpio_flags(node, "powerdn-gpio",
 322                        0, NULL);
 323        if (tfp410_mod->gpio < 0) {
 324                dev_warn(&pdev->dev, "No power down GPIO\n");
 325        } else {
 326                ret = gpio_request(tfp410_mod->gpio, "DVI_PDn");
 327                if (ret) {
 328                        dev_err(&pdev->dev, "could not get DVI_PDn gpio\n");
 329                        goto fail_adapter;
 330                }
 331        }
 332
 333        return 0;
 334
 335fail_adapter:
 336        i2c_put_adapter(tfp410_mod->i2c);
 337
 338fail:
 339        tilcdc_module_cleanup(mod);
 340        return ret;
 341}
 342
 343static int tfp410_remove(struct platform_device *pdev)
 344{
 345        struct tilcdc_module *mod = dev_get_platdata(&pdev->dev);
 346        struct tfp410_module *tfp410_mod = to_tfp410_module(mod);
 347
 348        i2c_put_adapter(tfp410_mod->i2c);
 349        gpio_free(tfp410_mod->gpio);
 350
 351        tilcdc_module_cleanup(mod);
 352
 353        return 0;
 354}
 355
 356static const struct of_device_id tfp410_of_match[] = {
 357                { .compatible = "ti,tilcdc,tfp410", },
 358                { },
 359};
 360
 361struct platform_driver tfp410_driver = {
 362        .probe = tfp410_probe,
 363        .remove = tfp410_remove,
 364        .driver = {
 365                .owner = THIS_MODULE,
 366                .name = "tfp410",
 367                .of_match_table = tfp410_of_match,
 368        },
 369};
 370
 371int __init tilcdc_tfp410_init(void)
 372{
 373        return platform_driver_register(&tfp410_driver);
 374}
 375
 376void __exit tilcdc_tfp410_fini(void)
 377{
 378        platform_driver_unregister(&tfp410_driver);
 379}
 380