xref: /openbsd/sys/dev/fdt/rkdwhdmi.c (revision 4cfece93)
1 /* $OpenBSD: rkdwhdmi.c,v 1.5 2020/06/30 02:19:11 deraadt Exp $ */
2 /* $NetBSD: rk_dwhdmi.c,v 1.4 2019/12/17 18:26:36 jakllsch Exp $ */
3 
4 /*-
5  * Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/device.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 
35 #include <machine/bus.h>
36 #include <machine/fdt.h>
37 
38 #include <dev/ofw/openfirm.h>
39 #include <dev/ofw/ofw_clock.h>
40 #include <dev/ofw/ofw_misc.h>
41 #include <dev/ofw/ofw_pinctrl.h>
42 #include <dev/ofw/fdt.h>
43 
44 #include <drm/drm_crtc_helper.h>
45 
46 #include <dev/ic/dwhdmi.h>
47 
48 #define	RK3399_GRF_SOC_CON20		0x6250
49 #define	 HDMI_LCDC_SEL			(1 << 6)
50 
51 const struct dwhdmi_mpll_config rkdwhdmi_mpll_config[] = {
52 	{ 40000,	0x00b3, 0x0000, 0x0018 },
53 	{ 65000,	0x0072, 0x0001, 0x0028 },
54 	{ 66000,	0x013e, 0x0003, 0x0038 },
55 	{ 83500,	0x0072, 0x0001, 0x0028 },
56 	{ 146250,	0x0051, 0x0002, 0x0038 },
57 	{ 148500,	0x0051, 0x0003, 0x0000 },
58 	{ 272000,	0x0040, 0x0003, 0x0000 },
59 	{ 340000,	0x0040, 0x0003, 0x0000 },
60 	{ 0,		0x0051, 0x0003, 0x0000 },
61 };
62 
63 const struct dwhdmi_phy_config rkdwhdmi_phy_config[] = {
64 	{ 74250,	0x8009, 0x0004, 0x0272 },
65 	{ 148500,	0x802b, 0x0004, 0x028d },
66 	{ 297000,	0x8039, 0x0005, 0x028d },
67 	{ 594000,	0x8039, 0x0000, 0x019d },
68 	{ 0,		0x0000, 0x0000, 0x0000 }
69 };
70 
71 struct rkdwhdmi_softc {
72 	struct dwhdmi_softc	sc_base;
73 	int			sc_node;
74 	int			sc_clk_vpll;
75 
76 	struct drm_display_mode	sc_curmode;
77 	struct drm_encoder	sc_encoder;
78 	struct regmap		*sc_grf;
79 
80 	int			sc_activated;
81 
82 	struct device_ports	sc_ports;
83 };
84 
85 #define	to_rkdwhdmi_softc(x)	container_of(x, struct rkdwhdmi_softc, sc_base)
86 #define	to_rkdwhdmi_encoder(x)	container_of(x, struct rkdwhdmi_softc, sc_encoder)
87 
88 int rkdwhdmi_match(struct device *, void *, void *);
89 void rkdwhdmi_attach(struct device *, struct device *, void *);
90 
91 void rkdwhdmi_select_input(struct rkdwhdmi_softc *, u_int);
92 void rkdwhdmi_encoder_enable(struct drm_encoder *);
93 
94 int rkdwhdmi_ep_activate(void *, struct endpoint *, void *);
95 void *rkdwhdmi_ep_get_cookie(void *, struct endpoint *);
96 
97 void rkdwhdmi_enable(struct dwhdmi_softc *);
98 void rkdwhdmi_mode_set(struct dwhdmi_softc *, const struct drm_display_mode *,
99     const struct drm_display_mode *);
100 enum drm_mode_status rkdwhdmi_mode_valid(struct dwhdmi_softc *,
101     const struct drm_display_mode *);
102 
103 struct cfattach	rkdwhdmi_ca = {
104 	sizeof (struct rkdwhdmi_softc), rkdwhdmi_match, rkdwhdmi_attach
105 };
106 
107 struct cfdriver rkdwhdmi_cd = {
108 	NULL, "rkdwhdmi", DV_DULL
109 };
110 
111 int
112 rkdwhdmi_match(struct device *parent, void *match, void *aux)
113 {
114 	struct fdt_attach_args *faa = aux;
115 
116 	return OF_is_compatible(faa->fa_node, "rockchip,rk3399-dw-hdmi");
117 }
118 
119 void
120 rkdwhdmi_attach(struct device *parent, struct device *self, void *aux)
121 {
122 	struct rkdwhdmi_softc *sc = (struct rkdwhdmi_softc *)self;
123 	struct fdt_attach_args *faa = aux;
124 	uint32_t grf;
125 	bus_addr_t addr;
126 	bus_size_t size;
127 	uint32_t phandle;
128 
129 	if (faa->fa_nreg < 1) {
130 		printf(": no registers\n");
131 		return;
132 	}
133 
134 	pinctrl_byname(sc->sc_node, "default");
135 
136 	clock_enable(faa->fa_node, "iahb");
137 	clock_enable(faa->fa_node, "isfr");
138 	clock_enable(faa->fa_node, "vpll");
139 	clock_enable(faa->fa_node, "grf");
140 	clock_enable(faa->fa_node, "cec");
141 
142 	sc->sc_base.sc_reg_width =
143 	    OF_getpropint(faa->fa_node, "reg-io-width", 4);
144 
145 	sc->sc_base.sc_bst = faa->fa_iot;
146 	if (bus_space_map(sc->sc_base.sc_bst, faa->fa_reg[0].addr,
147 	    faa->fa_reg[0].size, 0, &sc->sc_base.sc_bsh)) {
148 		printf(": can't map registers\n");
149 		return;
150 	}
151 
152 	sc->sc_node = faa->fa_node;
153 	sc->sc_clk_vpll = OF_getindex(faa->fa_node, "vpll", "clock-names");
154 
155 	grf = OF_getpropint(faa->fa_node, "rockchip,grf", 0);
156 	sc->sc_grf = regmap_byphandle(grf);
157 	if (sc->sc_grf == NULL) {
158 		printf(": can't get grf\n");
159 		return;
160 	}
161 
162 	printf(": HDMI TX\n");
163 
164 	phandle = OF_getpropint(faa->fa_node, "ddc-i2c-bus", 0);
165 	sc->sc_base.sc_ic = i2c_byphandle(phandle);
166 	if (phandle && sc->sc_base.sc_ic == NULL) {
167 		printf("%s: couldn't find external I2C master\n",
168 		    self->dv_xname);
169 		return;
170 	}
171 
172 	sc->sc_base.sc_flags |= DWHDMI_USE_INTERNAL_PHY;
173 	sc->sc_base.sc_detect = dwhdmi_phy_detect;
174 	sc->sc_base.sc_enable = rkdwhdmi_enable;
175 	sc->sc_base.sc_disable = dwhdmi_phy_disable;
176 	sc->sc_base.sc_mode_set = rkdwhdmi_mode_set;
177 	sc->sc_base.sc_mode_valid = rkdwhdmi_mode_valid;
178 	sc->sc_base.sc_mpll_config = rkdwhdmi_mpll_config;
179 	sc->sc_base.sc_phy_config = rkdwhdmi_phy_config;
180 
181 	if (dwhdmi_attach(&sc->sc_base) != 0) {
182 		printf("%s: failed to attach driver\n", self->dv_xname);
183 		return;
184 	}
185 
186 	sc->sc_ports.dp_node = faa->fa_node;
187 	sc->sc_ports.dp_cookie = sc;
188 	sc->sc_ports.dp_ep_activate = rkdwhdmi_ep_activate;
189 	sc->sc_ports.dp_ep_get_cookie = rkdwhdmi_ep_get_cookie;
190 	device_ports_register(&sc->sc_ports, EP_DRM_ENCODER);
191 
192 #ifdef notyet
193 	fdtbus_register_dai_controller(self, phandle, &rkdwhdmi_dai_funcs);
194 #endif
195 }
196 
197 void
198 rkdwhdmi_select_input(struct rkdwhdmi_softc *sc, u_int crtc_index)
199 {
200 	const uint32_t write_mask = HDMI_LCDC_SEL << 16;
201 	const uint32_t write_val = crtc_index == 0 ? HDMI_LCDC_SEL : 0;
202 
203 	regmap_write_4(sc->sc_grf, RK3399_GRF_SOC_CON20, write_mask | write_val);
204 }
205 
206 void
207 rkdwhdmi_encoder_enable(struct drm_encoder *encoder)
208 {
209 	struct rkdwhdmi_softc * const sc = to_rkdwhdmi_encoder(encoder);
210 	const u_int crtc_index = drm_crtc_index(encoder->crtc);
211 
212 	rkdwhdmi_select_input(sc, crtc_index);
213 }
214 
215 struct drm_encoder_funcs rkdwhdmi_encoder_funcs = {
216 	.destroy = drm_encoder_cleanup,
217 };
218 
219 struct drm_encoder_helper_funcs rkdwhdmi_encoder_helper_funcs = {
220 	.enable = rkdwhdmi_encoder_enable,
221 };
222 
223 int
224 rkdwhdmi_ep_activate(void *cookie, struct endpoint *ep, void *arg)
225 {
226 	struct rkdwhdmi_softc *sc = cookie;
227 	struct drm_crtc *crtc = NULL;
228 	struct endpoint *rep;
229 	int error;
230 
231 	if (sc->sc_activated)
232 		return 0;
233 
234 	rep = endpoint_remote(ep);
235 	if (rep && rep->ep_type == EP_DRM_CRTC)
236 		crtc = endpoint_get_cookie(rep);
237 	if (crtc == NULL)
238 		return EINVAL;
239 
240 	sc->sc_encoder.possible_crtcs = 0x3; /* XXX */
241 	drm_encoder_init(crtc->dev, &sc->sc_encoder, &rkdwhdmi_encoder_funcs,
242 	    DRM_MODE_ENCODER_TMDS, NULL);
243 	drm_encoder_helper_add(&sc->sc_encoder, &rkdwhdmi_encoder_helper_funcs);
244 
245 	sc->sc_base.sc_connector.base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
246 	error = dwhdmi_bind(&sc->sc_base, &sc->sc_encoder);
247 	if (error != 0)
248 		return error;
249 
250 	sc->sc_activated = 1;
251 	return 0;
252 }
253 
254 void *
255 rkdwhdmi_ep_get_cookie(void *cookie, struct endpoint *ep)
256 {
257 	struct rkdwhdmi_softc *sc = cookie;
258 	return &sc->sc_encoder;
259 }
260 
261 void
262 rkdwhdmi_enable(struct dwhdmi_softc *dsc)
263 {
264 	dwhdmi_phy_enable(dsc);
265 }
266 
267 void
268 rkdwhdmi_mode_set(struct dwhdmi_softc *dsc,
269     const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode)
270 {
271 	struct rkdwhdmi_softc *sc = to_rkdwhdmi_softc(dsc);
272 	int error;
273 
274 	if (sc->sc_clk_vpll != -1) {
275 		error = clock_set_frequency(sc->sc_node, "vpll",
276 		    adjusted_mode->clock * 1000);
277 		if (error != 0)
278 			printf("%s: couldn't set pixel clock to %u Hz: %d\n",
279 			    dsc->sc_dev.dv_xname, adjusted_mode->clock * 1000,
280 			    error);
281 	}
282 
283 	dwhdmi_phy_mode_set(dsc, mode, adjusted_mode);
284 }
285 
286 enum drm_mode_status
287 rkdwhdmi_mode_valid(struct dwhdmi_softc *dsc, const struct drm_display_mode *mode)
288 {
289 	struct rkdwhdmi_softc *sc = to_rkdwhdmi_softc(dsc);
290 	int i;
291 
292 	for (i = 0; sc->sc_base.sc_mpll_config[i].pixel_clock != 0; i++)
293 		if (mode->clock == sc->sc_base.sc_mpll_config[i].pixel_clock)
294 			return MODE_OK;
295 
296 	return MODE_BAD;
297 }
298 
299 #ifdef notyet
300 
301 static audio_dai_tag_t
302 rkdwhdmi_dai_get_tag(device_t dev, const void *data, size_t len)
303 {
304 	struct rkdwhdmi_softc * const sc = device_private(dev);
305 
306 	if (len != 4)
307 		return NULL;
308 
309 	return &sc->sc_base.sc_dai;
310 }
311 
312 static struct fdtbus_dai_controller_func rkdwhdmi_dai_funcs = {
313 	.get_tag = rkdwhdmi_dai_get_tag
314 };
315 
316 #endif
317