cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

exynos_dp.c (7459B)


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