Index: head/sys/arm/allwinner/a10_hdmi.c =================================================================== --- head/sys/arm/allwinner/a10_hdmi.c (revision 308671) +++ head/sys/arm/allwinner/a10_hdmi.c (revision 308672) @@ -1,718 +1,718 @@ /*- * Copyright (c) 2016 Jared McNeill * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Allwinner A10/A20 HDMI TX */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hdmi_if.h" #define HDMI_CTRL 0x004 #define CTRL_MODULE_EN (1 << 31) #define HDMI_INT_STATUS 0x008 #define HDMI_HPD 0x00c #define HPD_DET (1 << 0) #define HDMI_VID_CTRL 0x010 #define VID_CTRL_VIDEO_EN (1 << 31) #define VID_CTRL_HDMI_MODE (1 << 30) #define VID_CTRL_INTERLACE (1 << 4) #define VID_CTRL_REPEATER_2X (1 << 0) #define HDMI_VID_TIMING0 0x014 #define VID_ACT_V(v) (((v) - 1) << 16) #define VID_ACT_H(h) (((h) - 1) << 0) #define HDMI_VID_TIMING1 0x018 #define VID_VBP(vbp) (((vbp) - 1) << 16) #define VID_HBP(hbp) (((hbp) - 1) << 0) #define HDMI_VID_TIMING2 0x01c #define VID_VFP(vfp) (((vfp) - 1) << 16) #define VID_HFP(hfp) (((hfp) - 1) << 0) #define HDMI_VID_TIMING3 0x020 #define VID_VSPW(vspw) (((vspw) - 1) << 16) #define VID_HSPW(hspw) (((hspw) - 1) << 0) #define HDMI_VID_TIMING4 0x024 #define TX_CLOCK_NORMAL 0x03e00000 #define VID_VSYNC_ACTSEL (1 << 1) #define VID_HSYNC_ACTSEL (1 << 0) #define HDMI_AUD_CTRL 0x040 #define AUD_CTRL_EN (1 << 31) #define AUD_CTRL_RST (1 << 30) #define HDMI_ADMA_CTRL 0x044 #define HDMI_ADMA_MODE (1 << 31) #define HDMI_ADMA_MODE_DDMA (0 << 31) #define HDMI_ADMA_MODE_NDMA (1 << 31) #define HDMI_AUD_FMT 0x048 #define AUD_FMT_CH(n) ((n) - 1) #define HDMI_PCM_CTRL 0x04c #define HDMI_AUD_CTS 0x050 #define HDMI_AUD_N 0x054 #define HDMI_AUD_CH_STATUS0 0x058 #define CH_STATUS0_FS_FREQ (0xf << 24) #define CH_STATUS0_FS_FREQ_48 (2 << 24) #define HDMI_AUD_CH_STATUS1 0x05c #define CH_STATUS1_WORD_LEN (0x7 << 1) #define CH_STATUS1_WORD_LEN_16 (1 << 1) #define HDMI_AUDIO_RESET_RETRY 1000 #define HDMI_AUDIO_CHANNELS 2 #define HDMI_AUDIO_CHANNELMAP 0x76543210 #define HDMI_AUDIO_N 6144 /* 48 kHz */ #define HDMI_AUDIO_CTS(r, n) ((((r) * 10) * ((n) / 128)) / 480) #define HDMI_PADCTRL0 0x200 #define PADCTRL0_BIASEN (1 << 31) #define PADCTRL0_LDOCEN (1 << 30) #define PADCTRL0_LDODEN (1 << 29) #define PADCTRL0_PWENC (1 << 28) #define PADCTRL0_PWEND (1 << 27) #define PADCTRL0_PWENG (1 << 26) #define PADCTRL0_CKEN (1 << 25) #define PADCTRL0_SEN (1 << 24) #define PADCTRL0_TXEN (1 << 23) #define HDMI_PADCTRL1 0x204 #define PADCTRL1_AMP_OPT (1 << 23) #define PADCTRL1_AMPCK_OPT (1 << 22) #define PADCTRL1_DMP_OPT (1 << 21) #define PADCTRL1_EMP_OPT (1 << 20) #define PADCTRL1_EMPCK_OPT (1 << 19) #define PADCTRL1_PWSCK (1 << 18) #define PADCTRL1_PWSDT (1 << 17) #define PADCTRL1_REG_CSMPS (1 << 16) #define PADCTRL1_REG_DEN (1 << 15) #define PADCTRL1_REG_DENCK (1 << 14) #define PADCTRL1_REG_PLRCK (1 << 13) #define PADCTRL1_REG_EMP (0x7 << 10) #define PADCTRL1_REG_EMP_EN (0x2 << 10) #define PADCTRL1_REG_CD (0x3 << 8) #define PADCTRL1_REG_CKSS (0x3 << 6) #define PADCTRL1_REG_CKSS_1X (0x1 << 6) #define PADCTRL1_REG_CKSS_2X (0x0 << 6) #define PADCTRL1_REG_AMP (0x7 << 3) #define PADCTRL1_REG_AMP_EN (0x6 << 3) #define PADCTRL1_REG_PLR (0x7 << 0) #define HDMI_PLLCTRL0 0x208 #define PLLCTRL0_PLL_EN (1 << 31) #define PLLCTRL0_BWS (1 << 30) #define PLLCTRL0_HV_IS_33 (1 << 29) #define PLLCTRL0_LDO1_EN (1 << 28) #define PLLCTRL0_LDO2_EN (1 << 27) #define PLLCTRL0_SDIV2 (1 << 25) #define PLLCTRL0_VCO_GAIN (0x1 << 22) #define PLLCTRL0_S (0x7 << 17) #define PLLCTRL0_CP_S (0xf << 12) #define PLLCTRL0_CS (0x7 << 8) #define PLLCTRL0_PREDIV(x) ((x) << 4) #define PLLCTRL0_VCO_S (0x8 << 0) #define HDMI_PLLDBG0 0x20c #define PLLDBG0_CKIN_SEL (1 << 21) #define PLLDBG0_CKIN_SEL_PLL3 (0 << 21) #define PLLDBG0_CKIN_SEL_PLL7 (1 << 21) #define HDMI_PKTCTRL0 0x2f0 #define HDMI_PKTCTRL1 0x2f4 #define PKTCTRL_PACKET(n,t) ((t) << ((n) << 2)) #define PKT_NULL 0 #define PKT_GC 1 #define PKT_AVI 2 #define PKT_AI 3 #define PKT_SPD 5 #define PKT_END 15 #define DDC_CTRL 0x500 #define CTRL_DDC_EN (1 << 31) #define CTRL_DDC_ACMD_START (1 << 30) #define CTRL_DDC_FIFO_DIR (1 << 8) #define CTRL_DDC_FIFO_DIR_READ (0 << 8) #define CTRL_DDC_FIFO_DIR_WRITE (1 << 8) #define CTRL_DDC_SWRST (1 << 0) #define DDC_SLAVE_ADDR 0x504 #define SLAVE_ADDR_SEG_SHIFT 24 #define SLAVE_ADDR_EDDC_SHIFT 16 #define SLAVE_ADDR_OFFSET_SHIFT 8 #define SLAVE_ADDR_SHIFT 0 #define DDC_INT_STATUS 0x50c #define INT_STATUS_XFER_DONE (1 << 0) #define DDC_FIFO_CTRL 0x510 #define FIFO_CTRL_CLEAR (1 << 31) #define DDC_BYTE_COUNTER 0x51c #define DDC_COMMAND 0x520 #define COMMAND_EOREAD (4 << 0) #define DDC_CLOCK 0x528 #define DDC_CLOCK_M (1 << 3) #define DDC_CLOCK_N (5 << 0) #define DDC_FIFO 0x518 #define SWRST_DELAY 1000 #define DDC_DELAY 1000 #define DDC_RETRY 1000 #define DDC_BLKLEN 16 #define DDC_ADDR 0x50 #define EDDC_ADDR 0x60 #define EDID_LENGTH 128 #define HDMI_ENABLE_DELAY 50000 #define DDC_READ_RETRY 4 #define EXT_TAG 0x00 #define CEA_TAG_ID 0x02 #define CEA_DTD 0x03 #define DTD_BASIC_AUDIO (1 << 6) #define CEA_REV 0x02 #define CEA_DATA_OFF 0x03 #define CEA_DATA_START 4 #define BLOCK_TAG(x) (((x) >> 5) & 0x7) #define BLOCK_TAG_VSDB 3 #define BLOCK_LEN(x) ((x) & 0x1f) #define HDMI_VSDB_MINLEN 5 #define HDMI_OUI "\x03\x0c\x00" #define HDMI_OUI_LEN 3 #define HDMI_DEFAULT_FREQ 297000000 struct a10hdmi_softc { struct resource *res; struct intr_config_hook mode_hook; uint8_t edid[EDID_LENGTH]; int has_hdmi; int has_audio; clk_t clk_ahb; clk_t clk_hdmi; clk_t clk_lcd; }; static struct resource_spec a10hdmi_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; #define HDMI_READ(sc, reg) bus_read_4((sc)->res, (reg)) #define HDMI_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) static void a10hdmi_init(struct a10hdmi_softc *sc) { /* Enable the HDMI module */ HDMI_WRITE(sc, HDMI_CTRL, CTRL_MODULE_EN); /* Configure PLL/DRV settings */ HDMI_WRITE(sc, HDMI_PADCTRL0, PADCTRL0_BIASEN | PADCTRL0_LDOCEN | PADCTRL0_LDODEN | PADCTRL0_PWENC | PADCTRL0_PWEND | PADCTRL0_PWENG | PADCTRL0_CKEN | PADCTRL0_TXEN); HDMI_WRITE(sc, HDMI_PADCTRL1, PADCTRL1_AMP_OPT | PADCTRL1_AMPCK_OPT | PADCTRL1_EMP_OPT | PADCTRL1_EMPCK_OPT | PADCTRL1_REG_DEN | PADCTRL1_REG_DENCK | PADCTRL1_REG_EMP_EN | PADCTRL1_REG_AMP_EN); /* Select PLL3 as input clock */ HDMI_WRITE(sc, HDMI_PLLDBG0, PLLDBG0_CKIN_SEL_PLL3); DELAY(HDMI_ENABLE_DELAY); } static void a10hdmi_hpd(void *arg) { struct a10hdmi_softc *sc; device_t dev; uint32_t hpd; dev = arg; sc = device_get_softc(dev); hpd = HDMI_READ(sc, HDMI_HPD); if ((hpd & HPD_DET) == HPD_DET) EVENTHANDLER_INVOKE(hdmi_event, dev, HDMI_EVENT_CONNECTED); config_intrhook_disestablish(&sc->mode_hook); } static int a10hdmi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-hdmi")) return (ENXIO); device_set_desc(dev, "Allwinner HDMI TX"); return (BUS_PROBE_DEFAULT); } static int a10hdmi_attach(device_t dev) { struct a10hdmi_softc *sc; int error; sc = device_get_softc(dev); if (bus_alloc_resources(dev, a10hdmi_spec, &sc->res)) { device_printf(dev, "cannot allocate resources for device\n"); return (ENXIO); } /* Setup clocks */ error = clk_get_by_ofw_name(dev, 0, "ahb", &sc->clk_ahb); if (error != 0) { device_printf(dev, "cannot find ahb clock\n"); return (error); } error = clk_get_by_ofw_name(dev, 0, "hdmi", &sc->clk_hdmi); if (error != 0) { device_printf(dev, "cannot find hdmi clock\n"); return (error); } error = clk_get_by_ofw_name(dev, 0, "lcd", &sc->clk_lcd); if (error != 0) { device_printf(dev, "cannot find lcd clock\n"); } /* Enable HDMI clock */ error = clk_enable(sc->clk_hdmi); if (error != 0) { device_printf(dev, "cannot enable hdmi clock\n"); return (error); } /* Gating AHB clock for HDMI */ error = clk_enable(sc->clk_ahb); if (error != 0) { device_printf(dev, "cannot enable ahb gate\n"); return (error); } a10hdmi_init(sc); sc->mode_hook.ich_func = a10hdmi_hpd; sc->mode_hook.ich_arg = dev; error = config_intrhook_establish(&sc->mode_hook); if (error != 0) return (error); return (0); } static int a10hdmi_ddc_xfer(struct a10hdmi_softc *sc, uint16_t addr, uint8_t seg, uint8_t off, int len) { uint32_t val; int retry; /* Set FIFO direction to read */ val = HDMI_READ(sc, DDC_CTRL); val &= ~CTRL_DDC_FIFO_DIR; val |= CTRL_DDC_FIFO_DIR_READ; HDMI_WRITE(sc, DDC_CTRL, val); /* Setup DDC slave address */ val = (addr << SLAVE_ADDR_SHIFT) | (seg << SLAVE_ADDR_SEG_SHIFT) | (EDDC_ADDR << SLAVE_ADDR_EDDC_SHIFT) | (off << SLAVE_ADDR_OFFSET_SHIFT); HDMI_WRITE(sc, DDC_SLAVE_ADDR, val); /* Clear FIFO */ val = HDMI_READ(sc, DDC_FIFO_CTRL); val |= FIFO_CTRL_CLEAR; HDMI_WRITE(sc, DDC_FIFO_CTRL, val); /* Set transfer length */ HDMI_WRITE(sc, DDC_BYTE_COUNTER, len); /* Set command to "Explicit Offset Address Read" */ HDMI_WRITE(sc, DDC_COMMAND, COMMAND_EOREAD); /* Start transfer */ val = HDMI_READ(sc, DDC_CTRL); val |= CTRL_DDC_ACMD_START; HDMI_WRITE(sc, DDC_CTRL, val); /* Wait for command to start */ retry = DDC_RETRY; while (--retry > 0) { val = HDMI_READ(sc, DDC_CTRL); if ((val & CTRL_DDC_ACMD_START) == 0) break; DELAY(DDC_DELAY); } if (retry == 0) return (ETIMEDOUT); /* Ensure that the transfer completed */ val = HDMI_READ(sc, DDC_INT_STATUS); if ((val & INT_STATUS_XFER_DONE) == 0) return (EIO); return (0); } static int a10hdmi_ddc_read(struct a10hdmi_softc *sc, int block, uint8_t *edid) { int resid, off, len, error; uint8_t *pbuf; pbuf = edid; resid = EDID_LENGTH; off = (block & 1) ? EDID_LENGTH : 0; while (resid > 0) { len = min(resid, DDC_BLKLEN); error = a10hdmi_ddc_xfer(sc, DDC_ADDR, block >> 1, off, len); if (error != 0) return (error); bus_read_multi_1(sc->res, DDC_FIFO, pbuf, len); pbuf += len; off += len; resid -= len; } return (0); } static int a10hdmi_detect_hdmi_vsdb(uint8_t *edid) { int off, p, btag, blen; if (edid[EXT_TAG] != CEA_TAG_ID) return (0); off = edid[CEA_DATA_OFF]; /* CEA data block collection starts at byte 4 */ if (off <= CEA_DATA_START) return (0); /* Parse the CEA data blocks */ for (p = CEA_DATA_START; p < off;) { btag = BLOCK_TAG(edid[p]); blen = BLOCK_LEN(edid[p]); /* Make sure the length is sane */ if (p + blen + 1 > off) break; /* Look for a VSDB with the HDMI 24-bit IEEE registration ID */ if (btag == BLOCK_TAG_VSDB && blen >= HDMI_VSDB_MINLEN && memcmp(&edid[p + 1], HDMI_OUI, HDMI_OUI_LEN) == 0) return (1); /* Next data block */ p += (1 + blen); } return (0); } static void a10hdmi_detect_hdmi(struct a10hdmi_softc *sc, int *phdmi, int *paudio) { struct edid_info ei; uint8_t edid[EDID_LENGTH]; int block; *phdmi = *paudio = 0; if (edid_parse(sc->edid, &ei) != 0) return; /* Scan through extension blocks, looking for a CEA-861 block. */ for (block = 1; block <= ei.edid_ext_block_count; block++) { if (a10hdmi_ddc_read(sc, block, edid) != 0) return; if (a10hdmi_detect_hdmi_vsdb(edid) != 0) { *phdmi = 1; *paudio = ((edid[CEA_DTD] & DTD_BASIC_AUDIO) != 0); return; } } } static int a10hdmi_get_edid(device_t dev, uint8_t **edid, uint32_t *edid_len) { struct a10hdmi_softc *sc; int error, retry; sc = device_get_softc(dev); retry = DDC_READ_RETRY; while (--retry > 0) { /* I2C software reset */ HDMI_WRITE(sc, DDC_FIFO_CTRL, 0); HDMI_WRITE(sc, DDC_CTRL, CTRL_DDC_EN | CTRL_DDC_SWRST); DELAY(SWRST_DELAY); if (HDMI_READ(sc, DDC_CTRL) & CTRL_DDC_SWRST) { device_printf(dev, "DDC software reset failed\n"); return (ENXIO); } /* Configure DDC clock */ HDMI_WRITE(sc, DDC_CLOCK, DDC_CLOCK_M | DDC_CLOCK_N); /* Read EDID block */ error = a10hdmi_ddc_read(sc, 0, sc->edid); if (error == 0) { *edid = sc->edid; *edid_len = sizeof(sc->edid); break; } } if (error == 0) a10hdmi_detect_hdmi(sc, &sc->has_hdmi, &sc->has_audio); else sc->has_hdmi = sc->has_audio = 0; return (error); } static void a10hdmi_set_audiomode(device_t dev, const struct videomode *mode) { struct a10hdmi_softc *sc; uint32_t val; int retry; sc = device_get_softc(dev); /* Disable and reset audio module and wait for reset bit to clear */ HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_RST); for (retry = HDMI_AUDIO_RESET_RETRY; retry > 0; retry--) { val = HDMI_READ(sc, HDMI_AUD_CTRL); if ((val & AUD_CTRL_RST) == 0) break; } if (retry == 0) { device_printf(dev, "timeout waiting for audio module\n"); return; } if (!sc->has_audio) return; /* DMA and FIFO control */ HDMI_WRITE(sc, HDMI_ADMA_CTRL, HDMI_ADMA_MODE_DDMA); /* Audio format control (LPCM, S16LE, stereo) */ HDMI_WRITE(sc, HDMI_AUD_FMT, AUD_FMT_CH(HDMI_AUDIO_CHANNELS)); /* Channel mappings */ HDMI_WRITE(sc, HDMI_PCM_CTRL, HDMI_AUDIO_CHANNELMAP); /* Clocks */ HDMI_WRITE(sc, HDMI_AUD_CTS, HDMI_AUDIO_CTS(mode->dot_clock, HDMI_AUDIO_N)); HDMI_WRITE(sc, HDMI_AUD_N, HDMI_AUDIO_N); /* Set sampling frequency to 48 kHz, word length to 16-bit */ HDMI_WRITE(sc, HDMI_AUD_CH_STATUS0, CH_STATUS0_FS_FREQ_48); HDMI_WRITE(sc, HDMI_AUD_CH_STATUS1, CH_STATUS1_WORD_LEN_16); /* Enable */ HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_EN); } static int a10hdmi_get_tcon_config(struct a10hdmi_softc *sc, int *div, int *dbl) { uint64_t lcd_fin, lcd_fout; clk_t clk_lcd_parent; const char *pname; int error; error = clk_get_parent(sc->clk_lcd, &clk_lcd_parent); if (error != 0) return (error); /* Get the LCD CH1 special clock 2 divider */ error = clk_get_freq(sc->clk_lcd, &lcd_fout); if (error != 0) return (error); error = clk_get_freq(clk_lcd_parent, &lcd_fin); if (error != 0) return (error); *div = lcd_fin / lcd_fout; /* Detect LCD CH1 special clock using a 1X or 2X source */ /* XXX */ pname = clk_get_name(clk_lcd_parent); - if (strcmp(pname, "pll3-1x") == 0 || strcmp(pname, "pll7-1x") == 0) + if (strcmp(pname, "pll3") == 0 || strcmp(pname, "pll7") == 0) *dbl = 0; else *dbl = 1; return (0); } static int a10hdmi_set_videomode(device_t dev, const struct videomode *mode) { struct a10hdmi_softc *sc; int error, clk_div, clk_dbl; int dblscan, hfp, hspw, hbp, vfp, vspw, vbp; uint32_t val; sc = device_get_softc(dev); dblscan = !!(mode->flags & VID_DBLSCAN); hfp = mode->hsync_start - mode->hdisplay; hspw = mode->hsync_end - mode->hsync_start; hbp = mode->htotal - mode->hsync_start; vfp = mode->vsync_start - mode->vdisplay; vspw = mode->vsync_end - mode->vsync_start; vbp = mode->vtotal - mode->vsync_start; error = a10hdmi_get_tcon_config(sc, &clk_div, &clk_dbl); if (error != 0) { device_printf(dev, "couldn't get tcon config: %d\n", error); return (error); } /* Clear interrupt status */ HDMI_WRITE(sc, HDMI_INT_STATUS, HDMI_READ(sc, HDMI_INT_STATUS)); /* Clock setup */ val = HDMI_READ(sc, HDMI_PADCTRL1); val &= ~PADCTRL1_REG_CKSS; val |= (clk_dbl ? PADCTRL1_REG_CKSS_2X : PADCTRL1_REG_CKSS_1X); HDMI_WRITE(sc, HDMI_PADCTRL1, val); HDMI_WRITE(sc, HDMI_PLLCTRL0, PLLCTRL0_PLL_EN | PLLCTRL0_BWS | PLLCTRL0_HV_IS_33 | PLLCTRL0_LDO1_EN | PLLCTRL0_LDO2_EN | PLLCTRL0_SDIV2 | PLLCTRL0_VCO_GAIN | PLLCTRL0_S | PLLCTRL0_CP_S | PLLCTRL0_CS | PLLCTRL0_PREDIV(clk_div) | PLLCTRL0_VCO_S); /* Setup display settings */ if (bootverbose) device_printf(dev, "HDMI: %s, Audio: %s\n", sc->has_hdmi ? "yes" : "no", sc->has_audio ? "yes" : "no"); val = 0; if (sc->has_hdmi) val |= VID_CTRL_HDMI_MODE; if (mode->flags & VID_INTERLACE) val |= VID_CTRL_INTERLACE; if (mode->flags & VID_DBLSCAN) val |= VID_CTRL_REPEATER_2X; HDMI_WRITE(sc, HDMI_VID_CTRL, val); /* Setup display timings */ HDMI_WRITE(sc, HDMI_VID_TIMING0, VID_ACT_V(mode->vdisplay) | VID_ACT_H(mode->hdisplay << dblscan)); HDMI_WRITE(sc, HDMI_VID_TIMING1, VID_VBP(vbp) | VID_HBP(hbp << dblscan)); HDMI_WRITE(sc, HDMI_VID_TIMING2, VID_VFP(vfp) | VID_HFP(hfp << dblscan)); HDMI_WRITE(sc, HDMI_VID_TIMING3, VID_VSPW(vspw) | VID_HSPW(hspw << dblscan)); val = TX_CLOCK_NORMAL; if (mode->flags & VID_PVSYNC) val |= VID_VSYNC_ACTSEL; if (mode->flags & VID_PHSYNC) val |= VID_HSYNC_ACTSEL; HDMI_WRITE(sc, HDMI_VID_TIMING4, val); /* This is an ordered list of infoframe packets that the HDMI * transmitter will send. Transmit packets in the following order: * 1. General control packet * 2. AVI infoframe * 3. Audio infoframe * There are 2 registers with 4 slots each. The list is terminated * with the special PKT_END marker. */ HDMI_WRITE(sc, HDMI_PKTCTRL0, PKTCTRL_PACKET(0, PKT_GC) | PKTCTRL_PACKET(1, PKT_AVI) | PKTCTRL_PACKET(2, PKT_AI) | PKTCTRL_PACKET(3, PKT_END)); HDMI_WRITE(sc, HDMI_PKTCTRL1, 0); /* Setup audio */ a10hdmi_set_audiomode(dev, mode); return (0); } static int a10hdmi_enable(device_t dev, int onoff) { struct a10hdmi_softc *sc; uint32_t val; sc = device_get_softc(dev); /* Enable or disable video output */ val = HDMI_READ(sc, HDMI_VID_CTRL); if (onoff) val |= VID_CTRL_VIDEO_EN; else val &= ~VID_CTRL_VIDEO_EN; HDMI_WRITE(sc, HDMI_VID_CTRL, val); return (0); } static device_method_t a10hdmi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, a10hdmi_probe), DEVMETHOD(device_attach, a10hdmi_attach), /* HDMI interface */ DEVMETHOD(hdmi_get_edid, a10hdmi_get_edid), DEVMETHOD(hdmi_set_videomode, a10hdmi_set_videomode), DEVMETHOD(hdmi_enable, a10hdmi_enable), DEVMETHOD_END }; static driver_t a10hdmi_driver = { "a10hdmi", a10hdmi_methods, sizeof(struct a10hdmi_softc), }; static devclass_t a10hdmi_devclass; DRIVER_MODULE(a10hdmi, simplebus, a10hdmi_driver, a10hdmi_devclass, 0, 0); MODULE_VERSION(a10hdmi, 1); Index: head/sys/arm/allwinner/clk/aw_pll.c =================================================================== --- head/sys/arm/allwinner/clk/aw_pll.c (revision 308671) +++ head/sys/arm/allwinner/clk/aw_pll.c (revision 308672) @@ -1,1326 +1,1316 @@ /*- * Copyright (c) 2016 Jared McNeill * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Allwinner PLL clock */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "clkdev_if.h" #define SUN4I_A10_PLL2_1X 0 #define SUN4I_A10_PLL2_2X 1 #define SUN4I_A10_PLL2_4X 2 #define SUN4I_A10_PLL2_8X 3 #define AW_PLL_ENABLE (1 << 31) #define A10_PLL1_OUT_EXT_DIVP (0x3 << 16) #define A10_PLL1_OUT_EXT_DIVP_SHIFT 16 #define A10_PLL1_FACTOR_N (0x1f << 8) #define A10_PLL1_FACTOR_N_SHIFT 8 #define A10_PLL1_FACTOR_K (0x3 << 4) #define A10_PLL1_FACTOR_K_SHIFT 4 #define A10_PLL1_FACTOR_M (0x3 << 0) #define A10_PLL1_FACTOR_M_SHIFT 0 #define A10_PLL2_POST_DIV (0xf << 26) #define A10_PLL2_POST_DIV_SHIFT 26 #define A10_PLL2_FACTOR_N (0x7f << 8) #define A10_PLL2_FACTOR_N_SHIFT 8 #define A10_PLL2_PRE_DIV (0x1f << 0) #define A10_PLL2_PRE_DIV_SHIFT 0 #define A10_PLL3_MODE_SEL (0x1 << 15) #define A10_PLL3_MODE_SEL_FRACT (0 << 15) #define A10_PLL3_MODE_SEL_INT (1 << 15) #define A10_PLL3_FUNC_SET (0x1 << 14) #define A10_PLL3_FUNC_SET_270MHZ (0 << 14) #define A10_PLL3_FUNC_SET_297MHZ (1 << 14) #define A10_PLL3_FACTOR_M (0x7f << 0) #define A10_PLL3_FACTOR_M_SHIFT 0 #define A10_PLL3_REF_FREQ 3000000 #define A10_PLL5_OUT_EXT_DIVP (0x3 << 16) #define A10_PLL5_OUT_EXT_DIVP_SHIFT 16 #define A10_PLL5_FACTOR_N (0x1f << 8) #define A10_PLL5_FACTOR_N_SHIFT 8 #define A10_PLL5_FACTOR_K (0x3 << 4) #define A10_PLL5_FACTOR_K_SHIFT 4 #define A10_PLL5_FACTOR_M1 (0x3 << 2) #define A10_PLL5_FACTOR_M1_SHIFT 2 #define A10_PLL5_FACTOR_M (0x3 << 0) #define A10_PLL5_FACTOR_M_SHIFT 0 #define A10_PLL6_BYPASS_EN (1 << 30) #define A10_PLL6_SATA_CLK_EN (1 << 14) #define A10_PLL6_FACTOR_N (0x1f << 8) #define A10_PLL6_FACTOR_N_SHIFT 8 #define A10_PLL6_FACTOR_K (0x3 << 4) #define A10_PLL6_FACTOR_K_SHIFT 4 #define A10_PLL6_FACTOR_M (0x3 << 0) #define A10_PLL6_FACTOR_M_SHIFT 0 #define A10_PLL2_POST_DIV (0xf << 26) #define A13_PLL2_POST_DIV (0xf << 26) #define A13_PLL2_POST_DIV_SHIFT 26 #define A13_PLL2_FACTOR_N (0x7f << 8) #define A13_PLL2_FACTOR_N_SHIFT 8 #define A13_PLL2_PRE_DIV (0x1f << 0) #define A13_PLL2_PRE_DIV_SHIFT 0 #define A23_PLL1_FACTOR_P (0x3 << 16) #define A23_PLL1_FACTOR_P_SHIFT 16 #define A23_PLL1_FACTOR_N (0x1f << 8) #define A23_PLL1_FACTOR_N_SHIFT 8 #define A23_PLL1_FACTOR_K (0x3 << 4) #define A23_PLL1_FACTOR_K_SHIFT 4 #define A23_PLL1_FACTOR_M (0x3 << 0) #define A23_PLL1_FACTOR_M_SHIFT 0 #define A31_PLL1_LOCK (1 << 28) #define A31_PLL1_CPU_SIGMA_DELTA_EN (1 << 24) #define A31_PLL1_FACTOR_N (0x1f << 8) #define A31_PLL1_FACTOR_N_SHIFT 8 #define A31_PLL1_FACTOR_K (0x3 << 4) #define A31_PLL1_FACTOR_K_SHIFT 4 #define A31_PLL1_FACTOR_M (0x3 << 0) #define A31_PLL1_FACTOR_M_SHIFT 0 #define A31_PLL6_LOCK (1 << 28) #define A31_PLL6_BYPASS_EN (1 << 25) #define A31_PLL6_CLK_OUT_EN (1 << 24) #define A31_PLL6_24M_OUT_EN (1 << 18) #define A31_PLL6_24M_POST_DIV (0x3 << 16) #define A31_PLL6_24M_POST_DIV_SHIFT 16 #define A31_PLL6_FACTOR_N (0x1f << 8) #define A31_PLL6_FACTOR_N_SHIFT 8 #define A31_PLL6_FACTOR_K (0x3 << 4) #define A31_PLL6_FACTOR_K_SHIFT 4 #define A31_PLL6_DEFAULT_N 0x18 #define A31_PLL6_DEFAULT_K 0x1 #define A31_PLL6_TIMEOUT 10 #define A64_PLLHSIC_LOCK (1 << 28) #define A64_PLLHSIC_FRAC_CLK_OUT (1 << 25) #define A64_PLLHSIC_PLL_MODE_SEL (1 << 24) #define A64_PLLHSIC_PLL_SDM_EN (1 << 20) #define A64_PLLHSIC_FACTOR_N (0x7f << 8) #define A64_PLLHSIC_FACTOR_N_SHIFT 8 #define A64_PLLHSIC_PRE_DIV_M (0xf << 0) #define A64_PLLHSIC_PRE_DIV_M_SHIFT 0 #define A80_PLL4_CLK_OUT_EN (1 << 20) #define A80_PLL4_PLL_DIV2 (1 << 18) #define A80_PLL4_PLL_DIV1 (1 << 16) #define A80_PLL4_FACTOR_N (0xff << 8) #define A80_PLL4_FACTOR_N_SHIFT 8 #define A83T_PLLCPUX_LOCK_TIME (0x7 << 24) #define A83T_PLLCPUX_LOCK_TIME_SHIFT 24 #define A83T_PLLCPUX_CLOCK_OUTPUT_DIS (1 << 20) #define A83T_PLLCPUX_OUT_EXT_DIVP (1 << 16) #define A83T_PLLCPUX_FACTOR_N (0xff << 8) #define A83T_PLLCPUX_FACTOR_N_SHIFT 8 #define A83T_PLLCPUX_FACTOR_N_MIN 12 #define A83T_PLLCPUX_FACTOR_N_MAX 125 #define A83T_PLLCPUX_POSTDIV_M (0x3 << 0) #define A83T_PLLCPUX_POSTDIV_M_SHIFT 0 #define H3_PLL2_LOCK (1 << 28) #define H3_PLL2_SDM_EN (1 << 24) #define H3_PLL2_POST_DIV (0xf << 16) #define H3_PLL2_POST_DIV_SHIFT 16 #define H3_PLL2_FACTOR_N (0x7f << 8) #define H3_PLL2_FACTOR_N_SHIFT 8 #define H3_PLL2_PRE_DIV (0x1f << 0) #define H3_PLL2_PRE_DIV_SHIFT 0 -#define CLKID_A10_PLL3_1X 0 -#define CLKID_A10_PLL3_2X 1 - #define CLKID_A10_PLL5_DDR 0 #define CLKID_A10_PLL5_OTHER 1 #define CLKID_A10_PLL6_SATA 0 #define CLKID_A10_PLL6_OTHER 1 #define CLKID_A10_PLL6 2 #define CLKID_A10_PLL6_DIV_4 3 #define CLKID_A31_PLL6 0 #define CLKID_A31_PLL6_X2 1 struct aw_pll_factor { unsigned int n; unsigned int k; unsigned int m; unsigned int p; uint64_t freq; }; #define PLLFACTOR(_n, _k, _m, _p, _freq) \ { .n = (_n), .k = (_k), .m = (_m), .p = (_p), .freq = (_freq) } static struct aw_pll_factor aw_a10_pll1_factors[] = { PLLFACTOR(6, 0, 0, 0, 144000000), PLLFACTOR(12, 0, 0, 0, 312000000), PLLFACTOR(21, 0, 0, 0, 528000000), PLLFACTOR(29, 0, 0, 0, 720000000), PLLFACTOR(18, 1, 0, 0, 864000000), PLLFACTOR(19, 1, 0, 0, 912000000), PLLFACTOR(20, 1, 0, 0, 960000000), }; static struct aw_pll_factor aw_a23_pll1_factors[] = { PLLFACTOR(9, 0, 0, 2, 60000000), PLLFACTOR(10, 0, 0, 2, 66000000), PLLFACTOR(11, 0, 0, 2, 72000000), PLLFACTOR(12, 0, 0, 2, 78000000), PLLFACTOR(13, 0, 0, 2, 84000000), PLLFACTOR(14, 0, 0, 2, 90000000), PLLFACTOR(15, 0, 0, 2, 96000000), PLLFACTOR(16, 0, 0, 2, 102000000), PLLFACTOR(17, 0, 0, 2, 108000000), PLLFACTOR(18, 0, 0, 2, 114000000), PLLFACTOR(9, 0, 0, 1, 120000000), PLLFACTOR(10, 0, 0, 1, 132000000), PLLFACTOR(11, 0, 0, 1, 144000000), PLLFACTOR(12, 0, 0, 1, 156000000), PLLFACTOR(13, 0, 0, 1, 168000000), PLLFACTOR(14, 0, 0, 1, 180000000), PLLFACTOR(15, 0, 0, 1, 192000000), PLLFACTOR(16, 0, 0, 1, 204000000), PLLFACTOR(17, 0, 0, 1, 216000000), PLLFACTOR(18, 0, 0, 1, 228000000), PLLFACTOR(9, 0, 0, 0, 240000000), PLLFACTOR(10, 0, 0, 0, 264000000), PLLFACTOR(11, 0, 0, 0, 288000000), PLLFACTOR(12, 0, 0, 0, 312000000), PLLFACTOR(13, 0, 0, 0, 336000000), PLLFACTOR(14, 0, 0, 0, 360000000), PLLFACTOR(15, 0, 0, 0, 384000000), PLLFACTOR(16, 0, 0, 0, 408000000), PLLFACTOR(17, 0, 0, 0, 432000000), PLLFACTOR(18, 0, 0, 0, 456000000), PLLFACTOR(19, 0, 0, 0, 480000000), PLLFACTOR(20, 0, 0, 0, 504000000), PLLFACTOR(21, 0, 0, 0, 528000000), PLLFACTOR(22, 0, 0, 0, 552000000), PLLFACTOR(23, 0, 0, 0, 576000000), PLLFACTOR(24, 0, 0, 0, 600000000), PLLFACTOR(25, 0, 0, 0, 624000000), PLLFACTOR(26, 0, 0, 0, 648000000), PLLFACTOR(27, 0, 0, 0, 672000000), PLLFACTOR(28, 0, 0, 0, 696000000), PLLFACTOR(29, 0, 0, 0, 720000000), PLLFACTOR(15, 1, 0, 0, 768000000), PLLFACTOR(10, 2, 0, 0, 792000000), PLLFACTOR(16, 1, 0, 0, 816000000), PLLFACTOR(17, 1, 0, 0, 864000000), PLLFACTOR(18, 1, 0, 0, 912000000), PLLFACTOR(12, 2, 0, 0, 936000000), PLLFACTOR(19, 1, 0, 0, 960000000), PLLFACTOR(20, 1, 0, 0, 1008000000), PLLFACTOR(21, 1, 0, 0, 1056000000), PLLFACTOR(14, 2, 0, 0, 1080000000), PLLFACTOR(22, 1, 0, 0, 1104000000), PLLFACTOR(23, 1, 0, 0, 1152000000), PLLFACTOR(24, 1, 0, 0, 1200000000), PLLFACTOR(16, 2, 0, 0, 1224000000), PLLFACTOR(25, 1, 0, 0, 1248000000), PLLFACTOR(26, 1, 0, 0, 1296000000), PLLFACTOR(27, 1, 0, 0, 1344000000), PLLFACTOR(18, 2, 0, 0, 1368000000), PLLFACTOR(28, 1, 0, 0, 1392000000), PLLFACTOR(29, 1, 0, 0, 1440000000), PLLFACTOR(20, 2, 0, 0, 1512000000), PLLFACTOR(15, 3, 0, 0, 1536000000), PLLFACTOR(21, 2, 0, 0, 1584000000), PLLFACTOR(16, 3, 0, 0, 1632000000), PLLFACTOR(22, 2, 0, 0, 1656000000), PLLFACTOR(23, 2, 0, 0, 1728000000), PLLFACTOR(24, 2, 0, 0, 1800000000), PLLFACTOR(18, 3, 0, 0, 1824000000), PLLFACTOR(25, 2, 0, 0, 1872000000), }; static struct aw_pll_factor aw_h3_pll2_factors[] = { PLLFACTOR(13, 0, 0, 13, 24576000), PLLFACTOR(6, 0, 0, 7, 22579200), }; enum aw_pll_type { AWPLL_A10_PLL1 = 1, AWPLL_A10_PLL2, AWPLL_A10_PLL3, AWPLL_A10_PLL5, AWPLL_A10_PLL6, AWPLL_A13_PLL2, AWPLL_A23_PLL1, AWPLL_A31_PLL1, AWPLL_A31_PLL6, AWPLL_A64_PLLHSIC, AWPLL_A80_PLL4, AWPLL_A83T_PLLCPUX, AWPLL_H3_PLL1, AWPLL_H3_PLL2, }; struct aw_pll_sc { enum aw_pll_type type; device_t clkdev; bus_addr_t reg; int id; }; struct aw_pll_funcs { int (*recalc)(struct aw_pll_sc *, uint64_t *); int (*set_freq)(struct aw_pll_sc *, uint64_t, uint64_t *, int); int (*init)(device_t, bus_addr_t, struct clknode_init_def *); }; #define PLL_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) #define PLL_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) #define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) #define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) static int a10_pll1_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { struct aw_pll_factor *f; uint32_t val; int n; f = NULL; for (n = 0; n < nitems(aw_a10_pll1_factors); n++) { if (aw_a10_pll1_factors[n].freq == *fout) { f = &aw_a10_pll1_factors[n]; break; } } if (f == NULL) return (EINVAL); DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~(A10_PLL1_FACTOR_N|A10_PLL1_FACTOR_K|A10_PLL1_FACTOR_M| A10_PLL1_OUT_EXT_DIVP); val |= (f->p << A10_PLL1_OUT_EXT_DIVP_SHIFT); val |= (f->n << A10_PLL1_FACTOR_N_SHIFT); val |= (f->k << A10_PLL1_FACTOR_K_SHIFT); val |= (f->m << A10_PLL1_FACTOR_M_SHIFT); PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int a10_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, n, k, p; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); p = 1 << ((val & A10_PLL1_OUT_EXT_DIVP) >> A10_PLL1_OUT_EXT_DIVP_SHIFT); m = ((val & A10_PLL1_FACTOR_M) >> A10_PLL1_FACTOR_M_SHIFT) + 1; k = ((val & A10_PLL1_FACTOR_K) >> A10_PLL1_FACTOR_K_SHIFT) + 1; n = (val & A10_PLL1_FACTOR_N) >> A10_PLL1_FACTOR_N_SHIFT; if (n == 0) n = 1; *freq = (*freq * n * k) / (m * p); return (0); } static int a10_pll2_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, post_div, n, pre_div; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); post_div = (val & A10_PLL2_POST_DIV) >> A10_PLL2_POST_DIV_SHIFT; if (post_div == 0) post_div = 1; n = (val & A10_PLL2_FACTOR_N) >> A10_PLL2_FACTOR_N_SHIFT; if (n == 0) n = 1; pre_div = (val & A10_PLL2_PRE_DIV) >> A10_PLL2_PRE_DIV_SHIFT; if (pre_div == 0) pre_div = 1; switch (sc->id) { case SUN4I_A10_PLL2_1X: *freq = (*freq * 2 * n) / pre_div / post_div / 2; break; case SUN4I_A10_PLL2_2X: *freq = (*freq * 2 * n) / pre_div / 4; break; case SUN4I_A10_PLL2_4X: *freq = (*freq * 2 * n) / pre_div / 2; break; case SUN4I_A10_PLL2_8X: *freq = (*freq * 2 * n) / pre_div; break; default: return (EINVAL); } return (0); } static int a10_pll2_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t val, post_div, n, pre_div; if (sc->id != SUN4I_A10_PLL2_1X) return (ENXIO); /* * Audio Codec needs PLL2-1X to be either 24576000 or 22579200. * * PLL2-1X output frequency is (48MHz * n) / pre_div / post_div / 2. * To get as close as possible to the desired rate, we use a * pre-divider of 21 and a post-divider of 4. With these values, * a multiplier of 86 or 79 gets us close to the target rates. */ if (*fout != 24576000 && *fout != 22579200) return (EINVAL); pre_div = 21; post_div = 4; n = (*fout * pre_div * post_div * 2) / (2 * fin); DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~(A10_PLL2_POST_DIV | A10_PLL2_FACTOR_N | A10_PLL2_PRE_DIV); val |= (post_div << A10_PLL2_POST_DIV_SHIFT); val |= (n << A10_PLL2_FACTOR_N_SHIFT); val |= (pre_div << A10_PLL2_PRE_DIV_SHIFT); PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int a10_pll3_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); if ((val & A10_PLL3_MODE_SEL) == A10_PLL3_MODE_SEL_INT) { /* In integer mode, output is 3MHz * m */ m = (val & A10_PLL3_FACTOR_M) >> A10_PLL3_FACTOR_M_SHIFT; *freq = A10_PLL3_REF_FREQ * m; } else { /* In fractional mode, output is either 270MHz or 297MHz */ if ((val & A10_PLL3_FUNC_SET) == A10_PLL3_FUNC_SET_270MHZ) *freq = 270000000; else *freq = 297000000; } - if (sc->id == CLKID_A10_PLL3_2X) - *freq *= 2; - return (0); } static int a10_pll3_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t val, m, mode, func; m = *fout / A10_PLL3_REF_FREQ; - if (sc->id == CLKID_A10_PLL3_2X) - m /= 2; mode = A10_PLL3_MODE_SEL_INT; func = 0; *fout = m * A10_PLL3_REF_FREQ; - if (sc->id == CLKID_A10_PLL3_2X) - *fout *= 2; DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~(A10_PLL3_MODE_SEL | A10_PLL3_FUNC_SET | A10_PLL3_FACTOR_M); val |= mode; val |= func; val |= (m << A10_PLL3_FACTOR_M_SHIFT); PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int a10_pll3_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) { uint32_t val; /* Allow changing PLL frequency while enabled */ def->flags = CLK_NODE_GLITCH_FREE; /* Set PLL to 297MHz */ CLKDEV_DEVICE_LOCK(dev); CLKDEV_READ_4(dev, reg, &val); val &= ~(A10_PLL3_MODE_SEL | A10_PLL3_FUNC_SET | A10_PLL3_FACTOR_M); val |= A10_PLL3_MODE_SEL_FRACT; val |= A10_PLL3_FUNC_SET_297MHZ; CLKDEV_WRITE_4(dev, reg, val); CLKDEV_DEVICE_UNLOCK(dev); return (0); } static int a10_pll5_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, n, k, p; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); p = 1 << ((val & A10_PLL5_OUT_EXT_DIVP) >> A10_PLL5_OUT_EXT_DIVP_SHIFT); m = ((val & A10_PLL5_FACTOR_M) >> A10_PLL5_FACTOR_M_SHIFT) + 1; k = ((val & A10_PLL5_FACTOR_K) >> A10_PLL5_FACTOR_K_SHIFT) + 1; n = (val & A10_PLL5_FACTOR_N) >> A10_PLL5_FACTOR_N_SHIFT; if (n == 0) return (ENXIO); switch (sc->id) { case CLKID_A10_PLL5_DDR: *freq = (*freq * n * k) / m; break; case CLKID_A10_PLL5_OTHER: *freq = (*freq * n * k) / p; break; default: return (ENXIO); } return (0); } static int a10_pll6_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) { uint32_t val, m, n, k; /* * SATA needs PLL6 to be a 100MHz clock. * * The SATA output frequency is (24MHz * n * k) / m / 6. * To get to 100MHz, k & m must be equal and n must be 25. */ m = k = 0; n = 25; CLKDEV_DEVICE_LOCK(dev); CLKDEV_READ_4(dev, reg, &val); val &= ~(A10_PLL6_FACTOR_N | A10_PLL6_FACTOR_K | A10_PLL6_FACTOR_M); val &= ~A10_PLL6_BYPASS_EN; val |= A10_PLL6_SATA_CLK_EN; val |= (n << A10_PLL6_FACTOR_N_SHIFT); val |= (k << A10_PLL6_FACTOR_K_SHIFT); val |= (m << A10_PLL6_FACTOR_M_SHIFT); CLKDEV_WRITE_4(dev, reg, val); CLKDEV_DEVICE_UNLOCK(dev); return (0); } static int a10_pll6_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, k, n; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); m = ((val & A10_PLL6_FACTOR_M) >> A10_PLL6_FACTOR_M_SHIFT) + 1; k = ((val & A10_PLL6_FACTOR_K) >> A10_PLL6_FACTOR_K_SHIFT) + 1; n = (val & A10_PLL6_FACTOR_N) >> A10_PLL6_FACTOR_N_SHIFT; if (n == 0) return (ENXIO); switch (sc->id) { case CLKID_A10_PLL6_SATA: *freq = (*freq * n * k) / m / 6; break; case CLKID_A10_PLL6_OTHER: *freq = (*freq * n * k) / 2; break; case CLKID_A10_PLL6: *freq = (*freq * n * k); break; case CLKID_A10_PLL6_DIV_4: *freq = (*freq * n * k) / 4; break; default: return (ENXIO); } return (0); } static int a10_pll6_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { if (sc->id != CLKID_A10_PLL6_SATA) return (ENXIO); /* PLL6 SATA output has been set to 100MHz in a10_pll6_init */ if (*fout != 100000000) return (ERANGE); return (0); } static int a13_pll2_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, post_div, n, pre_div; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); post_div = ((val & A13_PLL2_POST_DIV) >> A13_PLL2_POST_DIV_SHIFT) + 1; if (post_div == 0) post_div = 1; n = (val & A13_PLL2_FACTOR_N) >> A13_PLL2_FACTOR_N_SHIFT; if (n == 0) n = 1; pre_div = ((val & A13_PLL2_PRE_DIV) >> A13_PLL2_PRE_DIV_SHIFT) + 1; if (pre_div == 0) pre_div = 1; switch (sc->id) { case SUN4I_A10_PLL2_1X: *freq = (*freq * 2 * n) / pre_div / post_div / 2; break; case SUN4I_A10_PLL2_2X: *freq = (*freq * 2 * n) / pre_div / 4; break; case SUN4I_A10_PLL2_4X: *freq = (*freq * 2 * n) / pre_div / 2; break; case SUN4I_A10_PLL2_8X: *freq = (*freq * 2 * n) / pre_div; break; default: return (EINVAL); } return (0); } static int a13_pll2_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t val, post_div, n, pre_div; if (sc->id != SUN4I_A10_PLL2_1X) return (ENXIO); /* * Audio Codec needs PLL2-1X to be either 24576000 or 22579200. * * PLL2-1X output frequency is (48MHz * n) / pre_div / post_div / 2. * To get as close as possible to the desired rate, we use a * pre-divider of 21 and a post-divider of 4. With these values, * a multiplier of 86 or 79 gets us close to the target rates. */ if (*fout != 24576000 && *fout != 22579200) return (EINVAL); pre_div = 21; post_div = 4; n = (*fout * pre_div * post_div * 2) / (2 * fin); DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~(A13_PLL2_POST_DIV | A13_PLL2_FACTOR_N | A13_PLL2_PRE_DIV); val |= ((post_div - 1) << A13_PLL2_POST_DIV_SHIFT); val |= (n << A13_PLL2_FACTOR_N_SHIFT); val |= ((pre_div - 1) << A13_PLL2_PRE_DIV_SHIFT); PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int h3_pll2_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, p, n, m; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); p = ((val & H3_PLL2_POST_DIV) >> H3_PLL2_POST_DIV_SHIFT) + 1; n = ((val & H3_PLL2_FACTOR_N) >> H3_PLL2_FACTOR_N_SHIFT) + 1; m = ((val & H3_PLL2_PRE_DIV) >> H3_PLL2_PRE_DIV_SHIFT) + 1; switch (sc->id) { case SUN4I_A10_PLL2_1X: *freq = (*freq * n) / (m * p); break; case SUN4I_A10_PLL2_2X: *freq = (*freq * 2 * n) / m / 4; break; case SUN4I_A10_PLL2_4X: *freq = (*freq * 2 * n) / m / 2; break; case SUN4I_A10_PLL2_8X: *freq = (*freq * 2 * n) / m; break; default: return (EINVAL); } return (0); } static int h3_pll2_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { struct aw_pll_factor *f; uint32_t val; int n, error, retry; if (sc->id != SUN4I_A10_PLL2_1X) return (ENXIO); f = NULL; for (n = 0; n < nitems(aw_h3_pll2_factors); n++) { if (aw_h3_pll2_factors[n].freq == *fout) { f = &aw_h3_pll2_factors[n]; break; } } if (f == NULL) return (EINVAL); DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~(H3_PLL2_POST_DIV|H3_PLL2_FACTOR_N|H3_PLL2_PRE_DIV); val |= (f->p << H3_PLL2_POST_DIV_SHIFT); val |= (f->n << H3_PLL2_FACTOR_N_SHIFT); val |= (f->m << H3_PLL2_PRE_DIV_SHIFT); val |= AW_PLL_ENABLE; PLL_WRITE(sc, val); /* Wait for lock */ error = 0; for (retry = 0; retry < 1000; retry++) { PLL_READ(sc, &val); if ((val & H3_PLL2_LOCK) != 0) break; DELAY(100); } if (retry == 0) error = ETIMEDOUT; DEVICE_UNLOCK(sc); return (error); } static int a23_pll1_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { struct aw_pll_factor *f; uint32_t val; int n; f = NULL; for (n = 0; n < nitems(aw_a23_pll1_factors); n++) { if (aw_a23_pll1_factors[n].freq == *fout) { f = &aw_a23_pll1_factors[n]; break; } } if (f == NULL) return (EINVAL); DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~(A23_PLL1_FACTOR_N|A23_PLL1_FACTOR_K|A23_PLL1_FACTOR_M| A23_PLL1_FACTOR_P); val |= (f->n << A23_PLL1_FACTOR_N_SHIFT); val |= (f->k << A23_PLL1_FACTOR_K_SHIFT); val |= (f->m << A23_PLL1_FACTOR_M_SHIFT); val |= (f->p << A23_PLL1_FACTOR_P_SHIFT); PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int a23_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, n, k, p; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); m = ((val & A23_PLL1_FACTOR_M) >> A23_PLL1_FACTOR_M_SHIFT) + 1; k = ((val & A23_PLL1_FACTOR_K) >> A23_PLL1_FACTOR_K_SHIFT) + 1; n = ((val & A23_PLL1_FACTOR_N) >> A23_PLL1_FACTOR_N_SHIFT) + 1; p = ((val & A23_PLL1_FACTOR_P) >> A23_PLL1_FACTOR_P_SHIFT) + 1; *freq = (*freq * n * k) / (m * p); return (0); } static int h3_pll1_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { struct aw_pll_factor *f; uint32_t val, n, k, m, p; int i; f = NULL; for (i = 0; i < nitems(aw_a23_pll1_factors); i++) { if (aw_a23_pll1_factors[i].freq == *fout) { f = &aw_a23_pll1_factors[i]; break; } } if (f == NULL) return (EINVAL); DEVICE_LOCK(sc); PLL_READ(sc, &val); n = (val & A23_PLL1_FACTOR_N) >> A23_PLL1_FACTOR_N_SHIFT; k = (val & A23_PLL1_FACTOR_K) >> A23_PLL1_FACTOR_K_SHIFT; m = (val & A23_PLL1_FACTOR_M) >> A23_PLL1_FACTOR_M_SHIFT; p = (val & A23_PLL1_FACTOR_P) >> A23_PLL1_FACTOR_P_SHIFT; if (p < f->p) { val &= ~A23_PLL1_FACTOR_P; val |= (f->p << A23_PLL1_FACTOR_P_SHIFT); PLL_WRITE(sc, val); DELAY(2000); } if (m < f->m) { val &= ~A23_PLL1_FACTOR_M; val |= (f->m << A23_PLL1_FACTOR_M_SHIFT); PLL_WRITE(sc, val); DELAY(2000); } val &= ~(A23_PLL1_FACTOR_N|A23_PLL1_FACTOR_K); val |= (f->n << A23_PLL1_FACTOR_N_SHIFT); val |= (f->k << A23_PLL1_FACTOR_K_SHIFT); PLL_WRITE(sc, val); DELAY(2000); if (m > f->m) { val &= ~A23_PLL1_FACTOR_M; val |= (f->m << A23_PLL1_FACTOR_M_SHIFT); PLL_WRITE(sc, val); DELAY(2000); } if (p > f->p) { val &= ~A23_PLL1_FACTOR_P; val |= (f->p << A23_PLL1_FACTOR_P_SHIFT); PLL_WRITE(sc, val); DELAY(2000); } DEVICE_UNLOCK(sc); return (0); } static int a31_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, n, k; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); m = ((val & A31_PLL1_FACTOR_M) >> A31_PLL1_FACTOR_M_SHIFT) + 1; k = ((val & A31_PLL1_FACTOR_K) >> A31_PLL1_FACTOR_K_SHIFT) + 1; n = ((val & A31_PLL1_FACTOR_N) >> A31_PLL1_FACTOR_N_SHIFT) + 1; *freq = (*freq * n * k) / m; return (0); } static int a31_pll6_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) { uint32_t val; int retry; if (def->id != CLKID_A31_PLL6) return (0); /* * The datasheet recommends that PLL6 output should be fixed to * 600MHz. */ CLKDEV_DEVICE_LOCK(dev); CLKDEV_READ_4(dev, reg, &val); val &= ~(A31_PLL6_FACTOR_N | A31_PLL6_FACTOR_K | A31_PLL6_BYPASS_EN); val |= (A31_PLL6_DEFAULT_N << A31_PLL6_FACTOR_N_SHIFT); val |= (A31_PLL6_DEFAULT_K << A31_PLL6_FACTOR_K_SHIFT); val |= AW_PLL_ENABLE; CLKDEV_WRITE_4(dev, reg, val); /* Wait for PLL to become stable */ for (retry = A31_PLL6_TIMEOUT; retry > 0; retry--) { CLKDEV_READ_4(dev, reg, &val); if ((val & A31_PLL6_LOCK) == A31_PLL6_LOCK) break; DELAY(1); } CLKDEV_DEVICE_UNLOCK(dev); return (0); } static int a31_pll6_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, k, n; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); k = ((val & A10_PLL6_FACTOR_K) >> A10_PLL6_FACTOR_K_SHIFT) + 1; n = ((val & A10_PLL6_FACTOR_N) >> A10_PLL6_FACTOR_N_SHIFT) + 1; switch (sc->id) { case CLKID_A31_PLL6: *freq = (*freq * n * k) / 2; break; case CLKID_A31_PLL6_X2: *freq = *freq * n * k; break; default: return (ENXIO); } return (0); } static int a80_pll4_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, n, div1, div2; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); n = (val & A80_PLL4_FACTOR_N) >> A80_PLL4_FACTOR_N_SHIFT; div1 = (val & A80_PLL4_PLL_DIV1) == 0 ? 1 : 2; div2 = (val & A80_PLL4_PLL_DIV2) == 0 ? 1 : 2; *freq = (*freq * n) / div1 / div2; return (0); } static int a64_pllhsic_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, n, m; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); n = ((val & A64_PLLHSIC_FACTOR_N) >> A64_PLLHSIC_FACTOR_N_SHIFT) + 1; m = ((val & A64_PLLHSIC_PRE_DIV_M) >> A64_PLLHSIC_PRE_DIV_M_SHIFT) + 1; *freq = (*freq * n) / m; return (0); } static int a64_pllhsic_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) { uint32_t val; /* * PLL_HSIC default is 480MHz, just enable it. */ CLKDEV_DEVICE_LOCK(dev); CLKDEV_READ_4(dev, reg, &val); val |= AW_PLL_ENABLE; CLKDEV_WRITE_4(dev, reg, val); CLKDEV_DEVICE_UNLOCK(dev); return (0); } static int a83t_pllcpux_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, n, p; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); n = (val & A83T_PLLCPUX_FACTOR_N) >> A83T_PLLCPUX_FACTOR_N_SHIFT; p = (val & A83T_PLLCPUX_OUT_EXT_DIVP) ? 4 : 1; *freq = (*freq * n) / p; return (0); } static int a83t_pllcpux_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t val; u_int n; n = *fout / fin; if (n < A83T_PLLCPUX_FACTOR_N_MIN || n > A83T_PLLCPUX_FACTOR_N_MAX) return (EINVAL); DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~A83T_PLLCPUX_FACTOR_N; val |= (n << A83T_PLLCPUX_FACTOR_N_SHIFT); val &= ~A83T_PLLCPUX_CLOCK_OUTPUT_DIS; PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } #define PLL(_type, _recalc, _set_freq, _init) \ [(_type)] = { \ .recalc = (_recalc), \ .set_freq = (_set_freq), \ .init = (_init) \ } static struct aw_pll_funcs aw_pll_func[] = { PLL(AWPLL_A10_PLL1, a10_pll1_recalc, a10_pll1_set_freq, NULL), PLL(AWPLL_A10_PLL2, a10_pll2_recalc, a10_pll2_set_freq, NULL), PLL(AWPLL_A10_PLL3, a10_pll3_recalc, a10_pll3_set_freq, a10_pll3_init), PLL(AWPLL_A10_PLL5, a10_pll5_recalc, NULL, NULL), PLL(AWPLL_A10_PLL6, a10_pll6_recalc, a10_pll6_set_freq, a10_pll6_init), PLL(AWPLL_A13_PLL2, a13_pll2_recalc, a13_pll2_set_freq, NULL), PLL(AWPLL_A23_PLL1, a23_pll1_recalc, a23_pll1_set_freq, NULL), PLL(AWPLL_A31_PLL1, a31_pll1_recalc, NULL, NULL), PLL(AWPLL_A31_PLL6, a31_pll6_recalc, NULL, a31_pll6_init), PLL(AWPLL_A80_PLL4, a80_pll4_recalc, NULL, NULL), PLL(AWPLL_A83T_PLLCPUX, a83t_pllcpux_recalc, a83t_pllcpux_set_freq, NULL), PLL(AWPLL_A64_PLLHSIC, a64_pllhsic_recalc, NULL, a64_pllhsic_init), PLL(AWPLL_H3_PLL1, a23_pll1_recalc, h3_pll1_set_freq, NULL), PLL(AWPLL_H3_PLL2, h3_pll2_recalc, h3_pll2_set_freq, NULL), }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-pll1-clk", AWPLL_A10_PLL1 }, { "allwinner,sun4i-a10-pll2-clk", AWPLL_A10_PLL2 }, { "allwinner,sun4i-a10-pll3-clk", AWPLL_A10_PLL3 }, { "allwinner,sun4i-a10-pll5-clk", AWPLL_A10_PLL5 }, { "allwinner,sun4i-a10-pll6-clk", AWPLL_A10_PLL6 }, { "allwinner,sun5i-a13-pll2-clk", AWPLL_A13_PLL2 }, { "allwinner,sun6i-a31-pll1-clk", AWPLL_A31_PLL1 }, { "allwinner,sun6i-a31-pll6-clk", AWPLL_A31_PLL6 }, { "allwinner,sun8i-a23-pll1-clk", AWPLL_A23_PLL1 }, { "allwinner,sun8i-a83t-pllcpux-clk", AWPLL_A83T_PLLCPUX }, { "allwinner,sun8i-h3-pll1-clk", AWPLL_H3_PLL1 }, { "allwinner,sun8i-h3-pll2-clk", AWPLL_H3_PLL2 }, { "allwinner,sun9i-a80-pll4-clk", AWPLL_A80_PLL4 }, { "allwinner,sun50i-a64-pllhsic-clk", AWPLL_A64_PLLHSIC }, { NULL, 0 } }; static int aw_pll_init(struct clknode *clk, device_t dev) { clknode_init_parent_idx(clk, 0); return (0); } static int aw_pll_set_gate(struct clknode *clk, bool enable) { struct aw_pll_sc *sc; uint32_t val; sc = clknode_get_softc(clk); DEVICE_LOCK(sc); PLL_READ(sc, &val); if (enable) val |= AW_PLL_ENABLE; else val &= ~AW_PLL_ENABLE; PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int aw_pll_recalc(struct clknode *clk, uint64_t *freq) { struct aw_pll_sc *sc; sc = clknode_get_softc(clk); if (aw_pll_func[sc->type].recalc == NULL) return (ENXIO); return (aw_pll_func[sc->type].recalc(sc, freq)); } static int aw_pll_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, int flags, int *stop) { struct aw_pll_sc *sc; sc = clknode_get_softc(clk); *stop = 1; if (aw_pll_func[sc->type].set_freq == NULL) return (ENXIO); return (aw_pll_func[sc->type].set_freq(sc, fin, fout, flags)); } static clknode_method_t aw_pll_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, aw_pll_init), CLKNODEMETHOD(clknode_set_gate, aw_pll_set_gate), CLKNODEMETHOD(clknode_recalc_freq, aw_pll_recalc), CLKNODEMETHOD(clknode_set_freq, aw_pll_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(aw_pll_clknode, aw_pll_clknode_class, aw_pll_clknode_methods, sizeof(struct aw_pll_sc), clknode_class); static int aw_pll_create(device_t dev, bus_addr_t paddr, struct clkdom *clkdom, const char *pclkname, const char *clkname, int index) { enum aw_pll_type type; struct clknode_init_def clkdef; struct aw_pll_sc *sc; struct clknode *clk; int error; type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; memset(&clkdef, 0, sizeof(clkdef)); clkdef.id = index; clkdef.name = clkname; if (pclkname != NULL) { clkdef.parent_names = malloc(sizeof(char *), M_OFWPROP, M_WAITOK); clkdef.parent_names[0] = pclkname; clkdef.parent_cnt = 1; } else clkdef.parent_cnt = 0; if (aw_pll_func[type].init != NULL) { error = aw_pll_func[type].init(device_get_parent(dev), paddr, &clkdef); if (error != 0) { device_printf(dev, "clock %s init failed\n", clkname); return (error); } } clk = clknode_create(clkdom, &aw_pll_clknode_class, &clkdef); if (clk == NULL) { device_printf(dev, "cannot create clock node\n"); return (ENXIO); } sc = clknode_get_softc(clk); sc->clkdev = device_get_parent(dev); sc->reg = paddr; sc->type = type; sc->id = clkdef.id; clknode_register(clkdom, clk); OF_prop_free(__DECONST(char *, clkdef.parent_names)); return (0); } static int aw_pll_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner PLL Clock"); return (BUS_PROBE_DEFAULT); } static int aw_pll_attach(device_t dev) { struct clkdom *clkdom; const char **names; int index, nout, error; clk_t clk_parent; uint32_t *indices; bus_addr_t paddr; bus_size_t psize; phandle_t node; node = ofw_bus_get_node(dev); if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { device_printf(dev, "couldn't parse 'reg' property\n"); return (ENXIO); } clkdom = clkdom_create(dev); nout = clk_parse_ofw_out_names(dev, node, &names, &indices); if (nout == 0) { device_printf(dev, "no clock outputs found\n"); error = ENOENT; goto fail; } if (clk_get_by_ofw_index(dev, 0, 0, &clk_parent) != 0) clk_parent = NULL; for (index = 0; index < nout; index++) { error = aw_pll_create(dev, paddr, clkdom, clk_parent ? clk_get_name(clk_parent) : NULL, names[index], nout == 1 ? 1 : index); if (error) goto fail; } if (clkdom_finit(clkdom) != 0) { device_printf(dev, "cannot finalize clkdom initialization\n"); error = ENXIO; goto fail; } if (bootverbose) clkdom_dump(clkdom); return (0); fail: return (error); } static device_method_t aw_pll_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_pll_probe), DEVMETHOD(device_attach, aw_pll_attach), DEVMETHOD_END }; static driver_t aw_pll_driver = { "aw_pll", aw_pll_methods, 0, }; static devclass_t aw_pll_devclass; EARLY_DRIVER_MODULE(aw_pll, simplebus, aw_pll_driver, aw_pll_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Index: head/sys/boot/fdt/dts/arm/sun7i-a20-hdmi.dtsi =================================================================== --- head/sys/boot/fdt/dts/arm/sun7i-a20-hdmi.dtsi (revision 308671) +++ head/sys/boot/fdt/dts/arm/sun7i-a20-hdmi.dtsi (revision 308672) @@ -1,111 +1,97 @@ /*- * Copyright (c) 2016 Jared McNeill * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ / { clocks { - pll3: clk@01c20010 { - #clock-cells = <1>; - compatible = "allwinner,sun4i-a10-pll3-clk"; - reg = <0x01c20010 0x4>; - clock-output-names = "pll3-1x", "pll3-2x"; - }; - - pll7: clk@01c20030 { - #clock-cells = <1>; - compatible = "allwinner,sun4i-a10-pll3-clk"; - reg = <0x01c20030 0x4>; - clock-output-names = "pll7-1x", "pll7-2x"; - }; - hdmi_clk: clk@01c20150 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-hdmi-clk"; reg = <0x01c20150 0x4>; - clocks = <&pll3 0>, <&pll7 0>, <&pll3 1>, <&pll7 1>; + clocks = <&pll3>, <&pll7>, <&pll3x2>, <&pll7x2>; clock-output-names = "hdmi"; }; lcd0_ch0_clk: clk@01c20118 { #clock-cells = <0>; #reset-cells = <0>; compatible = "allwinner,sun4i-a10-lcd-ch0-clk"; reg = <0x01c20118 0x4>; - clocks = <&pll3 0>, <&pll7 0>, <&pll3 1>, <&pll6 2>; + clocks = <&pll3>, <&pll7>, <&pll3x2>, <&pll6 2>; clock-output-names = "lcd0_ch0"; }; lcd0_ch1_clk: clk@01c2012c { #clock-cells = <1>; compatible = "allwinner,sun4i-a10-lcd-ch1-clk"; reg = <0x01c2012c 0x4>; - clocks = <&pll3 0>, <&pll7 0>, <&pll3 1>, <&pll7 1>; + clocks = <&pll3>, <&pll7>, <&pll3x2>, <&pll7x2>; clock-output-names = "lcd0_ch1_sclk1", "lcd0_ch1_sclk2"; }; de_be0_clk: clk@01c20104 { #clock-cells = <0>; #reset-cells = <0>; compatible = "allwinner,sun4i-a10-de-be-clk"; reg = <0x01c20104 0x4>; - clocks = <&pll3 0>, <&pll7 0>, <&pll5 1>; + clocks = <&pll3>, <&pll7>, <&pll5 1>; clock-output-names = "de_be0"; }; }; soc@01c00000 { hdmi: hdmi@01c16000 { compatible = "allwinner,sun7i-a20-hdmi"; reg = <0x01c16000 0x1000>; clocks = <&ahb_gates 43>, <&hdmi_clk>, <&lcd0_ch1_clk 1>; clock-names = "ahb", "hdmi", "lcd"; status = "disabled"; }; hdmiaudio { compatible = "allwinner,sun7i-a20-hdmiaudio"; status = "disabled"; }; fb: fb@01e60000 { compatible = "allwinner,sun7i-a20-fb"; reg = <0x01e60000 0x10000>, /* DEBE0 */ <0x01c0c000 0x1000>; /* LCD0 */ clocks = <&ahb_gates 44>, <&dram_gates 26>, <&de_be0_clk>, <&ahb_gates 36>, <&lcd0_ch1_clk 0>, <&lcd0_ch1_clk 1>; clock-names = "ahb_de_be", "dram_de_be", "de_be", "ahb_lcd", "lcd_ch1_sclk1", "lcd_ch1_sclk2"; resets = <&de_be0_clk>, <&lcd0_ch0_clk>; reset-names = "de_be", "lcd"; }; }; };