linux/drivers/gpu/drm/exynos/exynos_dp.c
<<
>>
Prefs
   1/*
   2 * Samsung SoC DP (Display Port) interface driver.
   3 *
   4 * Copyright (C) 2012 Samsung Electronics Co., Ltd.
   5 * Author: Jingoo Han <jg1.han@samsung.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 as published by the
   9 * Free Software Foundation; either version 2 of the License, or (at your
  10 * option) any later version.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/platform_device.h>
  15#include <linux/err.h>
  16#include <linux/clk.h>
  17#include <linux/of_graph.h>
  18#include <linux/component.h>
  19#include <video/of_display_timing.h>
  20#include <video/of_videomode.h>
  21#include <video/videomode.h>
  22
  23#include <drm/drmP.h>
  24#include <drm/drm_crtc.h>
  25#include <drm/drm_crtc_helper.h>
  26#include <drm/drm_panel.h>
  27
  28#include <drm/bridge/analogix_dp.h>
  29#include <drm/exynos_drm.h>
  30
  31#include "exynos_drm_crtc.h"
  32
  33#define to_dp(nm)       container_of(nm, struct exynos_dp_device, nm)
  34
  35struct exynos_dp_device {
  36        struct drm_encoder         encoder;
  37        struct drm_connector       *connector;
  38        struct drm_bridge          *ptn_bridge;
  39        struct drm_device          *drm_dev;
  40        struct device              *dev;
  41
  42        struct videomode           vm;
  43        struct analogix_dp_plat_data plat_data;
  44};
  45
  46static int exynos_dp_crtc_clock_enable(struct analogix_dp_plat_data *plat_data,
  47                                bool enable)
  48{
  49        struct exynos_dp_device *dp = to_dp(plat_data);
  50        struct drm_encoder *encoder = &dp->encoder;
  51
  52        if (!encoder->crtc)
  53                return -EPERM;
  54
  55        exynos_drm_pipe_clk_enable(to_exynos_crtc(encoder->crtc), enable);
  56
  57        return 0;
  58}
  59
  60static int exynos_dp_poweron(struct analogix_dp_plat_data *plat_data)
  61{
  62        return exynos_dp_crtc_clock_enable(plat_data, true);
  63}
  64
  65static int exynos_dp_poweroff(struct analogix_dp_plat_data *plat_data)
  66{
  67        return exynos_dp_crtc_clock_enable(plat_data, false);
  68}
  69
  70static int exynos_dp_get_modes(struct analogix_dp_plat_data *plat_data,
  71                               struct drm_connector *connector)
  72{
  73        struct exynos_dp_device *dp = to_dp(plat_data);
  74        struct drm_display_mode *mode;
  75        int num_modes = 0;
  76
  77        if (dp->plat_data.panel)
  78                return num_modes;
  79
  80        mode = drm_mode_create(connector->dev);
  81        if (!mode) {
  82                DRM_ERROR("failed to create a new display mode.\n");
  83                return num_modes;
  84        }
  85
  86        drm_display_mode_from_videomode(&dp->vm, mode);
  87        connector->display_info.width_mm = mode->width_mm;
  88        connector->display_info.height_mm = mode->height_mm;
  89
  90        mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
  91        drm_mode_set_name(mode);
  92        drm_mode_probed_add(connector, mode);
  93
  94        return num_modes + 1;
  95}
  96
  97static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data,
  98                                   struct drm_bridge *bridge,
  99                                   struct drm_connector *connector)
 100{
 101        struct exynos_dp_device *dp = to_dp(plat_data);
 102        struct drm_encoder *encoder = &dp->encoder;
 103        int ret;
 104
 105        drm_connector_register(connector);
 106        dp->connector = connector;
 107
 108        /* Pre-empt DP connector creation if there's a bridge */
 109        if (dp->ptn_bridge) {
 110                bridge->next = dp->ptn_bridge;
 111                dp->ptn_bridge->encoder = encoder;
 112                ret = drm_bridge_attach(encoder->dev, dp->ptn_bridge);
 113                if (ret) {
 114                        DRM_ERROR("Failed to attach bridge to drm\n");
 115                        bridge->next = NULL;
 116                        return ret;
 117                }
 118        }
 119
 120        return 0;
 121}
 122
 123static void exynos_dp_mode_set(struct drm_encoder *encoder,
 124                               struct drm_display_mode *mode,
 125                               struct drm_display_mode *adjusted_mode)
 126{
 127}
 128
 129static void exynos_dp_nop(struct drm_encoder *encoder)
 130{
 131        /* do nothing */
 132}
 133
 134static const struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = {
 135        .mode_set = exynos_dp_mode_set,
 136        .enable = exynos_dp_nop,
 137        .disable = exynos_dp_nop,
 138};
 139
 140static const struct drm_encoder_funcs exynos_dp_encoder_funcs = {
 141        .destroy = drm_encoder_cleanup,
 142};
 143
 144static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
 145{
 146        int ret;
 147
 148        ret = of_get_videomode(dp->dev->of_node, &dp->vm, OF_USE_NATIVE_MODE);
 149        if (ret) {
 150                DRM_ERROR("failed: of_get_videomode() : %d\n", ret);
 151                return ret;
 152        }
 153        return 0;
 154}
 155
 156static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 157{
 158        struct exynos_dp_device *dp = dev_get_drvdata(dev);
 159        struct drm_encoder *encoder = &dp->encoder;
 160        struct drm_device *drm_dev = data;
 161        int pipe, ret;
 162
 163        /*
 164         * Just like the probe function said, we don't need the
 165         * device drvrate anymore, we should leave the charge to
 166         * analogix dp driver, set the device drvdata to NULL.
 167         */
 168        dev_set_drvdata(dev, NULL);
 169
 170        dp->dev = dev;
 171        dp->drm_dev = drm_dev;
 172
 173        dp->plat_data.dev_type = EXYNOS_DP;
 174        dp->plat_data.power_on = exynos_dp_poweron;
 175        dp->plat_data.power_off = exynos_dp_poweroff;
 176        dp->plat_data.attach = exynos_dp_bridge_attach;
 177        dp->plat_data.get_modes = exynos_dp_get_modes;
 178
 179        if (!dp->plat_data.panel && !dp->ptn_bridge) {
 180                ret = exynos_dp_dt_parse_panel(dp);
 181                if (ret)
 182                        return ret;
 183        }
 184
 185        pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
 186                                                  EXYNOS_DISPLAY_TYPE_LCD);
 187        if (pipe < 0)
 188                return pipe;
 189
 190        encoder->possible_crtcs = 1 << pipe;
 191
 192        DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
 193
 194        drm_encoder_init(drm_dev, encoder, &exynos_dp_encoder_funcs,
 195                         DRM_MODE_ENCODER_TMDS, NULL);
 196
 197        drm_encoder_helper_add(encoder, &exynos_dp_encoder_helper_funcs);
 198
 199        dp->plat_data.encoder = encoder;
 200
 201        return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
 202}
 203
 204static void exynos_dp_unbind(struct device *dev, struct device *master,
 205                             void *data)
 206{
 207        return analogix_dp_unbind(dev, master, data);
 208}
 209
 210static const struct component_ops exynos_dp_ops = {
 211        .bind   = exynos_dp_bind,
 212        .unbind = exynos_dp_unbind,
 213};
 214
 215static int exynos_dp_probe(struct platform_device *pdev)
 216{
 217        struct device *dev = &pdev->dev;
 218        struct device_node *np = NULL, *endpoint = NULL;
 219        struct exynos_dp_device *dp;
 220
 221        dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
 222                          GFP_KERNEL);
 223        if (!dp)
 224                return -ENOMEM;
 225
 226        /*
 227         * We just use the drvdata until driver run into component
 228         * add function, and then we would set drvdata to null, so
 229         * that analogix dp driver would take charge of the drvdata.
 230         */
 231        platform_set_drvdata(pdev, dp);
 232
 233        /* This is for the backward compatibility. */
 234        np = of_parse_phandle(dev->of_node, "panel", 0);
 235        if (np) {
 236                dp->plat_data.panel = of_drm_find_panel(np);
 237                of_node_put(np);
 238                if (!dp->plat_data.panel)
 239                        return -EPROBE_DEFER;
 240                goto out;
 241        }
 242
 243        endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
 244        if (endpoint) {
 245                np = of_graph_get_remote_port_parent(endpoint);
 246                if (np) {
 247                        /* The remote port can be either a panel or a bridge */
 248                        dp->plat_data.panel = of_drm_find_panel(np);
 249                        if (!dp->plat_data.panel) {
 250                                dp->ptn_bridge = of_drm_find_bridge(np);
 251                                if (!dp->ptn_bridge) {
 252                                        of_node_put(np);
 253                                        return -EPROBE_DEFER;
 254                                }
 255                        }
 256                        of_node_put(np);
 257                } else {
 258                        DRM_ERROR("no remote endpoint device node found.\n");
 259                        return -EINVAL;
 260                }
 261        } else {
 262                DRM_ERROR("no port endpoint subnode found.\n");
 263                return -EINVAL;
 264        }
 265
 266out:
 267        return component_add(&pdev->dev, &exynos_dp_ops);
 268}
 269
 270static int exynos_dp_remove(struct platform_device *pdev)
 271{
 272        component_del(&pdev->dev, &exynos_dp_ops);
 273
 274        return 0;
 275}
 276
 277#ifdef CONFIG_PM
 278static int exynos_dp_suspend(struct device *dev)
 279{
 280        return analogix_dp_suspend(dev);
 281}
 282
 283static int exynos_dp_resume(struct device *dev)
 284{
 285        return analogix_dp_resume(dev);
 286}
 287#endif
 288
 289static const struct dev_pm_ops exynos_dp_pm_ops = {
 290        SET_RUNTIME_PM_OPS(exynos_dp_suspend, exynos_dp_resume, NULL)
 291};
 292
 293static const struct of_device_id exynos_dp_match[] = {
 294        { .compatible = "samsung,exynos5-dp" },
 295        {},
 296};
 297MODULE_DEVICE_TABLE(of, exynos_dp_match);
 298
 299struct platform_driver dp_driver = {
 300        .probe          = exynos_dp_probe,
 301        .remove         = exynos_dp_remove,
 302        .driver         = {
 303                .name   = "exynos-dp",
 304                .owner  = THIS_MODULE,
 305                .pm     = &exynos_dp_pm_ops,
 306                .of_match_table = exynos_dp_match,
 307        },
 308};
 309
 310MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
 311MODULE_DESCRIPTION("Samsung Specific Analogix-DP Driver Extension");
 312MODULE_LICENSE("GPL v2");
 313