summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/pl111/pl111_vexpress.c
blob: 350570fe06b5f39664d8fd38c9f37e1fac051b3e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// SPDX-License-Identifier: GPL-2.0
/*
 * Versatile Express PL111 handling
 * Copyright (C) 2018 Linus Walleij
 *
 * This module binds to the "arm,vexpress-muxfpga" device on the
 * Versatile Express configuration bus and sets up which CLCD instance
 * gets muxed out on the DVI bridge.
 */
#include <linux/device.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/vexpress.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include "pl111_drm.h"
#include "pl111_vexpress.h"

#define VEXPRESS_FPGAMUX_MOTHERBOARD		0x00
#define VEXPRESS_FPGAMUX_DAUGHTERBOARD_1	0x01
#define VEXPRESS_FPGAMUX_DAUGHTERBOARD_2	0x02

int pl111_vexpress_clcd_init(struct device *dev,
			     struct pl111_drm_dev_private *priv,
			     struct regmap *map)
{
	struct device_node *root;
	struct device_node *child;
	struct device_node *ct_clcd = NULL;
	bool has_coretile_clcd = false;
	bool has_coretile_hdlcd = false;
	bool mux_motherboard = true;
	u32 val;
	int ret;

	/*
	 * Check if we have a CLCD or HDLCD on the core tile by checking if a
	 * CLCD or HDLCD is available in the root of the device tree.
	 */
	root = of_find_node_by_path("/");
	if (!root)
		return -EINVAL;

	for_each_available_child_of_node(root, child) {
		if (of_device_is_compatible(child, "arm,pl111")) {
			has_coretile_clcd = true;
			ct_clcd = child;
			break;
		}
		if (of_device_is_compatible(child, "arm,hdlcd")) {
			has_coretile_hdlcd = true;
			of_node_put(child);
			break;
		}
	}

	of_node_put(root);

	/*
	 * If there is a coretile HDLCD and it has a driver,
	 * do not mux the CLCD on the motherboard to the DVI.
	 */
	if (has_coretile_hdlcd && IS_ENABLED(CONFIG_DRM_HDLCD))
		mux_motherboard = false;

	/*
	 * On the Vexpress CA9 we let the CLCD on the coretile
	 * take precedence, so also in this case do not mux the
	 * motherboard to the DVI.
	 */
	if (has_coretile_clcd)
		mux_motherboard = false;

	if (mux_motherboard) {
		dev_info(dev, "DVI muxed to motherboard CLCD\n");
		val = VEXPRESS_FPGAMUX_MOTHERBOARD;
	} else if (ct_clcd == dev->of_node) {
		dev_info(dev,
			 "DVI muxed to daughterboard 1 (core tile) CLCD\n");
		val = VEXPRESS_FPGAMUX_DAUGHTERBOARD_1;
	} else {
		dev_info(dev, "core tile graphics present\n");
		dev_info(dev, "this device will be deactivated\n");
		return -ENODEV;
	}

	ret = regmap_write(map, 0, val);
	if (ret) {
		dev_err(dev, "error setting DVI muxmode\n");
		return -ENODEV;
	}

	return 0;
}

/*
 * This sets up the regmap pointer that will then be retrieved by
 * the detection code in pl111_versatile.c and passed in to the
 * pl111_vexpress_clcd_init() function above.
 */
static int vexpress_muxfpga_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct regmap *map;

	map = devm_regmap_init_vexpress_config(&pdev->dev);
	if (IS_ERR(map))
		return PTR_ERR(map);
	dev_set_drvdata(dev, map);

	return 0;
}

static const struct of_device_id vexpress_muxfpga_match[] = {
	{ .compatible = "arm,vexpress-muxfpga", },
	{}
};

static struct platform_driver vexpress_muxfpga_driver = {
	.driver = {
		.name = "vexpress-muxfpga",
		.of_match_table = of_match_ptr(vexpress_muxfpga_match),
	},
	.probe = vexpress_muxfpga_probe,
};

int vexpress_muxfpga_init(void)
{
	int ret;

	ret = platform_driver_register(&vexpress_muxfpga_driver);
	/* -EBUSY just means this driver is already registered */
	if (ret == -EBUSY)
		ret = 0;
	return ret;
}