a10_hdmi.c revision 302408
1/*-
2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: stable/11/sys/arm/allwinner/a10_hdmi.c 297627 2016-04-06 23:11:03Z jmcneill $
27 */
28
29/*
30 * Allwinner A10/A20 HDMI TX
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_hdmi.c 297627 2016-04-06 23:11:03Z jmcneill $");
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/bus.h>
39#include <sys/rman.h>
40#include <sys/condvar.h>
41#include <sys/kernel.h>
42#include <sys/module.h>
43
44#include <machine/bus.h>
45
46#include <dev/ofw/ofw_bus.h>
47#include <dev/ofw/ofw_bus_subr.h>
48
49#include <dev/videomode/videomode.h>
50#include <dev/videomode/edidvar.h>
51
52#include <dev/extres/clk/clk.h>
53
54#include "hdmi_if.h"
55
56#define	HDMI_CTRL		0x004
57#define	CTRL_MODULE_EN		(1 << 31)
58#define	HDMI_INT_STATUS		0x008
59#define	HDMI_HPD		0x00c
60#define	HPD_DET			(1 << 0)
61#define	HDMI_VID_CTRL		0x010
62#define	VID_CTRL_VIDEO_EN	(1 << 31)
63#define	VID_CTRL_HDMI_MODE	(1 << 30)
64#define	VID_CTRL_INTERLACE	(1 << 4)
65#define	VID_CTRL_REPEATER_2X	(1 << 0)
66#define	HDMI_VID_TIMING0	0x014
67#define	VID_ACT_V(v)		(((v) - 1) << 16)
68#define	VID_ACT_H(h)		(((h) - 1) << 0)
69#define	HDMI_VID_TIMING1	0x018
70#define	VID_VBP(vbp)		(((vbp) - 1) << 16)
71#define	VID_HBP(hbp)		(((hbp) - 1) << 0)
72#define	HDMI_VID_TIMING2	0x01c
73#define	VID_VFP(vfp)		(((vfp) - 1) << 16)
74#define	VID_HFP(hfp)		(((hfp) - 1) << 0)
75#define	HDMI_VID_TIMING3	0x020
76#define	VID_VSPW(vspw)		(((vspw) - 1) << 16)
77#define	VID_HSPW(hspw)		(((hspw) - 1) << 0)
78#define	HDMI_VID_TIMING4	0x024
79#define	TX_CLOCK_NORMAL		0x03e00000
80#define	VID_VSYNC_ACTSEL	(1 << 1)
81#define	VID_HSYNC_ACTSEL	(1 << 0)
82#define	HDMI_AUD_CTRL		0x040
83#define	AUD_CTRL_EN		(1 << 31)
84#define	AUD_CTRL_RST		(1 << 30)
85#define	HDMI_ADMA_CTRL		0x044
86#define	HDMI_ADMA_MODE		(1 << 31)
87#define	HDMI_ADMA_MODE_DDMA	(0 << 31)
88#define	HDMI_ADMA_MODE_NDMA	(1 << 31)
89#define	HDMI_AUD_FMT		0x048
90#define	AUD_FMT_CH(n)		((n) - 1)
91#define	HDMI_PCM_CTRL		0x04c
92#define	HDMI_AUD_CTS		0x050
93#define	HDMI_AUD_N		0x054
94#define	HDMI_AUD_CH_STATUS0	0x058
95#define	CH_STATUS0_FS_FREQ	(0xf << 24)
96#define	CH_STATUS0_FS_FREQ_48	(2 << 24)
97#define	HDMI_AUD_CH_STATUS1	0x05c
98#define	CH_STATUS1_WORD_LEN	(0x7 << 1)
99#define	CH_STATUS1_WORD_LEN_16	(1 << 1)
100#define	HDMI_AUDIO_RESET_RETRY	1000
101#define	HDMI_AUDIO_CHANNELS	2
102#define	HDMI_AUDIO_CHANNELMAP	0x76543210
103#define	HDMI_AUDIO_N		6144	/* 48 kHz */
104#define	HDMI_AUDIO_CTS(r, n)	((((r) * 10) * ((n) / 128)) / 480)
105#define	HDMI_PADCTRL0		0x200
106#define	PADCTRL0_BIASEN		(1 << 31)
107#define	PADCTRL0_LDOCEN		(1 << 30)
108#define	PADCTRL0_LDODEN		(1 << 29)
109#define	PADCTRL0_PWENC		(1 << 28)
110#define	PADCTRL0_PWEND		(1 << 27)
111#define	PADCTRL0_PWENG		(1 << 26)
112#define	PADCTRL0_CKEN		(1 << 25)
113#define	PADCTRL0_SEN		(1 << 24)
114#define	PADCTRL0_TXEN		(1 << 23)
115#define	HDMI_PADCTRL1		0x204
116#define	PADCTRL1_AMP_OPT	(1 << 23)
117#define	PADCTRL1_AMPCK_OPT	(1 << 22)
118#define	PADCTRL1_DMP_OPT	(1 << 21)
119#define	PADCTRL1_EMP_OPT	(1 << 20)
120#define	PADCTRL1_EMPCK_OPT	(1 << 19)
121#define	PADCTRL1_PWSCK		(1 << 18)
122#define	PADCTRL1_PWSDT		(1 << 17)
123#define	PADCTRL1_REG_CSMPS	(1 << 16)
124#define	PADCTRL1_REG_DEN	(1 << 15)
125#define	PADCTRL1_REG_DENCK	(1 << 14)
126#define	PADCTRL1_REG_PLRCK	(1 << 13)
127#define	PADCTRL1_REG_EMP	(0x7 << 10)
128#define	PADCTRL1_REG_EMP_EN	(0x2 << 10)
129#define	PADCTRL1_REG_CD		(0x3 << 8)
130#define	PADCTRL1_REG_CKSS	(0x3 << 6)
131#define	PADCTRL1_REG_CKSS_1X	(0x1 << 6)
132#define	PADCTRL1_REG_CKSS_2X	(0x0 << 6)
133#define	PADCTRL1_REG_AMP	(0x7 << 3)
134#define	PADCTRL1_REG_AMP_EN	(0x6 << 3)
135#define	PADCTRL1_REG_PLR	(0x7 << 0)
136#define	HDMI_PLLCTRL0		0x208
137#define	PLLCTRL0_PLL_EN		(1 << 31)
138#define	PLLCTRL0_BWS		(1 << 30)
139#define	PLLCTRL0_HV_IS_33	(1 << 29)
140#define	PLLCTRL0_LDO1_EN	(1 << 28)
141#define	PLLCTRL0_LDO2_EN	(1 << 27)
142#define	PLLCTRL0_SDIV2		(1 << 25)
143#define	PLLCTRL0_VCO_GAIN	(0x1 << 22)
144#define	PLLCTRL0_S		(0x7 << 17)
145#define	PLLCTRL0_CP_S		(0xf << 12)
146#define	PLLCTRL0_CS		(0x7 << 8)
147#define	PLLCTRL0_PREDIV(x)	((x) << 4)
148#define	PLLCTRL0_VCO_S		(0x8 << 0)
149#define	HDMI_PLLDBG0		0x20c
150#define	PLLDBG0_CKIN_SEL	(1 << 21)
151#define	PLLDBG0_CKIN_SEL_PLL3	(0 << 21)
152#define	PLLDBG0_CKIN_SEL_PLL7	(1 << 21)
153#define	HDMI_PKTCTRL0		0x2f0
154#define	HDMI_PKTCTRL1		0x2f4
155#define	PKTCTRL_PACKET(n,t)	((t) << ((n) << 2))
156#define	PKT_NULL		0
157#define	PKT_GC			1
158#define	PKT_AVI			2
159#define	PKT_AI			3
160#define	PKT_SPD			5
161#define	PKT_END			15
162#define	DDC_CTRL		0x500
163#define	CTRL_DDC_EN		(1 << 31)
164#define	CTRL_DDC_ACMD_START	(1 << 30)
165#define	CTRL_DDC_FIFO_DIR	(1 << 8)
166#define	CTRL_DDC_FIFO_DIR_READ	(0 << 8)
167#define	CTRL_DDC_FIFO_DIR_WRITE	(1 << 8)
168#define	CTRL_DDC_SWRST		(1 << 0)
169#define	DDC_SLAVE_ADDR		0x504
170#define	SLAVE_ADDR_SEG_SHIFT	24
171#define	SLAVE_ADDR_EDDC_SHIFT	16
172#define	SLAVE_ADDR_OFFSET_SHIFT	8
173#define	SLAVE_ADDR_SHIFT	0
174#define	DDC_INT_STATUS		0x50c
175#define	INT_STATUS_XFER_DONE	(1 << 0)
176#define	DDC_FIFO_CTRL		0x510
177#define	FIFO_CTRL_CLEAR		(1 << 31)
178#define	DDC_BYTE_COUNTER	0x51c
179#define	DDC_COMMAND		0x520
180#define	COMMAND_EOREAD		(4 << 0)
181#define	DDC_CLOCK		0x528
182#define	DDC_CLOCK_M		(1 << 3)
183#define	DDC_CLOCK_N		(5 << 0)
184#define	DDC_FIFO		0x518
185#define	SWRST_DELAY		1000
186#define	DDC_DELAY		1000
187#define	DDC_RETRY		1000
188#define	DDC_BLKLEN		16
189#define	DDC_ADDR		0x50
190#define	EDDC_ADDR		0x60
191#define	EDID_LENGTH		128
192#define	HDMI_ENABLE_DELAY	50000
193#define	DDC_READ_RETRY		4
194#define	EXT_TAG			0x00
195#define	CEA_TAG_ID		0x02
196#define	CEA_DTD			0x03
197#define	DTD_BASIC_AUDIO		(1 << 6)
198#define	CEA_REV			0x02
199#define	CEA_DATA_OFF		0x03
200#define	CEA_DATA_START		4
201#define	BLOCK_TAG(x)		(((x) >> 5) & 0x7)
202#define	BLOCK_TAG_VSDB		3
203#define	BLOCK_LEN(x)		((x) & 0x1f)
204#define	HDMI_VSDB_MINLEN	5
205#define	HDMI_OUI		"\x03\x0c\x00"
206#define	HDMI_OUI_LEN		3
207#define	HDMI_DEFAULT_FREQ	297000000
208
209struct a10hdmi_softc {
210	struct resource		*res;
211
212	struct intr_config_hook	mode_hook;
213
214	uint8_t			edid[EDID_LENGTH];
215
216	int			has_hdmi;
217	int			has_audio;
218
219	clk_t			clk_ahb;
220	clk_t			clk_hdmi;
221	clk_t			clk_lcd;
222};
223
224static struct resource_spec a10hdmi_spec[] = {
225	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
226	{ -1, 0 }
227};
228
229#define	HDMI_READ(sc, reg)		bus_read_4((sc)->res, (reg))
230#define	HDMI_WRITE(sc, reg, val)	bus_write_4((sc)->res, (reg), (val))
231
232static void
233a10hdmi_init(struct a10hdmi_softc *sc)
234{
235	/* Enable the HDMI module */
236	HDMI_WRITE(sc, HDMI_CTRL, CTRL_MODULE_EN);
237
238	/* Configure PLL/DRV settings */
239	HDMI_WRITE(sc, HDMI_PADCTRL0, PADCTRL0_BIASEN | PADCTRL0_LDOCEN |
240	    PADCTRL0_LDODEN | PADCTRL0_PWENC | PADCTRL0_PWEND |
241	    PADCTRL0_PWENG | PADCTRL0_CKEN | PADCTRL0_TXEN);
242	HDMI_WRITE(sc, HDMI_PADCTRL1, PADCTRL1_AMP_OPT | PADCTRL1_AMPCK_OPT |
243	    PADCTRL1_EMP_OPT | PADCTRL1_EMPCK_OPT | PADCTRL1_REG_DEN |
244	    PADCTRL1_REG_DENCK | PADCTRL1_REG_EMP_EN | PADCTRL1_REG_AMP_EN);
245
246	/* Select PLL3 as input clock */
247	HDMI_WRITE(sc, HDMI_PLLDBG0, PLLDBG0_CKIN_SEL_PLL3);
248
249	DELAY(HDMI_ENABLE_DELAY);
250}
251
252static void
253a10hdmi_hpd(void *arg)
254{
255	struct a10hdmi_softc *sc;
256	device_t dev;
257	uint32_t hpd;
258
259	dev = arg;
260	sc = device_get_softc(dev);
261
262	hpd = HDMI_READ(sc, HDMI_HPD);
263	if ((hpd & HPD_DET) == HPD_DET)
264		EVENTHANDLER_INVOKE(hdmi_event, dev, HDMI_EVENT_CONNECTED);
265
266	config_intrhook_disestablish(&sc->mode_hook);
267}
268
269static int
270a10hdmi_probe(device_t dev)
271{
272	if (!ofw_bus_status_okay(dev))
273		return (ENXIO);
274
275	if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-hdmi"))
276		return (ENXIO);
277
278	device_set_desc(dev, "Allwinner HDMI TX");
279	return (BUS_PROBE_DEFAULT);
280}
281
282static int
283a10hdmi_attach(device_t dev)
284{
285	struct a10hdmi_softc *sc;
286	int error;
287
288	sc = device_get_softc(dev);
289
290	if (bus_alloc_resources(dev, a10hdmi_spec, &sc->res)) {
291		device_printf(dev, "cannot allocate resources for device\n");
292		return (ENXIO);
293	}
294
295	/* Setup clocks */
296	error = clk_get_by_ofw_name(dev, "ahb", &sc->clk_ahb);
297	if (error != 0) {
298		device_printf(dev, "cannot find ahb clock\n");
299		return (error);
300	}
301	error = clk_get_by_ofw_name(dev, "hdmi", &sc->clk_hdmi);
302	if (error != 0) {
303		device_printf(dev, "cannot find hdmi clock\n");
304		return (error);
305	}
306	error = clk_get_by_ofw_name(dev, "lcd", &sc->clk_lcd);
307	if (error != 0) {
308		device_printf(dev, "cannot find lcd clock\n");
309	}
310	/* Enable HDMI clock */
311	error = clk_enable(sc->clk_hdmi);
312	if (error != 0) {
313		device_printf(dev, "cannot enable hdmi clock\n");
314		return (error);
315	}
316	/* Gating AHB clock for HDMI */
317	error = clk_enable(sc->clk_ahb);
318	if (error != 0) {
319		device_printf(dev, "cannot enable ahb gate\n");
320		return (error);
321	}
322
323	a10hdmi_init(sc);
324
325	sc->mode_hook.ich_func = a10hdmi_hpd;
326	sc->mode_hook.ich_arg = dev;
327
328	error = config_intrhook_establish(&sc->mode_hook);
329	if (error != 0)
330		return (error);
331
332	return (0);
333}
334
335static int
336a10hdmi_ddc_xfer(struct a10hdmi_softc *sc, uint16_t addr, uint8_t seg,
337    uint8_t off, int len)
338{
339	uint32_t val;
340	int retry;
341
342	/* Set FIFO direction to read */
343	val = HDMI_READ(sc, DDC_CTRL);
344	val &= ~CTRL_DDC_FIFO_DIR;
345	val |= CTRL_DDC_FIFO_DIR_READ;
346	HDMI_WRITE(sc, DDC_CTRL, val);
347
348	/* Setup DDC slave address */
349	val = (addr << SLAVE_ADDR_SHIFT) | (seg << SLAVE_ADDR_SEG_SHIFT) |
350	    (EDDC_ADDR << SLAVE_ADDR_EDDC_SHIFT) |
351	    (off << SLAVE_ADDR_OFFSET_SHIFT);
352	HDMI_WRITE(sc, DDC_SLAVE_ADDR, val);
353
354	/* Clear FIFO */
355	val = HDMI_READ(sc, DDC_FIFO_CTRL);
356	val |= FIFO_CTRL_CLEAR;
357	HDMI_WRITE(sc, DDC_FIFO_CTRL, val);
358
359	/* Set transfer length */
360	HDMI_WRITE(sc, DDC_BYTE_COUNTER, len);
361
362	/* Set command to "Explicit Offset Address Read" */
363	HDMI_WRITE(sc, DDC_COMMAND, COMMAND_EOREAD);
364
365	/* Start transfer */
366	val = HDMI_READ(sc, DDC_CTRL);
367	val |= CTRL_DDC_ACMD_START;
368	HDMI_WRITE(sc, DDC_CTRL, val);
369
370	/* Wait for command to start */
371	retry = DDC_RETRY;
372	while (--retry > 0) {
373		val = HDMI_READ(sc, DDC_CTRL);
374		if ((val & CTRL_DDC_ACMD_START) == 0)
375			break;
376		DELAY(DDC_DELAY);
377	}
378	if (retry == 0)
379		return (ETIMEDOUT);
380
381	/* Ensure that the transfer completed */
382	val = HDMI_READ(sc, DDC_INT_STATUS);
383	if ((val & INT_STATUS_XFER_DONE) == 0)
384		return (EIO);
385
386	return (0);
387}
388
389static int
390a10hdmi_ddc_read(struct a10hdmi_softc *sc, int block, uint8_t *edid)
391{
392	int resid, off, len, error;
393	uint8_t *pbuf;
394
395	pbuf = edid;
396	resid = EDID_LENGTH;
397	off = (block & 1) ? EDID_LENGTH : 0;
398
399	while (resid > 0) {
400		len = min(resid, DDC_BLKLEN);
401		error = a10hdmi_ddc_xfer(sc, DDC_ADDR, block >> 1, off, len);
402		if (error != 0)
403			return (error);
404
405		bus_read_multi_1(sc->res, DDC_FIFO, pbuf, len);
406
407		pbuf += len;
408		off += len;
409		resid -= len;
410	}
411
412	return (0);
413}
414
415static int
416a10hdmi_detect_hdmi_vsdb(uint8_t *edid)
417{
418	int off, p, btag, blen;
419
420	if (edid[EXT_TAG] != CEA_TAG_ID)
421		return (0);
422
423	off = edid[CEA_DATA_OFF];
424
425	/* CEA data block collection starts at byte 4 */
426	if (off <= CEA_DATA_START)
427		return (0);
428
429	/* Parse the CEA data blocks */
430	for (p = CEA_DATA_START; p < off;) {
431		btag = BLOCK_TAG(edid[p]);
432		blen = BLOCK_LEN(edid[p]);
433
434		/* Make sure the length is sane */
435		if (p + blen + 1 > off)
436			break;
437
438		/* Look for a VSDB with the HDMI 24-bit IEEE registration ID */
439		if (btag == BLOCK_TAG_VSDB && blen >= HDMI_VSDB_MINLEN &&
440		    memcmp(&edid[p + 1], HDMI_OUI, HDMI_OUI_LEN) == 0)
441			return (1);
442
443		/* Next data block */
444		p += (1 + blen);
445	}
446
447	return (0);
448}
449
450static void
451a10hdmi_detect_hdmi(struct a10hdmi_softc *sc, int *phdmi, int *paudio)
452{
453	struct edid_info ei;
454	uint8_t edid[EDID_LENGTH];
455	int block;
456
457	*phdmi = *paudio = 0;
458
459	if (edid_parse(sc->edid, &ei) != 0)
460		return;
461
462	/* Scan through extension blocks, looking for a CEA-861 block. */
463	for (block = 1; block <= ei.edid_ext_block_count; block++) {
464		if (a10hdmi_ddc_read(sc, block, edid) != 0)
465			return;
466
467		if (a10hdmi_detect_hdmi_vsdb(edid) != 0) {
468			*phdmi = 1;
469			*paudio = ((edid[CEA_DTD] & DTD_BASIC_AUDIO) != 0);
470			return;
471		}
472	}
473}
474
475static int
476a10hdmi_get_edid(device_t dev, uint8_t **edid, uint32_t *edid_len)
477{
478	struct a10hdmi_softc *sc;
479	int error, retry;
480
481	sc = device_get_softc(dev);
482	retry = DDC_READ_RETRY;
483
484	while (--retry > 0) {
485		/* I2C software reset */
486		HDMI_WRITE(sc, DDC_FIFO_CTRL, 0);
487		HDMI_WRITE(sc, DDC_CTRL, CTRL_DDC_EN | CTRL_DDC_SWRST);
488		DELAY(SWRST_DELAY);
489		if (HDMI_READ(sc, DDC_CTRL) & CTRL_DDC_SWRST) {
490			device_printf(dev, "DDC software reset failed\n");
491			return (ENXIO);
492		}
493
494		/* Configure DDC clock */
495		HDMI_WRITE(sc, DDC_CLOCK, DDC_CLOCK_M | DDC_CLOCK_N);
496
497		/* Read EDID block */
498		error = a10hdmi_ddc_read(sc, 0, sc->edid);
499		if (error == 0) {
500			*edid = sc->edid;
501			*edid_len = sizeof(sc->edid);
502			break;
503		}
504	}
505
506	if (error == 0)
507		a10hdmi_detect_hdmi(sc, &sc->has_hdmi, &sc->has_audio);
508	else
509		sc->has_hdmi = sc->has_audio = 0;
510
511	return (error);
512}
513
514static void
515a10hdmi_set_audiomode(device_t dev, const struct videomode *mode)
516{
517	struct a10hdmi_softc *sc;
518	uint32_t val;
519	int retry;
520
521	sc = device_get_softc(dev);
522
523	/* Disable and reset audio module and wait for reset bit to clear */
524	HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_RST);
525	for (retry = HDMI_AUDIO_RESET_RETRY; retry > 0; retry--) {
526		val = HDMI_READ(sc, HDMI_AUD_CTRL);
527		if ((val & AUD_CTRL_RST) == 0)
528			break;
529	}
530	if (retry == 0) {
531		device_printf(dev, "timeout waiting for audio module\n");
532		return;
533	}
534
535	if (!sc->has_audio)
536		return;
537
538	/* DMA and FIFO control */
539	HDMI_WRITE(sc, HDMI_ADMA_CTRL, HDMI_ADMA_MODE_DDMA);
540
541	/* Audio format control (LPCM, S16LE, stereo) */
542	HDMI_WRITE(sc, HDMI_AUD_FMT, AUD_FMT_CH(HDMI_AUDIO_CHANNELS));
543
544	/* Channel mappings */
545	HDMI_WRITE(sc, HDMI_PCM_CTRL, HDMI_AUDIO_CHANNELMAP);
546
547	/* Clocks */
548	HDMI_WRITE(sc, HDMI_AUD_CTS,
549	    HDMI_AUDIO_CTS(mode->dot_clock, HDMI_AUDIO_N));
550	HDMI_WRITE(sc, HDMI_AUD_N, HDMI_AUDIO_N);
551
552	/* Set sampling frequency to 48 kHz, word length to 16-bit */
553	HDMI_WRITE(sc, HDMI_AUD_CH_STATUS0, CH_STATUS0_FS_FREQ_48);
554	HDMI_WRITE(sc, HDMI_AUD_CH_STATUS1, CH_STATUS1_WORD_LEN_16);
555
556	/* Enable */
557	HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_EN);
558}
559
560static int
561a10hdmi_get_tcon_config(struct a10hdmi_softc *sc, int *div, int *dbl)
562{
563	uint64_t lcd_fin, lcd_fout;
564	clk_t clk_lcd_parent;
565	const char *pname;
566	int error;
567
568	error = clk_get_parent(sc->clk_lcd, &clk_lcd_parent);
569	if (error != 0)
570		return (error);
571
572	/* Get the LCD CH1 special clock 2 divider */
573	error = clk_get_freq(sc->clk_lcd, &lcd_fout);
574	if (error != 0)
575		return (error);
576	error = clk_get_freq(clk_lcd_parent, &lcd_fin);
577	if (error != 0)
578		return (error);
579	*div = lcd_fin / lcd_fout;
580
581	/* Detect LCD CH1 special clock using a 1X or 2X source */
582	/* XXX */
583	pname = clk_get_name(clk_lcd_parent);
584	if (strcmp(pname, "pll3-1x") == 0 || strcmp(pname, "pll7-1x") == 0)
585		*dbl = 0;
586	else
587		*dbl = 1;
588
589	return (0);
590}
591
592static int
593a10hdmi_set_videomode(device_t dev, const struct videomode *mode)
594{
595	struct a10hdmi_softc *sc;
596	int error, clk_div, clk_dbl;
597	int dblscan, hfp, hspw, hbp, vfp, vspw, vbp;
598	uint32_t val;
599
600	sc = device_get_softc(dev);
601	dblscan = !!(mode->flags & VID_DBLSCAN);
602	hfp = mode->hsync_start - mode->hdisplay;
603	hspw = mode->hsync_end - mode->hsync_start;
604	hbp = mode->htotal - mode->hsync_start;
605	vfp = mode->vsync_start - mode->vdisplay;
606	vspw = mode->vsync_end - mode->vsync_start;
607	vbp = mode->vtotal - mode->vsync_start;
608
609	error = a10hdmi_get_tcon_config(sc, &clk_div, &clk_dbl);
610	if (error != 0) {
611		device_printf(dev, "couldn't get tcon config: %d\n", error);
612		return (error);
613	}
614
615	/* Clear interrupt status */
616	HDMI_WRITE(sc, HDMI_INT_STATUS, HDMI_READ(sc, HDMI_INT_STATUS));
617
618	/* Clock setup */
619	val = HDMI_READ(sc, HDMI_PADCTRL1);
620	val &= ~PADCTRL1_REG_CKSS;
621	val |= (clk_dbl ? PADCTRL1_REG_CKSS_2X : PADCTRL1_REG_CKSS_1X);
622	HDMI_WRITE(sc, HDMI_PADCTRL1, val);
623	HDMI_WRITE(sc, HDMI_PLLCTRL0, PLLCTRL0_PLL_EN | PLLCTRL0_BWS |
624	    PLLCTRL0_HV_IS_33 | PLLCTRL0_LDO1_EN | PLLCTRL0_LDO2_EN |
625	    PLLCTRL0_SDIV2 | PLLCTRL0_VCO_GAIN | PLLCTRL0_S |
626	    PLLCTRL0_CP_S | PLLCTRL0_CS | PLLCTRL0_PREDIV(clk_div) |
627	    PLLCTRL0_VCO_S);
628
629	/* Setup display settings */
630	if (bootverbose)
631		device_printf(dev, "HDMI: %s, Audio: %s\n",
632		    sc->has_hdmi ? "yes" : "no", sc->has_audio ? "yes" : "no");
633	val = 0;
634	if (sc->has_hdmi)
635		val |= VID_CTRL_HDMI_MODE;
636	if (mode->flags & VID_INTERLACE)
637		val |= VID_CTRL_INTERLACE;
638	if (mode->flags & VID_DBLSCAN)
639		val |= VID_CTRL_REPEATER_2X;
640	HDMI_WRITE(sc, HDMI_VID_CTRL, val);
641
642	/* Setup display timings */
643	HDMI_WRITE(sc, HDMI_VID_TIMING0,
644	    VID_ACT_V(mode->vdisplay) | VID_ACT_H(mode->hdisplay << dblscan));
645	HDMI_WRITE(sc, HDMI_VID_TIMING1,
646	    VID_VBP(vbp) | VID_HBP(hbp << dblscan));
647	HDMI_WRITE(sc, HDMI_VID_TIMING2,
648	    VID_VFP(vfp) | VID_HFP(hfp << dblscan));
649	HDMI_WRITE(sc, HDMI_VID_TIMING3,
650	    VID_VSPW(vspw) | VID_HSPW(hspw << dblscan));
651	val = TX_CLOCK_NORMAL;
652	if (mode->flags & VID_PVSYNC)
653		val |= VID_VSYNC_ACTSEL;
654	if (mode->flags & VID_PHSYNC)
655		val |= VID_HSYNC_ACTSEL;
656	HDMI_WRITE(sc, HDMI_VID_TIMING4, val);
657
658	/* This is an ordered list of infoframe packets that the HDMI
659	 * transmitter will send. Transmit packets in the following order:
660	 *  1. General control packet
661	 *  2. AVI infoframe
662	 *  3. Audio infoframe
663	 * There are 2 registers with 4 slots each. The list is terminated
664	 * with the special PKT_END marker.
665	 */
666	HDMI_WRITE(sc, HDMI_PKTCTRL0,
667	    PKTCTRL_PACKET(0, PKT_GC) | PKTCTRL_PACKET(1, PKT_AVI) |
668	    PKTCTRL_PACKET(2, PKT_AI) | PKTCTRL_PACKET(3, PKT_END));
669	HDMI_WRITE(sc, HDMI_PKTCTRL1, 0);
670
671	/* Setup audio */
672	a10hdmi_set_audiomode(dev, mode);
673
674	return (0);
675}
676
677static int
678a10hdmi_enable(device_t dev, int onoff)
679{
680	struct a10hdmi_softc *sc;
681	uint32_t val;
682
683	sc = device_get_softc(dev);
684
685	/* Enable or disable video output */
686	val = HDMI_READ(sc, HDMI_VID_CTRL);
687	if (onoff)
688		val |= VID_CTRL_VIDEO_EN;
689	else
690		val &= ~VID_CTRL_VIDEO_EN;
691	HDMI_WRITE(sc, HDMI_VID_CTRL, val);
692
693	return (0);
694}
695
696static device_method_t a10hdmi_methods[] = {
697	/* Device interface */
698	DEVMETHOD(device_probe,		a10hdmi_probe),
699	DEVMETHOD(device_attach,	a10hdmi_attach),
700
701	/* HDMI interface */
702	DEVMETHOD(hdmi_get_edid,	a10hdmi_get_edid),
703	DEVMETHOD(hdmi_set_videomode,	a10hdmi_set_videomode),
704	DEVMETHOD(hdmi_enable,		a10hdmi_enable),
705
706	DEVMETHOD_END
707};
708
709static driver_t a10hdmi_driver = {
710	"a10hdmi",
711	a10hdmi_methods,
712	sizeof(struct a10hdmi_softc),
713};
714
715static devclass_t a10hdmi_devclass;
716
717DRIVER_MODULE(a10hdmi, simplebus, a10hdmi_driver, a10hdmi_devclass, 0, 0);
718MODULE_VERSION(a10hdmi, 1);
719