| // SPDX-License-Identifier: GPL-2.0+ | |
| /* Copyright (C) 2019 Stephan Gerhold */ | |
| #include <dm.h> | |
| #include <log.h> | |
| #include <video.h> | |
| #include <asm/io.h> | |
| #include <linux/bitfield.h> | |
| #include <linux/iopoll.h> | |
| #define MCDE_EXTSRC0A0 0x200 | |
| #define MCDE_EXTSRC0CONF 0x20C | |
| #define MCDE_EXTSRC0CONF_BPP GENMASK(11, 8) | |
| #define MCDE_OVL0CONF 0x404 | |
| #define MCDE_OVL0CONF_PPL GENMASK(10, 0) | |
| #define MCDE_OVL0CONF_LPF GENMASK(26, 16) | |
| #define MCDE_CHNL0SYNCHMOD 0x608 | |
| #define MCDE_CHNL0SYNCHMOD_SRC_SYNCH GENMASK(1, 0) | |
| #define MCDE_CHNL0SYNCHSW 0x60C | |
| #define MCDE_CHNL0SYNCHSW_SW_TRIG BIT(0) | |
| #define MCDE_CRA0 0x800 | |
| #define MCDE_CRA0_FLOEN BIT(0) | |
| #define MCDE_FLOW_COMPLETION_TIMEOUT 200000 /* us */ | |
| enum mcde_bpp { | |
| MCDE_EXTSRC0CONF_BPP_1BPP_PAL, | |
| MCDE_EXTSRC0CONF_BPP_2BPP_PAL, | |
| MCDE_EXTSRC0CONF_BPP_4BPP_PAL, | |
| MCDE_EXTSRC0CONF_BPP_8BPP_PAL, | |
| MCDE_EXTSRC0CONF_BPP_RGB444, | |
| MCDE_EXTSRC0CONF_BPP_ARGB4444, | |
| MCDE_EXTSRC0CONF_BPP_IRGB1555, | |
| MCDE_EXTSRC0CONF_BPP_RGB565, | |
| MCDE_EXTSRC0CONF_BPP_RGB888, | |
| MCDE_EXTSRC0CONF_BPP_XRGB8888, | |
| MCDE_EXTSRC0CONF_BPP_ARGB8888, | |
| MCDE_EXTSRC0CONF_BPP_YCBCR422, | |
| }; | |
| enum mcde_src_synch { | |
| MCDE_CHNL0SYNCHMOD_SRC_SYNCH_HARDWARE, | |
| MCDE_CHNL0SYNCHMOD_SRC_SYNCH_NO_SYNCH, | |
| MCDE_CHNL0SYNCHMOD_SRC_SYNCH_SOFTWARE, | |
| }; | |
| struct mcde_simple_priv { | |
| fdt_addr_t base; | |
| enum mcde_src_synch src_synch; | |
| }; | |
| static int mcde_simple_probe(struct udevice *dev) | |
| { | |
| struct mcde_simple_priv *priv = dev_get_priv(dev); | |
| struct video_uc_plat *plat = dev_get_uclass_plat(dev); | |
| struct video_priv *uc_priv = dev_get_uclass_priv(dev); | |
| u32 val; | |
| priv->base = dev_read_addr(dev); | |
| if (priv->base == FDT_ADDR_T_NONE) | |
| return -EINVAL; | |
| plat->base = readl(priv->base + MCDE_EXTSRC0A0); | |
| if (!plat->base) | |
| return -ENODEV; | |
| val = readl(priv->base + MCDE_OVL0CONF); | |
| uc_priv->xsize = FIELD_GET(MCDE_OVL0CONF_PPL, val); | |
| uc_priv->ysize = FIELD_GET(MCDE_OVL0CONF_LPF, val); | |
| uc_priv->rot = 0; | |
| val = readl(priv->base + MCDE_EXTSRC0CONF); | |
| switch (FIELD_GET(MCDE_EXTSRC0CONF_BPP, val)) { | |
| case MCDE_EXTSRC0CONF_BPP_RGB565: | |
| uc_priv->bpix = VIDEO_BPP16; | |
| break; | |
| case MCDE_EXTSRC0CONF_BPP_XRGB8888: | |
| case MCDE_EXTSRC0CONF_BPP_ARGB8888: | |
| uc_priv->bpix = VIDEO_BPP32; | |
| break; | |
| default: | |
| printf("unsupported format: %#x\n", val); | |
| return -EINVAL; | |
| } | |
| val = readl(priv->base + MCDE_CHNL0SYNCHMOD); | |
| priv->src_synch = FIELD_GET(MCDE_CHNL0SYNCHMOD_SRC_SYNCH, val); | |
| plat->size = uc_priv->xsize * uc_priv->ysize * VNBYTES(uc_priv->bpix); | |
| debug("MCDE base: %#lx, xsize: %d, ysize: %d, bpp: %d\n", | |
| plat->base, uc_priv->xsize, uc_priv->ysize, VNBITS(uc_priv->bpix)); | |
| video_set_flush_dcache(dev, true); | |
| return 0; | |
| } | |
| static int mcde_simple_video_sync(struct udevice *dev) | |
| { | |
| struct mcde_simple_priv *priv = dev_get_priv(dev); | |
| unsigned int val; | |
| if (priv->src_synch != MCDE_CHNL0SYNCHMOD_SRC_SYNCH_SOFTWARE) | |
| return 0; | |
| /* Enable flow */ | |
| val = readl(priv->base + MCDE_CRA0); | |
| val |= MCDE_CRA0_FLOEN; | |
| writel(val, priv->base + MCDE_CRA0); | |
| /* Trigger a software sync */ | |
| writel(MCDE_CHNL0SYNCHSW_SW_TRIG, priv->base + MCDE_CHNL0SYNCHSW); | |
| /* Disable flow */ | |
| val = readl(priv->base + MCDE_CRA0); | |
| val &= ~MCDE_CRA0_FLOEN; | |
| writel(val, priv->base + MCDE_CRA0); | |
| /* Wait for completion */ | |
| return readl_poll_timeout(priv->base + MCDE_CRA0, val, | |
| !(val & MCDE_CRA0_FLOEN), | |
| MCDE_FLOW_COMPLETION_TIMEOUT); | |
| } | |
| static struct video_ops mcde_simple_ops = { | |
| .video_sync = mcde_simple_video_sync, | |
| }; | |
| static const struct udevice_id mcde_simple_ids[] = { | |
| { .compatible = "ste,mcde" }, | |
| { } | |
| }; | |
| U_BOOT_DRIVER(mcde_simple) = { | |
| .name = "mcde_simple", | |
| .id = UCLASS_VIDEO, | |
| .ops = &mcde_simple_ops, | |
| .of_match = mcde_simple_ids, | |
| .probe = mcde_simple_probe, | |
| .priv_auto = sizeof(struct mcde_simple_priv), | |
| }; |