1a0a23564SMichal Meloun /*-
2a0a23564SMichal Meloun * Copyright (c) 2015 Michal Meloun
3a0a23564SMichal Meloun * All rights reserved.
4a0a23564SMichal Meloun *
5a0a23564SMichal Meloun * Redistribution and use in source and binary forms, with or without
6a0a23564SMichal Meloun * modification, are permitted provided that the following conditions
7a0a23564SMichal Meloun * are met:
8a0a23564SMichal Meloun * 1. Redistributions of source code must retain the above copyright
9a0a23564SMichal Meloun * notice, this list of conditions and the following disclaimer.
10a0a23564SMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright
11a0a23564SMichal Meloun * notice, this list of conditions and the following disclaimer in the
12a0a23564SMichal Meloun * documentation and/or other materials provided with the distribution.
13a0a23564SMichal Meloun *
14a0a23564SMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15a0a23564SMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16a0a23564SMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17a0a23564SMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18a0a23564SMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19a0a23564SMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20a0a23564SMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21a0a23564SMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22a0a23564SMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23a0a23564SMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24a0a23564SMichal Meloun * SUCH DAMAGE.
25a0a23564SMichal Meloun */
26a0a23564SMichal Meloun
27a0a23564SMichal Meloun #include <sys/param.h>
28a0a23564SMichal Meloun #include <sys/systm.h>
29a0a23564SMichal Meloun #include <sys/bus.h>
30a0a23564SMichal Meloun #include <sys/gpio.h>
31a0a23564SMichal Meloun #include <sys/kernel.h>
32a0a23564SMichal Meloun #include <sys/module.h>
33a0a23564SMichal Meloun #include <sys/malloc.h>
34a0a23564SMichal Meloun #include <sys/rman.h>
35a0a23564SMichal Meloun #include <sys/sysctl.h>
36a0a23564SMichal Meloun
37a0a23564SMichal Meloun #include <machine/bus.h>
38a0a23564SMichal Meloun
39be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
401f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h>
41b2f0caf1SEmmanuel Vadot #include <dev/regulator/regulator.h>
42a0a23564SMichal Meloun #include <dev/drm2/drmP.h>
43a0a23564SMichal Meloun #include <dev/drm2/drm_crtc.h>
44a0a23564SMichal Meloun #include <dev/drm2/drm_crtc_helper.h>
45a0a23564SMichal Meloun #include <dev/drm2/drm_fb_helper.h>
46a0a23564SMichal Meloun #include <dev/gpio/gpiobusvar.h>
47a0a23564SMichal Meloun #include <dev/ofw/ofw_bus.h>
48a0a23564SMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
49a0a23564SMichal Meloun
50a0a23564SMichal Meloun #include <arm/nvidia/drm2/tegra_drm.h>
51a0a23564SMichal Meloun #include <arm/nvidia/drm2/tegra_hdmi_reg.h>
52a0a23564SMichal Meloun #include <arm/nvidia/drm2/tegra_dc_reg.h>
53a0a23564SMichal Meloun #include <arm/nvidia/drm2/hdmi.h>
54a0a23564SMichal Meloun
55a0a23564SMichal Meloun #include "tegra_dc_if.h"
56a0a23564SMichal Meloun #include "tegra_drm_if.h"
57a0a23564SMichal Meloun
58a0a23564SMichal Meloun #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, 4 * (_r), (_v))
59a0a23564SMichal Meloun #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, 4 * (_r))
60a0a23564SMichal Meloun
61a0a23564SMichal Meloun /* HDA stream format verb. */
62a0a23564SMichal Meloun #define AC_FMT_CHAN_GET(x) (((x) >> 0) & 0xf)
63a0a23564SMichal Meloun #define AC_FMT_CHAN_BITS_GET(x) (((x) >> 4) & 0x7)
64a0a23564SMichal Meloun #define AC_FMT_DIV_GET(x) (((x) >> 8) & 0x7)
65a0a23564SMichal Meloun #define AC_FMT_MUL_GET(x) (((x) >> 11) & 0x7)
66a0a23564SMichal Meloun #define AC_FMT_BASE_44K (1 << 14)
67a0a23564SMichal Meloun #define AC_FMT_TYPE_NON_PCM (1 << 15)
68a0a23564SMichal Meloun
69a0a23564SMichal Meloun #define HDMI_REKEY_DEFAULT 56
70a0a23564SMichal Meloun #define HDMI_ELD_BUFFER_SIZE 96
71a0a23564SMichal Meloun
72a0a23564SMichal Meloun #define HDMI_DC_CLOCK_MULTIPIER 2
73a0a23564SMichal Meloun
74a0a23564SMichal Meloun struct audio_reg {
75a0a23564SMichal Meloun uint32_t audio_clk;
76a0a23564SMichal Meloun bus_size_t acr_reg;
77a0a23564SMichal Meloun bus_size_t nval_reg;
78a0a23564SMichal Meloun bus_size_t aval_reg;
79a0a23564SMichal Meloun };
80a0a23564SMichal Meloun
81a0a23564SMichal Meloun static const struct audio_reg audio_regs[] =
82a0a23564SMichal Meloun {
83a0a23564SMichal Meloun {
84a0a23564SMichal Meloun .audio_clk = 32000,
85a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW,
86a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0320,
87a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320,
88a0a23564SMichal Meloun },
89a0a23564SMichal Meloun {
90a0a23564SMichal Meloun .audio_clk = 44100,
91a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW,
92a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0441,
93a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441,
94a0a23564SMichal Meloun },
95a0a23564SMichal Meloun {
96a0a23564SMichal Meloun .audio_clk = 88200,
97a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW,
98a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0882,
99a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882,
100a0a23564SMichal Meloun },
101a0a23564SMichal Meloun {
102a0a23564SMichal Meloun .audio_clk = 176400,
103a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW,
104a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_1764,
105a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764,
106a0a23564SMichal Meloun },
107a0a23564SMichal Meloun {
108a0a23564SMichal Meloun .audio_clk = 48000,
109a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW,
110a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0480,
111a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480,
112a0a23564SMichal Meloun },
113a0a23564SMichal Meloun {
114a0a23564SMichal Meloun .audio_clk = 96000,
115a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW,
116a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0960,
117a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960,
118a0a23564SMichal Meloun },
119a0a23564SMichal Meloun {
120a0a23564SMichal Meloun .audio_clk = 192000,
121a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW,
122a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_1920,
123a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920,
124a0a23564SMichal Meloun },
125a0a23564SMichal Meloun };
126a0a23564SMichal Meloun
127a0a23564SMichal Meloun struct tmds_config {
128a0a23564SMichal Meloun uint32_t pclk;
129a0a23564SMichal Meloun uint32_t pll0;
130a0a23564SMichal Meloun uint32_t pll1;
131a0a23564SMichal Meloun uint32_t drive_c;
132a0a23564SMichal Meloun uint32_t pe_c;
133a0a23564SMichal Meloun uint32_t peak_c;
134a0a23564SMichal Meloun uint32_t pad_ctls;
135a0a23564SMichal Meloun };
136a0a23564SMichal Meloun
137a0a23564SMichal Meloun static const struct tmds_config tegra124_tmds_config[] =
138a0a23564SMichal Meloun {
139a0a23564SMichal Meloun { /* 480p/576p / 25.2MHz/27MHz */
140a0a23564SMichal Meloun .pclk = 27000000,
141a0a23564SMichal Meloun .pll0 = 0x01003010,
142a0a23564SMichal Meloun .pll1 = 0x00301B00,
143a0a23564SMichal Meloun .drive_c = 0x1F1F1F1F,
144a0a23564SMichal Meloun .pe_c = 0x00000000,
145a0a23564SMichal Meloun .peak_c = 0x03030303,
146a0a23564SMichal Meloun .pad_ctls = 0x800034BB,
147a0a23564SMichal Meloun },
148a0a23564SMichal Meloun { /* 720p/1080i / 74.25MHz */
149a0a23564SMichal Meloun .pclk = 74250000,
150a0a23564SMichal Meloun .pll0 = 0x01003110,
151a0a23564SMichal Meloun .pll1 = 0x00301500,
152a0a23564SMichal Meloun .drive_c = 0x2C2C2C2C,
153a0a23564SMichal Meloun .pe_c = 0x00000000,
154a0a23564SMichal Meloun .peak_c = 0x07070707,
155a0a23564SMichal Meloun .pad_ctls = 0x800034BB,
156a0a23564SMichal Meloun },
157a0a23564SMichal Meloun { /* 1080p / 148.5MHz */
158a0a23564SMichal Meloun .pclk = 148500000,
159a0a23564SMichal Meloun .pll0 = 0x01003310,
160a0a23564SMichal Meloun .pll1 = 0x00301500,
161a0a23564SMichal Meloun .drive_c = 0x33333333,
162a0a23564SMichal Meloun .pe_c = 0x00000000,
163a0a23564SMichal Meloun .peak_c = 0x0C0C0C0C,
164a0a23564SMichal Meloun .pad_ctls = 0x800034BB,
165a0a23564SMichal Meloun },
166a0a23564SMichal Meloun { /* 2216p / 297MHz */
167a0a23564SMichal Meloun .pclk = UINT_MAX,
168a0a23564SMichal Meloun .pll0 = 0x01003F10,
169a0a23564SMichal Meloun .pll1 = 0x00300F00,
170a0a23564SMichal Meloun .drive_c = 0x37373737,
171a0a23564SMichal Meloun .pe_c = 0x00000000,
172a0a23564SMichal Meloun .peak_c = 0x17171717,
173a0a23564SMichal Meloun .pad_ctls = 0x800036BB,
174a0a23564SMichal Meloun },
175a0a23564SMichal Meloun };
176a0a23564SMichal Meloun
177a0a23564SMichal Meloun struct hdmi_softc {
178a0a23564SMichal Meloun device_t dev;
179a0a23564SMichal Meloun struct resource *mem_res;
180a0a23564SMichal Meloun struct resource *irq_res;
181a0a23564SMichal Meloun void *irq_ih;
182a0a23564SMichal Meloun
183a0a23564SMichal Meloun clk_t clk_parent;
184a0a23564SMichal Meloun clk_t clk_hdmi;
185a0a23564SMichal Meloun hwreset_t hwreset_hdmi;
186a0a23564SMichal Meloun regulator_t supply_hdmi;
187a0a23564SMichal Meloun regulator_t supply_pll;
188a0a23564SMichal Meloun regulator_t supply_vdd;
189a0a23564SMichal Meloun
190a0a23564SMichal Meloun uint64_t pclk;
191a0a23564SMichal Meloun boolean_t hdmi_mode;
192a0a23564SMichal Meloun
193a0a23564SMichal Meloun int audio_src_type;
194a0a23564SMichal Meloun int audio_freq;
195a0a23564SMichal Meloun int audio_chans;
196a0a23564SMichal Meloun
197a0a23564SMichal Meloun struct tegra_drm *drm;
198a0a23564SMichal Meloun struct tegra_drm_encoder output;
199a0a23564SMichal Meloun
200a0a23564SMichal Meloun const struct tmds_config *tmds_config;
201a0a23564SMichal Meloun int n_tmds_configs;
202a0a23564SMichal Meloun };
203a0a23564SMichal Meloun
204a0a23564SMichal Meloun static struct ofw_compat_data compat_data[] = {
205a0a23564SMichal Meloun {"nvidia,tegra124-hdmi", 1},
206a0a23564SMichal Meloun {NULL, 0},
207a0a23564SMichal Meloun };
208a0a23564SMichal Meloun
209a0a23564SMichal Meloun /* These functions have been copied from newer version of drm_edid.c */
210a0a23564SMichal Meloun /* ELD Header Block */
211a0a23564SMichal Meloun #define DRM_ELD_HEADER_BLOCK_SIZE 4
212a0a23564SMichal Meloun #define DRM_ELD_BASELINE_ELD_LEN 2 /* in dwords! */
drm_eld_size(const uint8_t * eld)213a0a23564SMichal Meloun static int drm_eld_size(const uint8_t *eld)
214a0a23564SMichal Meloun {
215a0a23564SMichal Meloun return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4;
216a0a23564SMichal Meloun }
217a0a23564SMichal Meloun
218a0a23564SMichal Meloun static int
drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe * frame,struct drm_display_mode * mode)219a0a23564SMichal Meloun drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
220a0a23564SMichal Meloun struct drm_display_mode *mode)
221a0a23564SMichal Meloun {
222a0a23564SMichal Meloun int rv;
223a0a23564SMichal Meloun
224a0a23564SMichal Meloun if (!frame || !mode)
225a0a23564SMichal Meloun return -EINVAL;
226a0a23564SMichal Meloun
227a0a23564SMichal Meloun rv = hdmi_avi_infoframe_init(frame);
228a0a23564SMichal Meloun if (rv < 0)
229a0a23564SMichal Meloun return rv;
230a0a23564SMichal Meloun
231a0a23564SMichal Meloun if (mode->flags & DRM_MODE_FLAG_DBLCLK)
232a0a23564SMichal Meloun frame->pixel_repeat = 1;
233a0a23564SMichal Meloun
234a0a23564SMichal Meloun frame->video_code = drm_match_cea_mode(mode);
235a0a23564SMichal Meloun
236a0a23564SMichal Meloun frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
237a0a23564SMichal Meloun #ifdef FREEBSD_NOTYET
238a0a23564SMichal Meloun /*
239a0a23564SMichal Meloun * Populate picture aspect ratio from either
240a0a23564SMichal Meloun * user input (if specified) or from the CEA mode list.
241a0a23564SMichal Meloun */
242a0a23564SMichal Meloun if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_4_3 ||
243a0a23564SMichal Meloun mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_16_9)
244a0a23564SMichal Meloun frame->picture_aspect = mode->picture_aspect_ratio;
245a0a23564SMichal Meloun else if (frame->video_code > 0)
246a0a23564SMichal Meloun frame->picture_aspect = drm_get_cea_aspect_ratio(
247a0a23564SMichal Meloun frame->video_code);
248a0a23564SMichal Meloun #endif
249a0a23564SMichal Meloun
250a0a23564SMichal Meloun frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
251a0a23564SMichal Meloun frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
252a0a23564SMichal Meloun
253a0a23564SMichal Meloun return 0;
254a0a23564SMichal Meloun }
255a0a23564SMichal Meloun /* --------------------------------------------------------------------- */
256a0a23564SMichal Meloun
257a0a23564SMichal Meloun static int
hdmi_setup_clock(struct tegra_drm_encoder * output,clk_t clk,uint64_t pclk)258a0a23564SMichal Meloun hdmi_setup_clock(struct tegra_drm_encoder *output, clk_t clk, uint64_t pclk)
259a0a23564SMichal Meloun {
260a0a23564SMichal Meloun struct hdmi_softc *sc;
261a0a23564SMichal Meloun uint64_t freq;
262a0a23564SMichal Meloun int rv;
263a0a23564SMichal Meloun
264a0a23564SMichal Meloun sc = device_get_softc(output->dev);
265a0a23564SMichal Meloun
266a0a23564SMichal Meloun /* Disable consumers clock for while. */
267a0a23564SMichal Meloun rv = clk_disable(sc->clk_hdmi);
268a0a23564SMichal Meloun if (rv != 0) {
269a0a23564SMichal Meloun device_printf(sc->dev, "Cannot disable 'hdmi' clock\n");
270a0a23564SMichal Meloun return (rv);
271a0a23564SMichal Meloun }
272a0a23564SMichal Meloun rv = clk_disable(clk);
273a0a23564SMichal Meloun if (rv != 0) {
274a0a23564SMichal Meloun device_printf(sc->dev, "Cannot disable display clock\n");
275a0a23564SMichal Meloun return (rv);
276a0a23564SMichal Meloun }
277a0a23564SMichal Meloun
278a0a23564SMichal Meloun /* Set frequency for Display Controller PLL. */
279a0a23564SMichal Meloun freq = HDMI_DC_CLOCK_MULTIPIER * pclk;
280a0a23564SMichal Meloun rv = clk_set_freq(sc->clk_parent, freq, 0);
281a0a23564SMichal Meloun if (rv != 0) {
282a0a23564SMichal Meloun device_printf(output->dev,
283a0a23564SMichal Meloun "Cannot set display pixel frequency\n");
284a0a23564SMichal Meloun return (rv);
285a0a23564SMichal Meloun }
286a0a23564SMichal Meloun
287a0a23564SMichal Meloun /* Reparent display controller */
288a0a23564SMichal Meloun rv = clk_set_parent_by_clk(clk, sc->clk_parent);
289a0a23564SMichal Meloun if (rv != 0) {
290a0a23564SMichal Meloun device_printf(output->dev, "Cannot set parent clock\n");
291a0a23564SMichal Meloun return (rv);
292a0a23564SMichal Meloun }
293a0a23564SMichal Meloun rv = clk_set_freq(clk, freq, 0);
294a0a23564SMichal Meloun if (rv != 0) {
295a0a23564SMichal Meloun device_printf(output->dev,
296a0a23564SMichal Meloun "Cannot set display controller frequency\n");
297a0a23564SMichal Meloun return (rv);
298a0a23564SMichal Meloun }
299a0a23564SMichal Meloun rv = clk_set_freq(sc->clk_hdmi, pclk, 0);
300a0a23564SMichal Meloun if (rv != 0) {
301a0a23564SMichal Meloun device_printf(output->dev,
302a0a23564SMichal Meloun "Cannot set display controller frequency\n");
303a0a23564SMichal Meloun return (rv);
304a0a23564SMichal Meloun }
305a0a23564SMichal Meloun
306a0a23564SMichal Meloun /* And reenable consumers clock. */
307a0a23564SMichal Meloun rv = clk_enable(clk);
308a0a23564SMichal Meloun if (rv != 0) {
309a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable display clock\n");
310a0a23564SMichal Meloun return (rv);
311a0a23564SMichal Meloun }
312a0a23564SMichal Meloun rv = clk_enable(sc->clk_hdmi);
313a0a23564SMichal Meloun if (rv != 0) {
314a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable 'hdmi' clock\n");
315a0a23564SMichal Meloun return (rv);
316a0a23564SMichal Meloun }
317a0a23564SMichal Meloun
318a0a23564SMichal Meloun rv = clk_get_freq(clk, &freq);
319a0a23564SMichal Meloun if (rv != 0) {
320a0a23564SMichal Meloun device_printf(output->dev,
321a0a23564SMichal Meloun "Cannot get display controller frequency\n");
322a0a23564SMichal Meloun return (rv);
323a0a23564SMichal Meloun }
324a0a23564SMichal Meloun
325a0a23564SMichal Meloun DRM_DEBUG_KMS("DC frequency: %llu\n", freq);
326a0a23564SMichal Meloun
327a0a23564SMichal Meloun return (0);
328a0a23564SMichal Meloun }
329a0a23564SMichal Meloun
330a0a23564SMichal Meloun /* -------------------------------------------------------------------
331a0a23564SMichal Meloun *
332a0a23564SMichal Meloun * Infoframes.
333a0a23564SMichal Meloun *
334a0a23564SMichal Meloun */
335a0a23564SMichal Meloun static void
avi_setup_infoframe(struct hdmi_softc * sc,struct drm_display_mode * mode)336a0a23564SMichal Meloun avi_setup_infoframe(struct hdmi_softc *sc, struct drm_display_mode *mode)
337a0a23564SMichal Meloun {
338a0a23564SMichal Meloun struct hdmi_avi_infoframe frame;
339aeb665b5SEd Maste uint8_t buf[17], *hdr, *pb;
340a0a23564SMichal Meloun ssize_t rv;
341a0a23564SMichal Meloun
342a0a23564SMichal Meloun rv = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
343a0a23564SMichal Meloun if (rv < 0) {
344a0a23564SMichal Meloun device_printf(sc->dev, "Cannot setup AVI infoframe: %zd\n", rv);
345a0a23564SMichal Meloun return;
346a0a23564SMichal Meloun }
347a0a23564SMichal Meloun rv = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf));
348a0a23564SMichal Meloun if (rv < 0) {
349a0a23564SMichal Meloun device_printf(sc->dev, "Cannot pack AVI infoframe: %zd\n", rv);
350a0a23564SMichal Meloun return;
351a0a23564SMichal Meloun }
352a0a23564SMichal Meloun hdr = buf + 0;
353a0a23564SMichal Meloun pb = buf + 3;
354a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER,
355a0a23564SMichal Meloun (hdr[2] << 16) | (hdr[1] << 8) | (hdr[0] << 0));
356a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW,
357a0a23564SMichal Meloun (pb[3] << 24) |(pb[2] << 16) | (pb[1] << 8) | (pb[0] << 0));
358a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH,
359a0a23564SMichal Meloun (pb[6] << 16) | (pb[5] << 8) | (pb[4] << 0));
360a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW,
361a0a23564SMichal Meloun (pb[10] << 24) |(pb[9] << 16) | (pb[8] << 8) | (pb[7] << 0));
362a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH,
363a0a23564SMichal Meloun (pb[13] << 16) | (pb[12] << 8) | (pb[11] << 0));
364a0a23564SMichal Meloun
365a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL,
366a0a23564SMichal Meloun AVI_INFOFRAME_CTRL_ENABLE);
367a0a23564SMichal Meloun }
368a0a23564SMichal Meloun
369a0a23564SMichal Meloun static void
audio_setup_infoframe(struct hdmi_softc * sc)370a0a23564SMichal Meloun audio_setup_infoframe(struct hdmi_softc *sc)
371a0a23564SMichal Meloun {
372a0a23564SMichal Meloun struct hdmi_audio_infoframe frame;
373a0a23564SMichal Meloun uint8_t buf[14], *hdr, *pb;
374a0a23564SMichal Meloun ssize_t rv;
375a0a23564SMichal Meloun
376a0a23564SMichal Meloun rv = hdmi_audio_infoframe_init(&frame);
377a0a23564SMichal Meloun frame.channels = sc->audio_chans;
378a0a23564SMichal Meloun rv = hdmi_audio_infoframe_pack(&frame, buf, sizeof(buf));
379a0a23564SMichal Meloun if (rv < 0) {
380a0a23564SMichal Meloun device_printf(sc->dev, "Cannot pack audio infoframe\n");
381a0a23564SMichal Meloun return;
382a0a23564SMichal Meloun }
383a0a23564SMichal Meloun hdr = buf + 0;
384a0a23564SMichal Meloun pb = buf + 3;
385a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER,
386a0a23564SMichal Meloun (hdr[2] << 16) | (hdr[1] << 8) | (hdr[0] << 0));
387a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW,
388a0a23564SMichal Meloun (pb[3] << 24) |(pb[2] << 16) | (pb[1] << 8) | (pb[0] << 0));
389a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH,
390a0a23564SMichal Meloun (pb[5] << 8) | (pb[4] << 0));
391a0a23564SMichal Meloun
392a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL,
393a0a23564SMichal Meloun AUDIO_INFOFRAME_CTRL_ENABLE);
394a0a23564SMichal Meloun }
395a0a23564SMichal Meloun
396a0a23564SMichal Meloun /* -------------------------------------------------------------------
397a0a23564SMichal Meloun *
398a0a23564SMichal Meloun * Audio
399a0a23564SMichal Meloun *
400a0a23564SMichal Meloun */
401a0a23564SMichal Meloun static void
init_hda_eld(struct hdmi_softc * sc)402a0a23564SMichal Meloun init_hda_eld(struct hdmi_softc *sc)
403a0a23564SMichal Meloun {
404a0a23564SMichal Meloun size_t size;
405a0a23564SMichal Meloun int i ;
406a0a23564SMichal Meloun uint32_t val;
407a0a23564SMichal Meloun
408a0a23564SMichal Meloun size = drm_eld_size(sc->output.connector.eld);
409a0a23564SMichal Meloun for (i = 0; i < HDMI_ELD_BUFFER_SIZE; i++) {
410a0a23564SMichal Meloun val = i << 8;
411a0a23564SMichal Meloun if (i < size)
412a0a23564SMichal Meloun val |= sc->output.connector.eld[i];
413a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR, val);
414a0a23564SMichal Meloun }
415a0a23564SMichal Meloun WR4(sc,HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE,
416a0a23564SMichal Meloun SOR_AUDIO_HDA_PRESENSE_VALID | SOR_AUDIO_HDA_PRESENSE_PRESENT);
417a0a23564SMichal Meloun }
418a0a23564SMichal Meloun
419a0a23564SMichal Meloun static int
get_audio_regs(int freq,bus_size_t * acr_reg,bus_size_t * nval_reg,bus_size_t * aval_reg)420a0a23564SMichal Meloun get_audio_regs(int freq, bus_size_t *acr_reg, bus_size_t *nval_reg,
421a0a23564SMichal Meloun bus_size_t *aval_reg)
422a0a23564SMichal Meloun {
423a0a23564SMichal Meloun int i;
424a0a23564SMichal Meloun const struct audio_reg *reg;
425a0a23564SMichal Meloun
426a0a23564SMichal Meloun for (i = 0; i < nitems(audio_regs) ; i++) {
427a0a23564SMichal Meloun reg = audio_regs + i;
428a0a23564SMichal Meloun if (reg->audio_clk == freq) {
429a0a23564SMichal Meloun if (acr_reg != NULL)
430a0a23564SMichal Meloun *acr_reg = reg->acr_reg;
431a0a23564SMichal Meloun if (nval_reg != NULL)
432a0a23564SMichal Meloun *nval_reg = reg->nval_reg;
433a0a23564SMichal Meloun if (aval_reg != NULL)
434a0a23564SMichal Meloun *aval_reg = reg->aval_reg;
435a0a23564SMichal Meloun return (0);
436a0a23564SMichal Meloun }
437a0a23564SMichal Meloun }
438a0a23564SMichal Meloun return (ERANGE);
439a0a23564SMichal Meloun }
440a0a23564SMichal Meloun
441a0a23564SMichal Meloun #define FR_BITS 16
442a0a23564SMichal Meloun #define TO_FFP(x) (((int64_t)(x)) << FR_BITS)
443a0a23564SMichal Meloun #define TO_INT(x) ((int)((x) >> FR_BITS))
444a0a23564SMichal Meloun static int
get_hda_cts_n(uint32_t audio_freq_hz,uint32_t pixclk_freq_hz,uint32_t * best_cts,uint32_t * best_n,uint32_t * best_a)445a0a23564SMichal Meloun get_hda_cts_n(uint32_t audio_freq_hz, uint32_t pixclk_freq_hz,
446a0a23564SMichal Meloun uint32_t *best_cts, uint32_t *best_n, uint32_t *best_a)
447a0a23564SMichal Meloun {
448a0a23564SMichal Meloun int min_n;
449a0a23564SMichal Meloun int max_n;
450a0a23564SMichal Meloun int ideal_n;
451a0a23564SMichal Meloun int n;
452a0a23564SMichal Meloun int cts;
453a0a23564SMichal Meloun int aval;
454a0a23564SMichal Meloun int64_t err_f;
455a0a23564SMichal Meloun int64_t min_err_f;
456a0a23564SMichal Meloun int64_t cts_f;
457a0a23564SMichal Meloun int64_t aval_f;
458a0a23564SMichal Meloun int64_t half_f; /* constant 0.5 */
459a0a23564SMichal Meloun bool better_n;
460a0a23564SMichal Meloun
461a0a23564SMichal Meloun /*
462a0a23564SMichal Meloun * All floats are in fixed I48.16 format.
463a0a23564SMichal Meloun *
464a0a23564SMichal Meloun * Ideal ACR interval is 1000 hz (1 ms);
465a0a23564SMichal Meloun * acceptable is 300 hz .. 1500 hz
466a0a23564SMichal Meloun */
467a0a23564SMichal Meloun min_n = 128 * audio_freq_hz / 1500;
468a0a23564SMichal Meloun max_n = 128 * audio_freq_hz / 300;
469a0a23564SMichal Meloun ideal_n = 128 * audio_freq_hz / 1000;
470a0a23564SMichal Meloun min_err_f = TO_FFP(100);
471a0a23564SMichal Meloun half_f = TO_FFP(1) / 2;
472a0a23564SMichal Meloun
473a0a23564SMichal Meloun *best_n = 0;
474a0a23564SMichal Meloun *best_cts = 0;
475a0a23564SMichal Meloun *best_a = 0;
476a0a23564SMichal Meloun
477a0a23564SMichal Meloun for (n = min_n; n <= max_n; n++) {
478a0a23564SMichal Meloun cts_f = TO_FFP(pixclk_freq_hz);
479a0a23564SMichal Meloun cts_f *= n;
480a0a23564SMichal Meloun cts_f /= 128 * audio_freq_hz;
481a0a23564SMichal Meloun cts = TO_INT(cts_f + half_f); /* round */
482a0a23564SMichal Meloun err_f = cts_f - TO_FFP(cts);
483a0a23564SMichal Meloun if (err_f < 0)
484a0a23564SMichal Meloun err_f = -err_f;
485a0a23564SMichal Meloun aval_f = TO_FFP(24000000);
486a0a23564SMichal Meloun aval_f *= n;
487a0a23564SMichal Meloun aval_f /= 128 * audio_freq_hz;
488a0a23564SMichal Meloun aval = TO_INT(aval_f); /* truncate */
489a0a23564SMichal Meloun
490a0a23564SMichal Meloun better_n = abs(n - ideal_n) < abs((int)(*best_n) - ideal_n);
491a0a23564SMichal Meloun if (TO_FFP(aval) == aval_f &&
492a0a23564SMichal Meloun (err_f < min_err_f || (err_f == min_err_f && better_n))) {
493a0a23564SMichal Meloun min_err_f = err_f;
494a0a23564SMichal Meloun *best_n = (uint32_t)n;
495a0a23564SMichal Meloun *best_cts = (uint32_t)cts;
496a0a23564SMichal Meloun *best_a = (uint32_t)aval;
497a0a23564SMichal Meloun
498a0a23564SMichal Meloun if (err_f == 0 && n == ideal_n)
499a0a23564SMichal Meloun break;
500a0a23564SMichal Meloun }
501a0a23564SMichal Meloun }
502a0a23564SMichal Meloun return (0);
503a0a23564SMichal Meloun }
504a0a23564SMichal Meloun #undef FR_BITS
505a0a23564SMichal Meloun #undef TO_FFP
506a0a23564SMichal Meloun #undef TO_INT
507a0a23564SMichal Meloun
508a0a23564SMichal Meloun static int
audio_setup(struct hdmi_softc * sc)509a0a23564SMichal Meloun audio_setup(struct hdmi_softc *sc)
510a0a23564SMichal Meloun {
511a0a23564SMichal Meloun uint32_t val;
512a0a23564SMichal Meloun uint32_t audio_n;
513a0a23564SMichal Meloun uint32_t audio_cts;
514a0a23564SMichal Meloun uint32_t audio_aval;
515a0a23564SMichal Meloun uint64_t hdmi_freq;
516a0a23564SMichal Meloun bus_size_t aval_reg;
517a0a23564SMichal Meloun int rv;
518a0a23564SMichal Meloun
519a0a23564SMichal Meloun if (!sc->hdmi_mode)
520a0a23564SMichal Meloun return (ENOTSUP);
521a0a23564SMichal Meloun rv = get_audio_regs(sc->audio_freq, NULL, NULL, &aval_reg);
522a0a23564SMichal Meloun if (rv != 0) {
523a0a23564SMichal Meloun device_printf(sc->dev, "Unsupported audio frequency.\n");
524a0a23564SMichal Meloun return (rv);
525a0a23564SMichal Meloun }
526a0a23564SMichal Meloun
527a0a23564SMichal Meloun rv = clk_get_freq(sc->clk_hdmi, &hdmi_freq);
528a0a23564SMichal Meloun if (rv != 0) {
529a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get hdmi frequency: %d\n", rv);
530a0a23564SMichal Meloun return (rv);
531a0a23564SMichal Meloun }
532a0a23564SMichal Meloun
533a0a23564SMichal Meloun rv = get_hda_cts_n(sc->audio_freq, hdmi_freq, &audio_cts, &audio_n,
534a0a23564SMichal Meloun &audio_aval);
535a0a23564SMichal Meloun if (rv != 0) {
536a0a23564SMichal Meloun device_printf(sc->dev, "Cannot compute audio coefs: %d\n", rv);
537a0a23564SMichal Meloun return (rv);
538a0a23564SMichal Meloun }
539a0a23564SMichal Meloun
540a0a23564SMichal Meloun /* Audio infoframe. */
541a0a23564SMichal Meloun audio_setup_infoframe(sc);
542a0a23564SMichal Meloun /* Setup audio source */
543a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_CNTRL0,
544a0a23564SMichal Meloun SOR_AUDIO_CNTRL0_SOURCE_SELECT(sc->audio_src_type) |
545a0a23564SMichal Meloun SOR_AUDIO_CNTRL0_INJECT_NULLSMPL);
546a0a23564SMichal Meloun
547a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_AUDIO_SPARE0);
548a0a23564SMichal Meloun val |= SOR_AUDIO_SPARE0_HBR_ENABLE;
549a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_SPARE0, val);
550a0a23564SMichal Meloun
551a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_ACR_CTRL, 0);
552a0a23564SMichal Meloun
553a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_AUDIO_N,
554a0a23564SMichal Meloun AUDIO_N_RESETF |
555a0a23564SMichal Meloun AUDIO_N_GENERATE_ALTERNATE |
556a0a23564SMichal Meloun AUDIO_N_VALUE(audio_n - 1));
557a0a23564SMichal Meloun
558a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH,
559a0a23564SMichal Meloun ACR_SUBPACK_N(audio_n) | ACR_ENABLE);
560a0a23564SMichal Meloun
561a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW,
562a0a23564SMichal Meloun ACR_SUBPACK_CTS(audio_cts));
563a0a23564SMichal Meloun
564a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_SPARE,
565a0a23564SMichal Meloun SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1));
566a0a23564SMichal Meloun
567a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_AUDIO_N);
568a0a23564SMichal Meloun val &= ~AUDIO_N_RESETF;
569a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_AUDIO_N, val);
570a0a23564SMichal Meloun
571a0a23564SMichal Meloun WR4(sc, aval_reg, audio_aval);
572a0a23564SMichal Meloun
573a0a23564SMichal Meloun return (0);
574a0a23564SMichal Meloun }
575a0a23564SMichal Meloun
576a0a23564SMichal Meloun static void
audio_disable(struct hdmi_softc * sc)577a0a23564SMichal Meloun audio_disable(struct hdmi_softc *sc) {
578a0a23564SMichal Meloun uint32_t val;
579a0a23564SMichal Meloun
580a0a23564SMichal Meloun /* Disable audio */
581a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
582a0a23564SMichal Meloun val &= ~GENERIC_CTRL_AUDIO;
583a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL, val);
584a0a23564SMichal Meloun
585a0a23564SMichal Meloun /* Disable audio infoframes */
586a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
587a0a23564SMichal Meloun val &= ~AUDIO_INFOFRAME_CTRL_ENABLE;
588a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, val);
589a0a23564SMichal Meloun }
590a0a23564SMichal Meloun
591a0a23564SMichal Meloun static void
audio_enable(struct hdmi_softc * sc)592a0a23564SMichal Meloun audio_enable(struct hdmi_softc *sc) {
593a0a23564SMichal Meloun uint32_t val;
594a0a23564SMichal Meloun
595a0a23564SMichal Meloun if (!sc->hdmi_mode)
596a0a23564SMichal Meloun audio_disable(sc);
597a0a23564SMichal Meloun
598a0a23564SMichal Meloun /* Enable audio infoframes */
599a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
600a0a23564SMichal Meloun val |= AUDIO_INFOFRAME_CTRL_ENABLE;
601a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, val);
602a0a23564SMichal Meloun
603a0a23564SMichal Meloun /* Enable audio */
604a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
605a0a23564SMichal Meloun val |= GENERIC_CTRL_AUDIO;
606a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL, val);
607a0a23564SMichal Meloun }
608a0a23564SMichal Meloun
609a0a23564SMichal Meloun /* -------------------------------------------------------------------
610a0a23564SMichal Meloun *
611a0a23564SMichal Meloun * HDMI.
612a0a23564SMichal Meloun *
613a0a23564SMichal Meloun */
614a0a23564SMichal Meloun /* Process format change notification from HDA */
615a0a23564SMichal Meloun static void
hda_intr(struct hdmi_softc * sc)616a0a23564SMichal Meloun hda_intr(struct hdmi_softc *sc)
617a0a23564SMichal Meloun {
618a0a23564SMichal Meloun uint32_t val;
619a0a23564SMichal Meloun int rv;
620a0a23564SMichal Meloun
621a0a23564SMichal Meloun if (!sc->hdmi_mode)
622a0a23564SMichal Meloun return;
623a0a23564SMichal Meloun
624a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0);
625a0a23564SMichal Meloun if ((val & (1 << 30)) == 0) {
626a0a23564SMichal Meloun audio_disable(sc);
627a0a23564SMichal Meloun return;
628a0a23564SMichal Meloun }
629a0a23564SMichal Meloun
630a0a23564SMichal Meloun /* XXX Move this to any header */
631a0a23564SMichal Meloun /* Keep in sync with HDA */
632a0a23564SMichal Meloun sc->audio_freq = val & 0x00FFFFFF;
633a0a23564SMichal Meloun sc->audio_chans = (val >> 24) & 0x0f;
634a0a23564SMichal Meloun DRM_DEBUG_KMS("%d channel(s) at %dHz\n", sc->audio_chans,
635a0a23564SMichal Meloun sc->audio_freq);
636a0a23564SMichal Meloun
637a0a23564SMichal Meloun rv = audio_setup(sc);
638a0a23564SMichal Meloun if (rv != 0) {
639a0a23564SMichal Meloun audio_disable(sc);
640a0a23564SMichal Meloun return;
641a0a23564SMichal Meloun }
642a0a23564SMichal Meloun
643a0a23564SMichal Meloun audio_enable(sc);
644a0a23564SMichal Meloun }
645a0a23564SMichal Meloun
646a0a23564SMichal Meloun static void
tmds_init(struct hdmi_softc * sc,const struct tmds_config * tmds)647a0a23564SMichal Meloun tmds_init(struct hdmi_softc *sc, const struct tmds_config *tmds)
648a0a23564SMichal Meloun {
649a0a23564SMichal Meloun
650a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PLL0, tmds->pll0);
651a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PLL1, tmds->pll1);
652a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_PE_CURRENT, tmds->pe_c);
653a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT, tmds->drive_c);
654a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT, tmds->peak_c);
655a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PAD_CTLS0, tmds->pad_ctls);
656a0a23564SMichal Meloun }
657a0a23564SMichal Meloun
658a0a23564SMichal Meloun static int
hdmi_sor_start(struct hdmi_softc * sc,struct drm_display_mode * mode)659a0a23564SMichal Meloun hdmi_sor_start(struct hdmi_softc *sc, struct drm_display_mode *mode)
660a0a23564SMichal Meloun {
661a0a23564SMichal Meloun int i;
662a0a23564SMichal Meloun uint32_t val;
663a0a23564SMichal Meloun
664a0a23564SMichal Meloun /* Enable TMDS macro */
665a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0);
666a0a23564SMichal Meloun val &= ~SOR_PLL0_PWR;
667a0a23564SMichal Meloun val &= ~SOR_PLL0_VCOPD;
668a0a23564SMichal Meloun val &= ~SOR_PLL0_PULLDOWN;
669a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val);
670a0a23564SMichal Meloun DELAY(10);
671a0a23564SMichal Meloun
672a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0);
673a0a23564SMichal Meloun val &= ~SOR_PLL0_PDBG;
674a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val);
675a0a23564SMichal Meloun
676a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PWR, SOR_PWR_SETTING_NEW);
677a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PWR, 0);
678a0a23564SMichal Meloun
679a0a23564SMichal Meloun /* Wait until SOR is ready */
680a0a23564SMichal Meloun for (i = 1000; i > 0; i--) {
681a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_PWR);
682a0a23564SMichal Meloun if ((val & SOR_PWR_SETTING_NEW) == 0)
683a0a23564SMichal Meloun break;
684a0a23564SMichal Meloun DELAY(10);
685a0a23564SMichal Meloun }
686a0a23564SMichal Meloun if (i <= 0) {
687a0a23564SMichal Meloun device_printf(sc->dev, "Timeouted while enabling SOR power.\n");
688a0a23564SMichal Meloun return (ETIMEDOUT);
689a0a23564SMichal Meloun }
690a0a23564SMichal Meloun
691a0a23564SMichal Meloun val = SOR_STATE2_ASY_OWNER(ASY_OWNER_HEAD0) |
692a0a23564SMichal Meloun SOR_STATE2_ASY_SUBOWNER(SUBOWNER_BOTH) |
693a0a23564SMichal Meloun SOR_STATE2_ASY_CRCMODE(ASY_CRCMODE_COMPLETE) |
694a0a23564SMichal Meloun SOR_STATE2_ASY_PROTOCOL(ASY_PROTOCOL_SINGLE_TMDS_A);
695a0a23564SMichal Meloun if (mode->flags & DRM_MODE_FLAG_NHSYNC)
696a0a23564SMichal Meloun val |= SOR_STATE2_ASY_HSYNCPOL_NEG;
697a0a23564SMichal Meloun if (mode->flags & DRM_MODE_FLAG_NVSYNC)
698a0a23564SMichal Meloun val |= SOR_STATE2_ASY_VSYNCPOL_NEG;
699a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_STATE2, val);
700a0a23564SMichal Meloun
701a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_STATE1, SOR_STATE1_ASY_ORMODE_NORMAL |
702a0a23564SMichal Meloun SOR_STATE1_ASY_HEAD_OPMODE(ASY_HEAD_OPMODE_AWAKE));
703a0a23564SMichal Meloun
704a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_STATE0, 0);
705a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_STATE0, SOR_STATE0_UPDATE);
706a0a23564SMichal Meloun
707a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_STATE1);
708a0a23564SMichal Meloun val |= SOR_STATE1_ATTACHED;
709a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_STATE1, val);
710a0a23564SMichal Meloun
711a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_STATE0, 0);
712a0a23564SMichal Meloun
713a0a23564SMichal Meloun return 0;
714a0a23564SMichal Meloun }
715a0a23564SMichal Meloun
716a0a23564SMichal Meloun static int
hdmi_disable(struct hdmi_softc * sc)717a0a23564SMichal Meloun hdmi_disable(struct hdmi_softc *sc)
718a0a23564SMichal Meloun {
719a0a23564SMichal Meloun struct tegra_crtc *crtc;
720a0a23564SMichal Meloun device_t dc;
721a0a23564SMichal Meloun uint32_t val;
722a0a23564SMichal Meloun
723a0a23564SMichal Meloun dc = NULL;
724a0a23564SMichal Meloun if (sc->output.encoder.crtc != NULL) {
725a0a23564SMichal Meloun crtc = container_of(sc->output.encoder.crtc, struct tegra_crtc,
726a0a23564SMichal Meloun drm_crtc);
727a0a23564SMichal Meloun dc = crtc->dev;
728a0a23564SMichal Meloun }
729a0a23564SMichal Meloun
730a0a23564SMichal Meloun if (dc != NULL) {
731a0a23564SMichal Meloun TEGRA_DC_HDMI_ENABLE(dc, false);
732a0a23564SMichal Meloun TEGRA_DC_DISPLAY_ENABLE(dc, false);
733a0a23564SMichal Meloun }
734a0a23564SMichal Meloun audio_disable(sc);
735a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL);
736a0a23564SMichal Meloun val &= ~AVI_INFOFRAME_CTRL_ENABLE;
737a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL, val);
738a0a23564SMichal Meloun
739a0a23564SMichal Meloun /* Disable interrupts */
740a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_INT_ENABLE, 0);
741a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_INT_MASK, 0);
742a0a23564SMichal Meloun
743a0a23564SMichal Meloun return (0);
744a0a23564SMichal Meloun }
745a0a23564SMichal Meloun
746a0a23564SMichal Meloun static int
hdmi_enable(struct hdmi_softc * sc)747a0a23564SMichal Meloun hdmi_enable(struct hdmi_softc *sc)
748a0a23564SMichal Meloun {
749a0a23564SMichal Meloun uint64_t freq;
750a0a23564SMichal Meloun struct drm_display_mode *mode;
751a0a23564SMichal Meloun struct tegra_crtc *crtc;
752a0a23564SMichal Meloun uint32_t val, h_sync_width, h_back_porch, h_front_porch, h_pulse_start;
753a0a23564SMichal Meloun uint32_t h_max_ac_packet, div8_2;
754a0a23564SMichal Meloun device_t dc;
755a0a23564SMichal Meloun int i, rv;
756a0a23564SMichal Meloun
757a0a23564SMichal Meloun mode = &sc->output.encoder.crtc->mode;
758a0a23564SMichal Meloun crtc = container_of(sc->output.encoder.crtc, struct tegra_crtc,
759a0a23564SMichal Meloun drm_crtc);
760a0a23564SMichal Meloun dc = crtc->dev;
761a0a23564SMichal Meloun
762a0a23564SMichal Meloun /* Compute all timings first. */
763a0a23564SMichal Meloun sc->pclk = mode->clock * 1000;
764a0a23564SMichal Meloun h_sync_width = mode->hsync_end - mode->hsync_start;
765a0a23564SMichal Meloun h_back_porch = mode->htotal - mode->hsync_end;
766a0a23564SMichal Meloun h_front_porch = mode->hsync_start - mode->hdisplay;
767a0a23564SMichal Meloun h_pulse_start = 1 + h_sync_width + h_back_porch - 10;
768a0a23564SMichal Meloun h_max_ac_packet = (h_sync_width + h_back_porch + h_front_porch -
769a0a23564SMichal Meloun HDMI_REKEY_DEFAULT - 18) / 32;
770a0a23564SMichal Meloun
771a0a23564SMichal Meloun /* Check if HDMI device is connected and detected. */
772a0a23564SMichal Meloun if (sc->output.connector.edid_blob_ptr == NULL) {
773a0a23564SMichal Meloun sc->hdmi_mode = false;
774a0a23564SMichal Meloun } else {
775a0a23564SMichal Meloun sc->hdmi_mode = drm_detect_hdmi_monitor(
776a0a23564SMichal Meloun (struct edid *)sc->output.connector.edid_blob_ptr->data);
777a0a23564SMichal Meloun }
778a0a23564SMichal Meloun
779a0a23564SMichal Meloun /* Get exact HDMI pixel frequency. */
780a0a23564SMichal Meloun rv = clk_get_freq(sc->clk_hdmi, &freq);
781a0a23564SMichal Meloun if (rv != 0) {
782a0a23564SMichal Meloun device_printf(sc->dev,
783a0a23564SMichal Meloun "Cannot get 'hdmi' clock frequency\n");
784a0a23564SMichal Meloun return (rv);
785a0a23564SMichal Meloun }
786a0a23564SMichal Meloun DRM_DEBUG_KMS("HDMI frequency: %llu Hz\n", freq);
787a0a23564SMichal Meloun
788a0a23564SMichal Meloun /* Wakeup SOR power */
789a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0);
790a0a23564SMichal Meloun val &= ~SOR_PLL0_PDBG;
791a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val);
792a0a23564SMichal Meloun DELAY(10);
793a0a23564SMichal Meloun
794a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0);
795a0a23564SMichal Meloun val &= ~SOR_PLL0_PWR;
796a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val);
797a0a23564SMichal Meloun
798a0a23564SMichal Meloun /* Setup timings */
799a0a23564SMichal Meloun TEGRA_DC_SETUP_TIMING(dc, h_pulse_start);
800a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_VSYNC_WINDOW,
801a0a23564SMichal Meloun VSYNC_WINDOW_START(0x200) | VSYNC_WINDOW_END(0x210) |
802a0a23564SMichal Meloun VSYNC_WINDOW_ENABLE);
803a0a23564SMichal Meloun
804a0a23564SMichal Meloun /* Setup video source and adjust video range */
805a0a23564SMichal Meloun val = 0;
806a0a23564SMichal Meloun if (crtc->nvidia_head != 0)
807a0a23564SMichal Meloun HDMI_SRC_DISPLAYB;
808a0a23564SMichal Meloun if ((mode->hdisplay != 640) || (mode->vdisplay != 480))
809a0a23564SMichal Meloun val |= ARM_VIDEO_RANGE_LIMITED;
810a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_INPUT_CONTROL, val);
811a0a23564SMichal Meloun
812a0a23564SMichal Meloun /* Program SOR reference clock - it uses 8.2 fractional divisor */
813a0a23564SMichal Meloun div8_2 = (freq * 4) / 1000000;
814a0a23564SMichal Meloun val = SOR_REFCLK_DIV_INT(div8_2 >> 2) | SOR_REFCLK_DIV_FRAC(div8_2);
815a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_REFCLK, val);
816a0a23564SMichal Meloun
817a0a23564SMichal Meloun /* Setup audio */
818a0a23564SMichal Meloun if (sc->hdmi_mode) {
819a0a23564SMichal Meloun rv = audio_setup(sc);
820a0a23564SMichal Meloun if (rv != 0)
821a0a23564SMichal Meloun sc->hdmi_mode = false;
822a0a23564SMichal Meloun }
823a0a23564SMichal Meloun
824a0a23564SMichal Meloun /* Init HDA ELD */
825a0a23564SMichal Meloun init_hda_eld(sc);
826a0a23564SMichal Meloun val = HDMI_CTRL_REKEY(HDMI_REKEY_DEFAULT);
827a0a23564SMichal Meloun val |= HDMI_CTRL_MAX_AC_PACKET(h_max_ac_packet);
828a0a23564SMichal Meloun if (sc->hdmi_mode)
829a0a23564SMichal Meloun val |= HDMI_CTRL_ENABLE;
830a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_CTRL, val);
831a0a23564SMichal Meloun
832a0a23564SMichal Meloun /* Setup TMDS */
833a0a23564SMichal Meloun for (i = 0; i < sc->n_tmds_configs; i++) {
834a0a23564SMichal Meloun if (sc->pclk <= sc->tmds_config[i].pclk) {
835a0a23564SMichal Meloun tmds_init(sc, sc->tmds_config + i);
836a0a23564SMichal Meloun break;
837a0a23564SMichal Meloun }
838a0a23564SMichal Meloun }
839a0a23564SMichal Meloun
840a0a23564SMichal Meloun /* Program sequencer. */
841a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_SEQ_CTL,
842a0a23564SMichal Meloun SOR_SEQ_PU_PC(0) | SOR_SEQ_PU_PC_ALT(0) |
843a0a23564SMichal Meloun SOR_SEQ_PD_PC(8) | SOR_SEQ_PD_PC_ALT(8));
844a0a23564SMichal Meloun
845a0a23564SMichal Meloun val = SOR_SEQ_INST_WAIT_TIME(1) |
846a0a23564SMichal Meloun SOR_SEQ_INST_WAIT_UNITS(WAIT_UNITS_VSYNC) |
847a0a23564SMichal Meloun SOR_SEQ_INST_HALT |
848a0a23564SMichal Meloun SOR_SEQ_INST_DRIVE_PWM_OUT_LO;
849a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_SEQ_INST(0), val);
850a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_SEQ_INST(8), val);
851a0a23564SMichal Meloun
852a0a23564SMichal Meloun val = RD4(sc,HDMI_NV_PDISP_SOR_CSTM);
853a0a23564SMichal Meloun val &= ~SOR_CSTM_LVDS_ENABLE;
854a0a23564SMichal Meloun val &= ~SOR_CSTM_ROTCLK(~0);
855a0a23564SMichal Meloun val |= SOR_CSTM_ROTCLK(2);
856a0a23564SMichal Meloun val &= ~SOR_CSTM_MODE(~0);
857a0a23564SMichal Meloun val |= SOR_CSTM_MODE(CSTM_MODE_TMDS);
858a0a23564SMichal Meloun val |= SOR_CSTM_PLLDIV;
859a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_CSTM, val);
860a0a23564SMichal Meloun
861a0a23564SMichal Meloun TEGRA_DC_DISPLAY_ENABLE(dc, false);
862a0a23564SMichal Meloun
863a0a23564SMichal Meloun rv = hdmi_sor_start(sc, mode);
864a0a23564SMichal Meloun if (rv != 0)
865a0a23564SMichal Meloun return (rv);
866a0a23564SMichal Meloun
867a0a23564SMichal Meloun TEGRA_DC_HDMI_ENABLE(dc, true);
868a0a23564SMichal Meloun TEGRA_DC_DISPLAY_ENABLE(dc, true);
869a0a23564SMichal Meloun
870a0a23564SMichal Meloun /* Enable HDA codec interrupt */
871a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_INT_MASK, INT_CODEC_SCRATCH0);
872a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_INT_ENABLE, INT_CODEC_SCRATCH0);
873a0a23564SMichal Meloun
874a0a23564SMichal Meloun if (sc->hdmi_mode) {
875a0a23564SMichal Meloun avi_setup_infoframe(sc, mode);
876a0a23564SMichal Meloun audio_enable(sc);
877a0a23564SMichal Meloun }
878a0a23564SMichal Meloun
879a0a23564SMichal Meloun return (0);
880a0a23564SMichal Meloun }
881a0a23564SMichal Meloun
882a0a23564SMichal Meloun /* -------------------------------------------------------------------
883a0a23564SMichal Meloun *
884a0a23564SMichal Meloun * DRM Interface.
885a0a23564SMichal Meloun *
886a0a23564SMichal Meloun */
887a0a23564SMichal Meloun static enum drm_mode_status
hdmi_connector_mode_valid(struct drm_connector * connector,struct drm_display_mode * mode)888a0a23564SMichal Meloun hdmi_connector_mode_valid(struct drm_connector *connector,
889a0a23564SMichal Meloun struct drm_display_mode *mode)
890a0a23564SMichal Meloun {
891a0a23564SMichal Meloun struct tegra_drm_encoder *output;
892a0a23564SMichal Meloun struct hdmi_softc *sc;
893a0a23564SMichal Meloun int rv;
894a0a23564SMichal Meloun uint64_t freq;
895a0a23564SMichal Meloun
896a0a23564SMichal Meloun output = container_of(connector, struct tegra_drm_encoder,
897a0a23564SMichal Meloun connector);
898a0a23564SMichal Meloun sc = device_get_softc(output->dev);
899a0a23564SMichal Meloun
900a0a23564SMichal Meloun freq = HDMI_DC_CLOCK_MULTIPIER * mode->clock * 1000;
901a0a23564SMichal Meloun rv = clk_test_freq(sc->clk_parent, freq, 0);
902a0a23564SMichal Meloun DRM_DEBUG_KMS("Test HDMI frequency: %u kHz, rv: %d\n", mode->clock, rv);
903a0a23564SMichal Meloun if (rv != 0)
904a0a23564SMichal Meloun return (MODE_NOCLOCK);
905a0a23564SMichal Meloun
906a0a23564SMichal Meloun return (MODE_OK);
907a0a23564SMichal Meloun }
908a0a23564SMichal Meloun
909a0a23564SMichal Meloun static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
910a0a23564SMichal Meloun .get_modes = tegra_drm_connector_get_modes,
911a0a23564SMichal Meloun .mode_valid = hdmi_connector_mode_valid,
912a0a23564SMichal Meloun .best_encoder = tegra_drm_connector_best_encoder,
913a0a23564SMichal Meloun };
914a0a23564SMichal Meloun
915a0a23564SMichal Meloun static const struct drm_connector_funcs hdmi_connector_funcs = {
916a0a23564SMichal Meloun .dpms = drm_helper_connector_dpms,
917a0a23564SMichal Meloun .detect = tegra_drm_connector_detect,
918a0a23564SMichal Meloun .fill_modes = drm_helper_probe_single_connector_modes,
919a0a23564SMichal Meloun .destroy = drm_connector_cleanup,
920a0a23564SMichal Meloun };
921a0a23564SMichal Meloun
922a0a23564SMichal Meloun static const struct drm_encoder_funcs hdmi_encoder_funcs = {
923a0a23564SMichal Meloun .destroy = drm_encoder_cleanup,
924a0a23564SMichal Meloun };
925a0a23564SMichal Meloun
926a0a23564SMichal Meloun static void
hdmi_encoder_dpms(struct drm_encoder * encoder,int mode)927a0a23564SMichal Meloun hdmi_encoder_dpms(struct drm_encoder *encoder, int mode)
928a0a23564SMichal Meloun {
929a0a23564SMichal Meloun
930a0a23564SMichal Meloun /* Empty function. */
931a0a23564SMichal Meloun }
932a0a23564SMichal Meloun
933a0a23564SMichal Meloun static bool
hdmi_encoder_mode_fixup(struct drm_encoder * encoder,const struct drm_display_mode * mode,struct drm_display_mode * adjusted)934a0a23564SMichal Meloun hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
935a0a23564SMichal Meloun const struct drm_display_mode *mode,
936a0a23564SMichal Meloun struct drm_display_mode *adjusted)
937a0a23564SMichal Meloun {
938a0a23564SMichal Meloun
939a0a23564SMichal Meloun return (true);
940a0a23564SMichal Meloun }
941a0a23564SMichal Meloun
942a0a23564SMichal Meloun static void
hdmi_encoder_prepare(struct drm_encoder * encoder)943a0a23564SMichal Meloun hdmi_encoder_prepare(struct drm_encoder *encoder)
944a0a23564SMichal Meloun {
945a0a23564SMichal Meloun
946a0a23564SMichal Meloun /* Empty function. */
947a0a23564SMichal Meloun }
948a0a23564SMichal Meloun
949a0a23564SMichal Meloun static void
hdmi_encoder_commit(struct drm_encoder * encoder)950a0a23564SMichal Meloun hdmi_encoder_commit(struct drm_encoder *encoder)
951a0a23564SMichal Meloun {
952a0a23564SMichal Meloun
953a0a23564SMichal Meloun /* Empty function. */
954a0a23564SMichal Meloun }
955a0a23564SMichal Meloun
956a0a23564SMichal Meloun static void
hdmi_encoder_mode_set(struct drm_encoder * encoder,struct drm_display_mode * mode,struct drm_display_mode * adjusted)957a0a23564SMichal Meloun hdmi_encoder_mode_set(struct drm_encoder *encoder,
958a0a23564SMichal Meloun struct drm_display_mode *mode, struct drm_display_mode *adjusted)
959a0a23564SMichal Meloun {
960a0a23564SMichal Meloun struct tegra_drm_encoder *output;
961a0a23564SMichal Meloun struct hdmi_softc *sc;
962a0a23564SMichal Meloun int rv;
963a0a23564SMichal Meloun
964a0a23564SMichal Meloun output = container_of(encoder, struct tegra_drm_encoder, encoder);
965a0a23564SMichal Meloun sc = device_get_softc(output->dev);
966a0a23564SMichal Meloun rv = hdmi_enable(sc);
967a0a23564SMichal Meloun if (rv != 0)
968a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable HDMI port\n");
969a0a23564SMichal Meloun
970a0a23564SMichal Meloun }
971a0a23564SMichal Meloun
972a0a23564SMichal Meloun static void
hdmi_encoder_disable(struct drm_encoder * encoder)973a0a23564SMichal Meloun hdmi_encoder_disable(struct drm_encoder *encoder)
974a0a23564SMichal Meloun {
975a0a23564SMichal Meloun struct tegra_drm_encoder *output;
976a0a23564SMichal Meloun struct hdmi_softc *sc;
977a0a23564SMichal Meloun int rv;
978a0a23564SMichal Meloun
979a0a23564SMichal Meloun output = container_of(encoder, struct tegra_drm_encoder, encoder);
980a0a23564SMichal Meloun sc = device_get_softc(output->dev);
981a0a23564SMichal Meloun if (sc == NULL)
982a0a23564SMichal Meloun return;
983a0a23564SMichal Meloun rv = hdmi_disable(sc);
984a0a23564SMichal Meloun if (rv != 0)
985a0a23564SMichal Meloun device_printf(sc->dev, "Cannot disable HDMI port\n");
986a0a23564SMichal Meloun }
987a0a23564SMichal Meloun
988a0a23564SMichal Meloun static const struct drm_encoder_helper_funcs hdmi_encoder_helper_funcs = {
989a0a23564SMichal Meloun .dpms = hdmi_encoder_dpms,
990a0a23564SMichal Meloun .mode_fixup = hdmi_encoder_mode_fixup,
991a0a23564SMichal Meloun .prepare = hdmi_encoder_prepare,
992a0a23564SMichal Meloun .commit = hdmi_encoder_commit,
993a0a23564SMichal Meloun .mode_set = hdmi_encoder_mode_set,
994a0a23564SMichal Meloun .disable = hdmi_encoder_disable,
995a0a23564SMichal Meloun };
996a0a23564SMichal Meloun
997a0a23564SMichal Meloun /* -------------------------------------------------------------------
998a0a23564SMichal Meloun *
999a0a23564SMichal Meloun * Bus and infrastructure.
1000a0a23564SMichal Meloun *
1001a0a23564SMichal Meloun */
1002a0a23564SMichal Meloun static int
hdmi_init_client(device_t dev,device_t host1x,struct tegra_drm * drm)1003a0a23564SMichal Meloun hdmi_init_client(device_t dev, device_t host1x, struct tegra_drm *drm)
1004a0a23564SMichal Meloun {
1005a0a23564SMichal Meloun struct hdmi_softc *sc;
1006a0a23564SMichal Meloun phandle_t node;
1007a0a23564SMichal Meloun int rv;
1008a0a23564SMichal Meloun
1009a0a23564SMichal Meloun sc = device_get_softc(dev);
1010a0a23564SMichal Meloun node = ofw_bus_get_node(sc->dev);
1011a0a23564SMichal Meloun sc->drm = drm;
1012a0a23564SMichal Meloun sc->output.setup_clock = &hdmi_setup_clock;
1013a0a23564SMichal Meloun
1014a0a23564SMichal Meloun rv = tegra_drm_encoder_attach(&sc->output, node);
1015a0a23564SMichal Meloun if (rv != 0) {
1016a0a23564SMichal Meloun device_printf(dev, "Cannot attach output connector\n");
1017a0a23564SMichal Meloun return(ENXIO);
1018a0a23564SMichal Meloun }
1019a0a23564SMichal Meloun
1020a0a23564SMichal Meloun /* Connect this encoder + connector to DRM. */
1021a0a23564SMichal Meloun drm_connector_init(&drm->drm_dev, &sc->output.connector,
1022a0a23564SMichal Meloun &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
1023a0a23564SMichal Meloun
1024a0a23564SMichal Meloun drm_connector_helper_add(&sc->output.connector,
1025a0a23564SMichal Meloun &hdmi_connector_helper_funcs);
1026a0a23564SMichal Meloun
1027a0a23564SMichal Meloun sc->output.connector.dpms = DRM_MODE_DPMS_OFF;
1028a0a23564SMichal Meloun
1029a0a23564SMichal Meloun drm_encoder_init(&drm->drm_dev, &sc->output.encoder,
1030a0a23564SMichal Meloun &hdmi_encoder_funcs, DRM_MODE_ENCODER_TMDS);
1031a0a23564SMichal Meloun
1032a0a23564SMichal Meloun drm_encoder_helper_add(&sc->output.encoder, &hdmi_encoder_helper_funcs);
1033a0a23564SMichal Meloun
1034a0a23564SMichal Meloun drm_mode_connector_attach_encoder(&sc->output.connector,
1035a0a23564SMichal Meloun &sc->output.encoder);
1036a0a23564SMichal Meloun
1037a0a23564SMichal Meloun rv = tegra_drm_encoder_init(&sc->output, drm);
1038a0a23564SMichal Meloun if (rv < 0) {
1039a0a23564SMichal Meloun device_printf(sc->dev, "Unable to init HDMI output\n");
1040a0a23564SMichal Meloun return (rv);
1041a0a23564SMichal Meloun }
1042a0a23564SMichal Meloun sc->output.encoder.possible_crtcs = 0x3;
1043a0a23564SMichal Meloun return (0);
1044a0a23564SMichal Meloun }
1045a0a23564SMichal Meloun
1046a0a23564SMichal Meloun static int
hdmi_exit_client(device_t dev,device_t host1x,struct tegra_drm * drm)1047a0a23564SMichal Meloun hdmi_exit_client(device_t dev, device_t host1x, struct tegra_drm *drm)
1048a0a23564SMichal Meloun {
1049a0a23564SMichal Meloun struct hdmi_softc *sc;
1050a0a23564SMichal Meloun
1051a0a23564SMichal Meloun sc = device_get_softc(dev);
1052a0a23564SMichal Meloun tegra_drm_encoder_exit(&sc->output, drm);
1053a0a23564SMichal Meloun return (0);
1054a0a23564SMichal Meloun }
1055a0a23564SMichal Meloun
1056a0a23564SMichal Meloun static int
get_fdt_resources(struct hdmi_softc * sc,phandle_t node)1057a0a23564SMichal Meloun get_fdt_resources(struct hdmi_softc *sc, phandle_t node)
1058a0a23564SMichal Meloun {
1059a0a23564SMichal Meloun int rv;
1060a0a23564SMichal Meloun
1061a0a23564SMichal Meloun rv = regulator_get_by_ofw_property(sc->dev, 0, "hdmi-supply",
1062a0a23564SMichal Meloun &sc->supply_hdmi);
1063a0a23564SMichal Meloun if (rv != 0) {
1064a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get 'hdmi' regulator\n");
1065a0a23564SMichal Meloun return (ENXIO);
1066a0a23564SMichal Meloun }
1067a0a23564SMichal Meloun rv = regulator_get_by_ofw_property(sc->dev,0, "pll-supply",
1068a0a23564SMichal Meloun &sc->supply_pll);
1069a0a23564SMichal Meloun if (rv != 0) {
1070a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get 'pll' regulator\n");
1071a0a23564SMichal Meloun return (ENXIO);
1072a0a23564SMichal Meloun }
1073a0a23564SMichal Meloun rv = regulator_get_by_ofw_property(sc->dev, 0, "vdd-supply",
1074a0a23564SMichal Meloun &sc->supply_vdd);
1075a0a23564SMichal Meloun if (rv != 0) {
1076a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get 'vdd' regulator\n");
1077a0a23564SMichal Meloun return (ENXIO);
1078a0a23564SMichal Meloun }
1079a0a23564SMichal Meloun
1080a0a23564SMichal Meloun rv = hwreset_get_by_ofw_name(sc->dev, 0, "hdmi", &sc->hwreset_hdmi);
1081a0a23564SMichal Meloun if (rv != 0) {
1082a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get 'hdmi' reset\n");
1083a0a23564SMichal Meloun return (ENXIO);
1084a0a23564SMichal Meloun }
1085a0a23564SMichal Meloun rv = clk_get_by_ofw_name(sc->dev, 0, "parent", &sc->clk_parent);
1086a0a23564SMichal Meloun if (rv != 0) {
1087a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get 'parent' clock\n");
1088a0a23564SMichal Meloun return (ENXIO);
1089a0a23564SMichal Meloun }
1090a0a23564SMichal Meloun rv = clk_get_by_ofw_name(sc->dev, 0, "hdmi", &sc->clk_hdmi);
1091a0a23564SMichal Meloun if (rv != 0) {
1092a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get 'hdmi' clock\n");
1093a0a23564SMichal Meloun return (ENXIO);
1094a0a23564SMichal Meloun }
1095a0a23564SMichal Meloun
1096a0a23564SMichal Meloun return (0);
1097a0a23564SMichal Meloun }
1098a0a23564SMichal Meloun
1099a0a23564SMichal Meloun static int
enable_fdt_resources(struct hdmi_softc * sc)1100a0a23564SMichal Meloun enable_fdt_resources(struct hdmi_softc *sc)
1101a0a23564SMichal Meloun {
1102a0a23564SMichal Meloun int rv;
1103a0a23564SMichal Meloun
1104a0a23564SMichal Meloun rv = clk_set_parent_by_clk(sc->clk_hdmi, sc->clk_parent);
1105a0a23564SMichal Meloun if (rv != 0) {
1106a0a23564SMichal Meloun device_printf(sc->dev,
1107a0a23564SMichal Meloun "Cannot set parent for 'hdmi' clock\n");
1108a0a23564SMichal Meloun return (rv);
1109a0a23564SMichal Meloun }
1110a0a23564SMichal Meloun
1111a0a23564SMichal Meloun /* 594 MHz is arbitrarily selected value */
1112a0a23564SMichal Meloun rv = clk_set_freq(sc->clk_parent, 594000000, 0);
1113a0a23564SMichal Meloun if (rv != 0) {
1114a0a23564SMichal Meloun device_printf(sc->dev,
1115a0a23564SMichal Meloun "Cannot set frequency for 'hdmi' parent clock\n");
1116a0a23564SMichal Meloun return (rv);
1117a0a23564SMichal Meloun }
1118a0a23564SMichal Meloun rv = clk_set_freq(sc->clk_hdmi, 594000000 / 4, 0);
1119a0a23564SMichal Meloun if (rv != 0) {
1120a0a23564SMichal Meloun device_printf(sc->dev,
1121a0a23564SMichal Meloun "Cannot set frequency for 'hdmi' parent clock\n");
1122a0a23564SMichal Meloun return (rv);
1123a0a23564SMichal Meloun }
1124a0a23564SMichal Meloun
1125a0a23564SMichal Meloun rv = regulator_enable(sc->supply_hdmi);
1126a0a23564SMichal Meloun if (rv != 0) {
1127a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable 'hdmi' regulator\n");
1128a0a23564SMichal Meloun return (rv);
1129a0a23564SMichal Meloun }
1130a0a23564SMichal Meloun rv = regulator_enable(sc->supply_pll);
1131a0a23564SMichal Meloun if (rv != 0) {
1132a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable 'pll' regulator\n");
1133a0a23564SMichal Meloun return (rv);
1134a0a23564SMichal Meloun }
1135a0a23564SMichal Meloun rv = regulator_enable(sc->supply_vdd);
1136a0a23564SMichal Meloun if (rv != 0) {
1137a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable 'vdd' regulator\n");
1138a0a23564SMichal Meloun return (rv);
1139a0a23564SMichal Meloun }
1140a0a23564SMichal Meloun
1141a0a23564SMichal Meloun rv = clk_enable(sc->clk_hdmi);
1142a0a23564SMichal Meloun if (rv != 0) {
1143a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable 'hdmi' clock\n");
1144a0a23564SMichal Meloun return (rv);
1145a0a23564SMichal Meloun }
1146a0a23564SMichal Meloun
1147a0a23564SMichal Meloun rv = hwreset_deassert(sc->hwreset_hdmi);
1148a0a23564SMichal Meloun if (rv != 0) {
1149a0a23564SMichal Meloun device_printf(sc->dev, "Cannot unreset 'hdmi' reset\n");
1150a0a23564SMichal Meloun return (rv);
1151a0a23564SMichal Meloun }
1152a0a23564SMichal Meloun return (0);
1153a0a23564SMichal Meloun }
1154a0a23564SMichal Meloun
1155a0a23564SMichal Meloun static void
hdmi_intr(void * arg)1156a0a23564SMichal Meloun hdmi_intr(void *arg)
1157a0a23564SMichal Meloun {
1158a0a23564SMichal Meloun struct hdmi_softc *sc;
1159a0a23564SMichal Meloun uint32_t status;
1160a0a23564SMichal Meloun
1161a0a23564SMichal Meloun sc = arg;
1162a0a23564SMichal Meloun
1163a0a23564SMichal Meloun /* Confirm interrupt */
1164a0a23564SMichal Meloun status = RD4(sc, HDMI_NV_PDISP_INT_STATUS);
1165a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_INT_STATUS, status);
1166a0a23564SMichal Meloun
1167a0a23564SMichal Meloun /* process audio verb from HDA */
1168a0a23564SMichal Meloun if (status & INT_CODEC_SCRATCH0)
1169a0a23564SMichal Meloun hda_intr(sc);
1170a0a23564SMichal Meloun }
1171a0a23564SMichal Meloun
1172a0a23564SMichal Meloun static int
hdmi_probe(device_t dev)1173a0a23564SMichal Meloun hdmi_probe(device_t dev)
1174a0a23564SMichal Meloun {
1175a0a23564SMichal Meloun
1176a0a23564SMichal Meloun if (!ofw_bus_status_okay(dev))
1177a0a23564SMichal Meloun return (ENXIO);
1178a0a23564SMichal Meloun
1179a0a23564SMichal Meloun if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
1180a0a23564SMichal Meloun return (ENXIO);
1181a0a23564SMichal Meloun
1182a0a23564SMichal Meloun device_set_desc(dev, "Tegra HDMI");
1183a0a23564SMichal Meloun return (BUS_PROBE_DEFAULT);
1184a0a23564SMichal Meloun }
1185a0a23564SMichal Meloun
1186a0a23564SMichal Meloun static int
hdmi_attach(device_t dev)1187a0a23564SMichal Meloun hdmi_attach(device_t dev)
1188a0a23564SMichal Meloun {
1189a0a23564SMichal Meloun struct hdmi_softc *sc;
1190a0a23564SMichal Meloun phandle_t node;
1191a0a23564SMichal Meloun int rid, rv;
1192a0a23564SMichal Meloun
1193a0a23564SMichal Meloun sc = device_get_softc(dev);
1194a0a23564SMichal Meloun sc->dev = dev;
1195a0a23564SMichal Meloun sc->output.dev = sc->dev;
1196a0a23564SMichal Meloun node = ofw_bus_get_node(sc->dev);
1197a0a23564SMichal Meloun
1198a0a23564SMichal Meloun sc->audio_src_type = SOURCE_SELECT_AUTO;
1199a0a23564SMichal Meloun sc->audio_freq = 44100;
1200a0a23564SMichal Meloun sc->audio_chans = 2;
1201a0a23564SMichal Meloun sc->hdmi_mode = false;
1202a0a23564SMichal Meloun
1203a0a23564SMichal Meloun sc->tmds_config = tegra124_tmds_config;
1204a0a23564SMichal Meloun sc->n_tmds_configs = nitems(tegra124_tmds_config);
1205a0a23564SMichal Meloun
1206a0a23564SMichal Meloun rid = 0;
1207a0a23564SMichal Meloun sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
1208a0a23564SMichal Meloun RF_ACTIVE);
1209a0a23564SMichal Meloun if (sc->mem_res == NULL) {
1210a0a23564SMichal Meloun device_printf(dev, "Cannot allocate memory resources\n");
1211a0a23564SMichal Meloun goto fail;
1212a0a23564SMichal Meloun }
1213a0a23564SMichal Meloun
1214a0a23564SMichal Meloun rid = 0;
1215a0a23564SMichal Meloun sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
1216a0a23564SMichal Meloun if (sc->irq_res == NULL) {
1217a0a23564SMichal Meloun device_printf(dev, "Cannot allocate IRQ resources\n");
1218a0a23564SMichal Meloun goto fail;
1219a0a23564SMichal Meloun }
1220a0a23564SMichal Meloun
1221a0a23564SMichal Meloun rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
1222a0a23564SMichal Meloun NULL, hdmi_intr, sc, &sc->irq_ih);
1223a0a23564SMichal Meloun if (rv != 0) {
1224a0a23564SMichal Meloun device_printf(dev,
1225a0a23564SMichal Meloun "WARNING: unable to register interrupt handler\n");
1226a0a23564SMichal Meloun goto fail;
1227a0a23564SMichal Meloun }
1228a0a23564SMichal Meloun
1229a0a23564SMichal Meloun rv = get_fdt_resources(sc, node);
1230a0a23564SMichal Meloun if (rv != 0) {
1231a0a23564SMichal Meloun device_printf(dev, "Cannot parse FDT resources\n");
1232a0a23564SMichal Meloun goto fail;
1233a0a23564SMichal Meloun }
1234a0a23564SMichal Meloun rv = enable_fdt_resources(sc);
1235a0a23564SMichal Meloun if (rv != 0) {
1236a0a23564SMichal Meloun device_printf(dev, "Cannot enable FDT resources\n");
1237a0a23564SMichal Meloun goto fail;
1238a0a23564SMichal Meloun }
1239a0a23564SMichal Meloun
1240a0a23564SMichal Meloun rv = TEGRA_DRM_REGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
1241a0a23564SMichal Meloun if (rv != 0) {
1242a0a23564SMichal Meloun device_printf(dev, "Cannot register DRM device\n");
1243a0a23564SMichal Meloun goto fail;
1244a0a23564SMichal Meloun }
1245*18250ec6SJohn Baldwin bus_attach_children(dev);
1246*18250ec6SJohn Baldwin return (0);
1247a0a23564SMichal Meloun
1248a0a23564SMichal Meloun fail:
1249a0a23564SMichal Meloun TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
1250a0a23564SMichal Meloun
1251a0a23564SMichal Meloun if (sc->irq_ih != NULL)
1252a0a23564SMichal Meloun bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
1253a0a23564SMichal Meloun if (sc->clk_parent != NULL)
1254a0a23564SMichal Meloun clk_release(sc->clk_parent);
1255a0a23564SMichal Meloun if (sc->clk_hdmi != NULL)
1256a0a23564SMichal Meloun clk_release(sc->clk_hdmi);
1257a0a23564SMichal Meloun if (sc->hwreset_hdmi != NULL)
1258a0a23564SMichal Meloun hwreset_release(sc->hwreset_hdmi);
1259a0a23564SMichal Meloun if (sc->supply_hdmi != NULL)
1260a0a23564SMichal Meloun regulator_release(sc->supply_hdmi);
1261a0a23564SMichal Meloun if (sc->supply_pll != NULL)
1262a0a23564SMichal Meloun regulator_release(sc->supply_pll);
1263a0a23564SMichal Meloun if (sc->supply_vdd != NULL)
1264a0a23564SMichal Meloun regulator_release(sc->supply_vdd);
1265a0a23564SMichal Meloun if (sc->irq_res != NULL)
1266a0a23564SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
1267a0a23564SMichal Meloun if (sc->mem_res != NULL)
1268a0a23564SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
1269a0a23564SMichal Meloun return (ENXIO);
1270a0a23564SMichal Meloun }
1271a0a23564SMichal Meloun
1272a0a23564SMichal Meloun static int
hdmi_detach(device_t dev)1273a0a23564SMichal Meloun hdmi_detach(device_t dev)
1274a0a23564SMichal Meloun {
1275a0a23564SMichal Meloun struct hdmi_softc *sc;
1276d412c076SJohn Baldwin int error;
1277d412c076SJohn Baldwin
1278d412c076SJohn Baldwin error = bus_generic_detach(dev);
1279d412c076SJohn Baldwin if (error != 0)
1280d412c076SJohn Baldwin return (error);
1281d412c076SJohn Baldwin
1282a0a23564SMichal Meloun sc = device_get_softc(dev);
1283a0a23564SMichal Meloun
1284a0a23564SMichal Meloun TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
1285a0a23564SMichal Meloun
1286a0a23564SMichal Meloun if (sc->irq_ih != NULL)
1287a0a23564SMichal Meloun bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
1288a0a23564SMichal Meloun if (sc->clk_parent != NULL)
1289a0a23564SMichal Meloun clk_release(sc->clk_parent);
1290a0a23564SMichal Meloun if (sc->clk_hdmi != NULL)
1291a0a23564SMichal Meloun clk_release(sc->clk_hdmi);
1292a0a23564SMichal Meloun if (sc->hwreset_hdmi != NULL)
1293a0a23564SMichal Meloun hwreset_release(sc->hwreset_hdmi);
1294a0a23564SMichal Meloun if (sc->supply_hdmi != NULL)
1295a0a23564SMichal Meloun regulator_release(sc->supply_hdmi);
1296a0a23564SMichal Meloun if (sc->supply_pll != NULL)
1297a0a23564SMichal Meloun regulator_release(sc->supply_pll);
1298a0a23564SMichal Meloun if (sc->supply_vdd != NULL)
1299a0a23564SMichal Meloun regulator_release(sc->supply_vdd);
1300a0a23564SMichal Meloun if (sc->irq_res != NULL)
1301a0a23564SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
1302a0a23564SMichal Meloun if (sc->mem_res != NULL)
1303a0a23564SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
1304d412c076SJohn Baldwin return (0);
1305a0a23564SMichal Meloun }
1306a0a23564SMichal Meloun
1307a0a23564SMichal Meloun static device_method_t tegra_hdmi_methods[] = {
1308a0a23564SMichal Meloun /* Device interface */
1309a0a23564SMichal Meloun DEVMETHOD(device_probe, hdmi_probe),
1310a0a23564SMichal Meloun DEVMETHOD(device_attach, hdmi_attach),
1311a0a23564SMichal Meloun DEVMETHOD(device_detach, hdmi_detach),
1312a0a23564SMichal Meloun
1313a0a23564SMichal Meloun /* tegra drm interface */
1314a0a23564SMichal Meloun DEVMETHOD(tegra_drm_init_client, hdmi_init_client),
1315a0a23564SMichal Meloun DEVMETHOD(tegra_drm_exit_client, hdmi_exit_client),
1316a0a23564SMichal Meloun
1317a0a23564SMichal Meloun DEVMETHOD_END
1318a0a23564SMichal Meloun };
1319a0a23564SMichal Meloun
1320a0a23564SMichal Meloun DEFINE_CLASS_0(tegra_hdmi, tegra_hdmi_driver, tegra_hdmi_methods,
1321a0a23564SMichal Meloun sizeof(struct hdmi_softc));
1322289f133bSJohn Baldwin DRIVER_MODULE(tegra_hdmi, host1x, tegra_hdmi_driver, 0, 0);
1323