xref: /openbmc/linux/drivers/gpu/drm/tegra/rgb.c (revision b6bec26c)
1 /*
2  * Copyright (C) 2012 Avionic Design GmbH
3  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  */
9 
10 #include <linux/clk.h>
11 #include <linux/module.h>
12 #include <linux/of.h>
13 #include <linux/platform_device.h>
14 
15 #include "drm.h"
16 #include "dc.h"
17 
18 struct tegra_rgb {
19 	struct tegra_output output;
20 	struct clk *clk_parent;
21 	struct clk *clk;
22 };
23 
24 static inline struct tegra_rgb *to_rgb(struct tegra_output *output)
25 {
26 	return container_of(output, struct tegra_rgb, output);
27 }
28 
29 struct reg_entry {
30 	unsigned long offset;
31 	unsigned long value;
32 };
33 
34 static const struct reg_entry rgb_enable[] = {
35 	{ DC_COM_PIN_OUTPUT_ENABLE(0),   0x00000000 },
36 	{ DC_COM_PIN_OUTPUT_ENABLE(1),   0x00000000 },
37 	{ DC_COM_PIN_OUTPUT_ENABLE(2),   0x00000000 },
38 	{ DC_COM_PIN_OUTPUT_ENABLE(3),   0x00000000 },
39 	{ DC_COM_PIN_OUTPUT_POLARITY(0), 0x00000000 },
40 	{ DC_COM_PIN_OUTPUT_POLARITY(1), 0x01000000 },
41 	{ DC_COM_PIN_OUTPUT_POLARITY(2), 0x00000000 },
42 	{ DC_COM_PIN_OUTPUT_POLARITY(3), 0x00000000 },
43 	{ DC_COM_PIN_OUTPUT_DATA(0),     0x00000000 },
44 	{ DC_COM_PIN_OUTPUT_DATA(1),     0x00000000 },
45 	{ DC_COM_PIN_OUTPUT_DATA(2),     0x00000000 },
46 	{ DC_COM_PIN_OUTPUT_DATA(3),     0x00000000 },
47 	{ DC_COM_PIN_OUTPUT_SELECT(0),   0x00000000 },
48 	{ DC_COM_PIN_OUTPUT_SELECT(1),   0x00000000 },
49 	{ DC_COM_PIN_OUTPUT_SELECT(2),   0x00000000 },
50 	{ DC_COM_PIN_OUTPUT_SELECT(3),   0x00000000 },
51 	{ DC_COM_PIN_OUTPUT_SELECT(4),   0x00210222 },
52 	{ DC_COM_PIN_OUTPUT_SELECT(5),   0x00002200 },
53 	{ DC_COM_PIN_OUTPUT_SELECT(6),   0x00020000 },
54 };
55 
56 static const struct reg_entry rgb_disable[] = {
57 	{ DC_COM_PIN_OUTPUT_SELECT(6),   0x00000000 },
58 	{ DC_COM_PIN_OUTPUT_SELECT(5),   0x00000000 },
59 	{ DC_COM_PIN_OUTPUT_SELECT(4),   0x00000000 },
60 	{ DC_COM_PIN_OUTPUT_SELECT(3),   0x00000000 },
61 	{ DC_COM_PIN_OUTPUT_SELECT(2),   0x00000000 },
62 	{ DC_COM_PIN_OUTPUT_SELECT(1),   0x00000000 },
63 	{ DC_COM_PIN_OUTPUT_SELECT(0),   0x00000000 },
64 	{ DC_COM_PIN_OUTPUT_DATA(3),     0xaaaaaaaa },
65 	{ DC_COM_PIN_OUTPUT_DATA(2),     0xaaaaaaaa },
66 	{ DC_COM_PIN_OUTPUT_DATA(1),     0xaaaaaaaa },
67 	{ DC_COM_PIN_OUTPUT_DATA(0),     0xaaaaaaaa },
68 	{ DC_COM_PIN_OUTPUT_POLARITY(3), 0x00000000 },
69 	{ DC_COM_PIN_OUTPUT_POLARITY(2), 0x00000000 },
70 	{ DC_COM_PIN_OUTPUT_POLARITY(1), 0x00000000 },
71 	{ DC_COM_PIN_OUTPUT_POLARITY(0), 0x00000000 },
72 	{ DC_COM_PIN_OUTPUT_ENABLE(3),   0x55555555 },
73 	{ DC_COM_PIN_OUTPUT_ENABLE(2),   0x55555555 },
74 	{ DC_COM_PIN_OUTPUT_ENABLE(1),   0x55150005 },
75 	{ DC_COM_PIN_OUTPUT_ENABLE(0),   0x55555555 },
76 };
77 
78 static void tegra_dc_write_regs(struct tegra_dc *dc,
79 				const struct reg_entry *table,
80 				unsigned int num)
81 {
82 	unsigned int i;
83 
84 	for (i = 0; i < num; i++)
85 		tegra_dc_writel(dc, table[i].value, table[i].offset);
86 }
87 
88 static int tegra_output_rgb_enable(struct tegra_output *output)
89 {
90 	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
91 
92 	tegra_dc_write_regs(dc, rgb_enable, ARRAY_SIZE(rgb_enable));
93 
94 	return 0;
95 }
96 
97 static int tegra_output_rgb_disable(struct tegra_output *output)
98 {
99 	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
100 
101 	tegra_dc_write_regs(dc, rgb_disable, ARRAY_SIZE(rgb_disable));
102 
103 	return 0;
104 }
105 
106 static int tegra_output_rgb_setup_clock(struct tegra_output *output,
107 					struct clk *clk, unsigned long pclk)
108 {
109 	struct tegra_rgb *rgb = to_rgb(output);
110 
111 	return clk_set_parent(clk, rgb->clk_parent);
112 }
113 
114 static int tegra_output_rgb_check_mode(struct tegra_output *output,
115 				       struct drm_display_mode *mode,
116 				       enum drm_mode_status *status)
117 {
118 	/*
119 	 * FIXME: For now, always assume that the mode is okay. There are
120 	 * unresolved issues with clk_round_rate(), which doesn't always
121 	 * reliably report whether a frequency can be set or not.
122 	 */
123 
124 	*status = MODE_OK;
125 
126 	return 0;
127 }
128 
129 static const struct tegra_output_ops rgb_ops = {
130 	.enable = tegra_output_rgb_enable,
131 	.disable = tegra_output_rgb_disable,
132 	.setup_clock = tegra_output_rgb_setup_clock,
133 	.check_mode = tegra_output_rgb_check_mode,
134 };
135 
136 int tegra_dc_rgb_probe(struct tegra_dc *dc)
137 {
138 	struct device_node *np;
139 	struct tegra_rgb *rgb;
140 	int err;
141 
142 	np = of_get_child_by_name(dc->dev->of_node, "rgb");
143 	if (!np || !of_device_is_available(np))
144 		return -ENODEV;
145 
146 	rgb = devm_kzalloc(dc->dev, sizeof(*rgb), GFP_KERNEL);
147 	if (!rgb)
148 		return -ENOMEM;
149 
150 	rgb->clk = devm_clk_get(dc->dev, NULL);
151 	if (IS_ERR(rgb->clk)) {
152 		dev_err(dc->dev, "failed to get clock\n");
153 		return PTR_ERR(rgb->clk);
154 	}
155 
156 	rgb->clk_parent = devm_clk_get(dc->dev, "parent");
157 	if (IS_ERR(rgb->clk_parent)) {
158 		dev_err(dc->dev, "failed to get parent clock\n");
159 		return PTR_ERR(rgb->clk_parent);
160 	}
161 
162 	err = clk_set_parent(rgb->clk, rgb->clk_parent);
163 	if (err < 0) {
164 		dev_err(dc->dev, "failed to set parent clock: %d\n", err);
165 		return err;
166 	}
167 
168 	rgb->output.dev = dc->dev;
169 	rgb->output.of_node = np;
170 
171 	err = tegra_output_parse_dt(&rgb->output);
172 	if (err < 0)
173 		return err;
174 
175 	dc->rgb = &rgb->output;
176 
177 	return 0;
178 }
179 
180 int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
181 {
182 	struct tegra_rgb *rgb = to_rgb(dc->rgb);
183 	int err;
184 
185 	if (!dc->rgb)
186 		return -ENODEV;
187 
188 	rgb->output.type = TEGRA_OUTPUT_RGB;
189 	rgb->output.ops = &rgb_ops;
190 
191 	err = tegra_output_init(dc->base.dev, &rgb->output);
192 	if (err < 0) {
193 		dev_err(dc->dev, "output setup failed: %d\n", err);
194 		return err;
195 	}
196 
197 	/*
198 	 * By default, outputs can be associated with each display controller.
199 	 * RGB outputs are an exception, so we make sure they can be attached
200 	 * to only their parent display controller.
201 	 */
202 	rgb->output.encoder.possible_crtcs = 1 << dc->pipe;
203 
204 	return 0;
205 }
206 
207 int tegra_dc_rgb_exit(struct tegra_dc *dc)
208 {
209 	if (dc->rgb) {
210 		int err;
211 
212 		err = tegra_output_disable(dc->rgb);
213 		if (err < 0) {
214 			dev_err(dc->dev, "output failed to disable: %d\n", err);
215 			return err;
216 		}
217 
218 		err = tegra_output_exit(dc->rgb);
219 		if (err < 0) {
220 			dev_err(dc->dev, "output cleanup failed: %d\n", err);
221 			return err;
222 		}
223 
224 		dc->rgb = NULL;
225 	}
226 
227 	return 0;
228 }
229