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