diff options
Diffstat (limited to 'drivers/gpu/drm/stm')
-rw-r--r-- | drivers/gpu/drm/stm/Kconfig | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/drv.c | 23 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/dw_mipi_dsi-stm.c | 352 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/ltdc.c | 470 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/ltdc.h | 4 |
6 files changed, 527 insertions, 333 deletions
diff --git a/drivers/gpu/drm/stm/Kconfig b/drivers/gpu/drm/stm/Kconfig index 8fe5b184b4e8..35367ada3bc1 100644 --- a/drivers/gpu/drm/stm/Kconfig +++ b/drivers/gpu/drm/stm/Kconfig @@ -4,7 +4,7 @@ config DRM_STM select DRM_KMS_HELPER select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER - select DRM_PANEL + select DRM_PANEL_BRIDGE select VIDEOMODE_HELPERS select FB_PROVIDE_GET_FB_UNMAPPED_AREA @@ -13,3 +13,10 @@ config DRM_STM STMicroelectronics STM32 MCUs. To compile this driver as a module, choose M here: the module will be called stm-drm. + +config DRM_STM_DSI + tristate "STMicroelectronics specific extensions for Synopsys MIPI DSI" + depends on DRM_STM + select DRM_DW_MIPI_DSI + help + Choose this option for MIPI DSI support on STMicroelectronics SoC. diff --git a/drivers/gpu/drm/stm/Makefile b/drivers/gpu/drm/stm/Makefile index a09ecf450218..d883adc365a2 100644 --- a/drivers/gpu/drm/stm/Makefile +++ b/drivers/gpu/drm/stm/Makefile @@ -2,4 +2,6 @@ stm-drm-y := \ drv.o \ ltdc.o +obj-$(CONFIG_DRM_STM_DSI) += dw_mipi_dsi-stm.o + obj-$(CONFIG_DRM_STM) += stm-drm.o diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index 83ab48f1fd00..b333b37f3f89 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -20,13 +20,6 @@ #include "ltdc.h" -#define DRIVER_NAME "stm" -#define DRIVER_DESC "STMicroelectronics SoC DRM" -#define DRIVER_DATE "20170330" -#define DRIVER_MAJOR 1 -#define DRIVER_MINOR 0 -#define DRIVER_PATCH_LEVEL 0 - #define STM_MAX_FB_WIDTH 2048 #define STM_MAX_FB_HEIGHT 2048 /* same as width to handle orientation */ @@ -59,16 +52,14 @@ static struct drm_driver drv_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, .lastclose = drv_lastclose, - .name = DRIVER_NAME, - .desc = DRIVER_DESC, - .date = DRIVER_DATE, - .major = DRIVER_MAJOR, - .minor = DRIVER_MINOR, - .patchlevel = DRIVER_PATCH_LEVEL, + .name = "stm", + .desc = "STMicroelectronics SoC DRM", + .date = "20170330", + .major = 1, + .minor = 0, + .patchlevel = 0, .fops = &drv_driver_fops, .dumb_create = drm_gem_cma_dumb_create, - .dumb_map_offset = drm_gem_cma_dumb_map_offset, - .dumb_destroy = drm_gem_dumb_destroy, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_free_object_unlocked = drm_gem_cma_free_object, @@ -206,7 +197,7 @@ static struct platform_driver stm_drm_platform_driver = { .probe = stm_drm_platform_probe, .remove = stm_drm_platform_remove, .driver = { - .name = DRIVER_NAME, + .name = "stm32-display", .of_match_table = drv_dt_ids, }, }; diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c new file mode 100644 index 000000000000..568c5d0461ea --- /dev/null +++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c @@ -0,0 +1,352 @@ +/* + * Copyright (C) STMicroelectronics SA 2017 + * + * Authors: Philippe Cornu <philippe.cornu@st.com> + * Yannick Fertre <yannick.fertre@st.com> + * + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <drm/drmP.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/bridge/dw_mipi_dsi.h> +#include <video/mipi_display.h> + +/* DSI wrapper register & bit definitions */ +/* Note: registers are named as in the Reference Manual */ +#define DSI_WCFGR 0x0400 /* Wrapper ConFiGuration Reg */ +#define WCFGR_DSIM BIT(0) /* DSI Mode */ +#define WCFGR_COLMUX GENMASK(3, 1) /* COLor MUltipleXing */ + +#define DSI_WCR 0x0404 /* Wrapper Control Reg */ +#define WCR_DSIEN BIT(3) /* DSI ENable */ + +#define DSI_WISR 0x040C /* Wrapper Interrupt and Status Reg */ +#define WISR_PLLLS BIT(8) /* PLL Lock Status */ +#define WISR_RRS BIT(12) /* Regulator Ready Status */ + +#define DSI_WPCR0 0x0418 /* Wrapper Phy Conf Reg 0 */ +#define WPCR0_UIX4 GENMASK(5, 0) /* Unit Interval X 4 */ +#define WPCR0_TDDL BIT(16) /* Turn Disable Data Lanes */ + +#define DSI_WRPCR 0x0430 /* Wrapper Regulator & Pll Ctrl Reg */ +#define WRPCR_PLLEN BIT(0) /* PLL ENable */ +#define WRPCR_NDIV GENMASK(8, 2) /* pll loop DIVision Factor */ +#define WRPCR_IDF GENMASK(14, 11) /* pll Input Division Factor */ +#define WRPCR_ODF GENMASK(17, 16) /* pll Output Division Factor */ +#define WRPCR_REGEN BIT(24) /* REGulator ENable */ +#define WRPCR_BGREN BIT(28) /* BandGap Reference ENable */ +#define IDF_MIN 1 +#define IDF_MAX 7 +#define NDIV_MIN 10 +#define NDIV_MAX 125 +#define ODF_MIN 1 +#define ODF_MAX 8 + +/* dsi color format coding according to the datasheet */ +enum dsi_color { + DSI_RGB565_CONF1, + DSI_RGB565_CONF2, + DSI_RGB565_CONF3, + DSI_RGB666_CONF1, + DSI_RGB666_CONF2, + DSI_RGB888, +}; + +#define LANE_MIN_KBPS 31250 +#define LANE_MAX_KBPS 500000 + +/* Sleep & timeout for regulator on/off, pll lock/unlock & fifo empty */ +#define SLEEP_US 1000 +#define TIMEOUT_US 200000 + +struct dw_mipi_dsi_stm { + void __iomem *base; + struct clk *pllref_clk; +}; + +static inline void dsi_write(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 val) +{ + writel(val, dsi->base + reg); +} + +static inline u32 dsi_read(struct dw_mipi_dsi_stm *dsi, u32 reg) +{ + return readl(dsi->base + reg); +} + +static inline void dsi_set(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 mask) +{ + dsi_write(dsi, reg, dsi_read(dsi, reg) | mask); +} + +static inline void dsi_clear(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 mask) +{ + dsi_write(dsi, reg, dsi_read(dsi, reg) & ~mask); +} + +static inline void dsi_update_bits(struct dw_mipi_dsi_stm *dsi, u32 reg, + u32 mask, u32 val) +{ + dsi_write(dsi, reg, (dsi_read(dsi, reg) & ~mask) | val); +} + +static enum dsi_color dsi_color_from_mipi(enum mipi_dsi_pixel_format fmt) +{ + switch (fmt) { + case MIPI_DSI_FMT_RGB888: + return DSI_RGB888; + case MIPI_DSI_FMT_RGB666: + return DSI_RGB666_CONF2; + case MIPI_DSI_FMT_RGB666_PACKED: + return DSI_RGB666_CONF1; + case MIPI_DSI_FMT_RGB565: + return DSI_RGB565_CONF1; + default: + DRM_DEBUG_DRIVER("MIPI color invalid, so we use rgb888\n"); + } + return DSI_RGB888; +} + +static int dsi_pll_get_clkout_khz(int clkin_khz, int idf, int ndiv, int odf) +{ + /* prevent from division by 0 */ + if (idf * odf) + return DIV_ROUND_CLOSEST(clkin_khz * ndiv, idf * odf); + + return 0; +} + +static int dsi_pll_get_params(int clkin_khz, int clkout_khz, + int *idf, int *ndiv, int *odf) +{ + int i, o, n, n_min, n_max; + int fvco_min, fvco_max, delta, best_delta; /* all in khz */ + + /* Early checks preventing division by 0 & odd results */ + if ((clkin_khz <= 0) || (clkout_khz <= 0)) + return -EINVAL; + + fvco_min = LANE_MIN_KBPS * 2 * ODF_MAX; + fvco_max = LANE_MAX_KBPS * 2 * ODF_MIN; + + best_delta = 1000000; /* big started value (1000000khz) */ + + for (i = IDF_MIN; i <= IDF_MAX; i++) { + /* Compute ndiv range according to Fvco */ + n_min = ((fvco_min * i) / (2 * clkin_khz)) + 1; + n_max = (fvco_max * i) / (2 * clkin_khz); + + /* No need to continue idf loop if we reach ndiv max */ + if (n_min >= NDIV_MAX) + break; + + /* Clamp ndiv to valid values */ + if (n_min < NDIV_MIN) + n_min = NDIV_MIN; + if (n_max > NDIV_MAX) + n_max = NDIV_MAX; + + for (o = ODF_MIN; o <= ODF_MAX; o *= 2) { + n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz); + /* Check ndiv according to vco range */ + if ((n < n_min) || (n > n_max)) + continue; + /* Check if new delta is better & saves parameters */ + delta = dsi_pll_get_clkout_khz(clkin_khz, i, n, o) - + clkout_khz; + if (delta < 0) + delta = -delta; + if (delta < best_delta) { + *idf = i; + *ndiv = n; + *odf = o; + best_delta = delta; + } + /* fast return in case of "perfect result" */ + if (!delta) + return 0; + } + } + + return 0; +} + +static int dw_mipi_dsi_phy_init(void *priv_data) +{ + struct dw_mipi_dsi_stm *dsi = priv_data; + u32 val; + int ret; + + /* Enable the regulator */ + dsi_set(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN); + ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_RRS, + SLEEP_US, TIMEOUT_US); + if (ret) + DRM_DEBUG_DRIVER("!TIMEOUT! waiting REGU, let's continue\n"); + + /* Enable the DSI PLL & wait for its lock */ + dsi_set(dsi, DSI_WRPCR, WRPCR_PLLEN); + ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_PLLLS, + SLEEP_US, TIMEOUT_US); + if (ret) + DRM_DEBUG_DRIVER("!TIMEOUT! waiting PLL, let's continue\n"); + + /* Enable the DSI wrapper */ + dsi_set(dsi, DSI_WCR, WCR_DSIEN); + + return 0; +} + +static int +dw_mipi_dsi_get_lane_mbps(void *priv_data, struct drm_display_mode *mode, + unsigned long mode_flags, u32 lanes, u32 format, + unsigned int *lane_mbps) +{ + struct dw_mipi_dsi_stm *dsi = priv_data; + unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz; + int ret, bpp; + u32 val; + + pll_in_khz = (unsigned int)(clk_get_rate(dsi->pllref_clk) / 1000); + + /* Compute requested pll out */ + bpp = mipi_dsi_pixel_format_to_bpp(format); + pll_out_khz = mode->clock * bpp / lanes; + /* Add 20% to pll out to be higher than pixel bw (burst mode only) */ + pll_out_khz = (pll_out_khz * 12) / 10; + if (pll_out_khz > LANE_MAX_KBPS) { + pll_out_khz = LANE_MAX_KBPS; + DRM_WARN("Warning max phy mbps is used\n"); + } + if (pll_out_khz < LANE_MIN_KBPS) { + pll_out_khz = LANE_MIN_KBPS; + DRM_WARN("Warning min phy mbps is used\n"); + } + + /* Compute best pll parameters */ + idf = 0; + ndiv = 0; + odf = 0; + ret = dsi_pll_get_params(pll_in_khz, pll_out_khz, &idf, &ndiv, &odf); + if (ret) + DRM_WARN("Warning dsi_pll_get_params(): bad params\n"); + + /* Get the adjusted pll out value */ + pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf); + + /* Set the PLL division factors */ + dsi_update_bits(dsi, DSI_WRPCR, WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF, + (ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16)); + + /* Compute uix4 & set the bit period in high-speed mode */ + val = 4000000 / pll_out_khz; + dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val); + + /* Select video mode by resetting DSIM bit */ + dsi_clear(dsi, DSI_WCFGR, WCFGR_DSIM); + + /* Select the color coding */ + dsi_update_bits(dsi, DSI_WCFGR, WCFGR_COLMUX, + dsi_color_from_mipi(format) << 1); + + *lane_mbps = pll_out_khz / 1000; + + DRM_DEBUG_DRIVER("pll_in %ukHz pll_out %ukHz lane_mbps %uMHz\n", + pll_in_khz, pll_out_khz, *lane_mbps); + + return 0; +} + +static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_stm_phy_ops = { + .init = dw_mipi_dsi_phy_init, + .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, +}; + +static struct dw_mipi_dsi_plat_data dw_mipi_dsi_stm_plat_data = { + .max_data_lanes = 2, + .phy_ops = &dw_mipi_dsi_stm_phy_ops, +}; + +static const struct of_device_id dw_mipi_dsi_stm_dt_ids[] = { + { .compatible = "st,stm32-dsi", .data = &dw_mipi_dsi_stm_plat_data, }, + { }, +}; +MODULE_DEVICE_TABLE(of, dw_mipi_dsi_stm_dt_ids); + +static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_mipi_dsi_stm *dsi; + struct resource *res; + int ret; + + dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + DRM_ERROR("Unable to get resource\n"); + return -ENODEV; + } + + dsi->base = devm_ioremap_resource(dev, res); + if (IS_ERR(dsi->base)) { + DRM_ERROR("Unable to get dsi registers\n"); + return PTR_ERR(dsi->base); + } + + dsi->pllref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(dsi->pllref_clk)) { + ret = PTR_ERR(dsi->pllref_clk); + dev_err(dev, "Unable to get pll reference clock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(dsi->pllref_clk); + if (ret) { + dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__); + return ret; + } + + dw_mipi_dsi_stm_plat_data.base = dsi->base; + dw_mipi_dsi_stm_plat_data.priv_data = dsi; + + ret = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data); + if (ret) { + DRM_ERROR("Failed to initialize mipi dsi host\n"); + clk_disable_unprepare(dsi->pllref_clk); + } + + return ret; +} + +static int dw_mipi_dsi_stm_remove(struct platform_device *pdev) +{ + struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data; + + clk_disable_unprepare(dsi->pllref_clk); + dw_mipi_dsi_remove(pdev); + + return 0; +} + +static struct platform_driver dw_mipi_dsi_stm_driver = { + .probe = dw_mipi_dsi_stm_probe, + .remove = dw_mipi_dsi_stm_remove, + .driver = { + .of_match_table = dw_mipi_dsi_stm_dt_ids, + .name = "dw_mipi_dsi-stm", + }, +}; + +module_platform_driver(dw_mipi_dsi_stm_driver); + +MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>"); +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics DW MIPI DSI host controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 1b9483d4f2a4..d394a03632c4 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -21,7 +21,7 @@ #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> +#include <drm/drm_bridge.h> #include <drm/drm_plane_helper.h> #include <video/videomode.h> @@ -42,52 +42,52 @@ * an extra offset specified with reg_ofs. */ #define REG_OFS_NONE 0 -#define REG_OFS_4 4 /* Insertion of "Layer Configuration 2" reg */ +#define REG_OFS_4 4 /* Insertion of "Layer Conf. 2" reg */ #define REG_OFS (ldev->caps.reg_ofs) -#define LAY_OFS 0x80 /* Register Offset between 2 layers */ +#define LAY_OFS 0x80 /* Register Offset between 2 layers */ /* Global register offsets */ -#define LTDC_IDR 0x0000 /* IDentification */ -#define LTDC_LCR 0x0004 /* Layer Count */ -#define LTDC_SSCR 0x0008 /* Synchronization Size Configuration */ -#define LTDC_BPCR 0x000C /* Back Porch Configuration */ -#define LTDC_AWCR 0x0010 /* Active Width Configuration */ -#define LTDC_TWCR 0x0014 /* Total Width Configuration */ -#define LTDC_GCR 0x0018 /* Global Control */ -#define LTDC_GC1R 0x001C /* Global Configuration 1 */ -#define LTDC_GC2R 0x0020 /* Global Configuration 2 */ -#define LTDC_SRCR 0x0024 /* Shadow Reload Configuration */ -#define LTDC_GACR 0x0028 /* GAmma Correction */ -#define LTDC_BCCR 0x002C /* Background Color Configuration */ -#define LTDC_IER 0x0034 /* Interrupt Enable */ -#define LTDC_ISR 0x0038 /* Interrupt Status */ -#define LTDC_ICR 0x003C /* Interrupt Clear */ -#define LTDC_LIPCR 0x0040 /* Line Interrupt Position Configuration */ -#define LTDC_CPSR 0x0044 /* Current Position Status */ -#define LTDC_CDSR 0x0048 /* Current Display Status */ +#define LTDC_IDR 0x0000 /* IDentification */ +#define LTDC_LCR 0x0004 /* Layer Count */ +#define LTDC_SSCR 0x0008 /* Synchronization Size Configuration */ +#define LTDC_BPCR 0x000C /* Back Porch Configuration */ +#define LTDC_AWCR 0x0010 /* Active Width Configuration */ +#define LTDC_TWCR 0x0014 /* Total Width Configuration */ +#define LTDC_GCR 0x0018 /* Global Control */ +#define LTDC_GC1R 0x001C /* Global Configuration 1 */ +#define LTDC_GC2R 0x0020 /* Global Configuration 2 */ +#define LTDC_SRCR 0x0024 /* Shadow Reload Configuration */ +#define LTDC_GACR 0x0028 /* GAmma Correction */ +#define LTDC_BCCR 0x002C /* Background Color Configuration */ +#define LTDC_IER 0x0034 /* Interrupt Enable */ +#define LTDC_ISR 0x0038 /* Interrupt Status */ +#define LTDC_ICR 0x003C /* Interrupt Clear */ +#define LTDC_LIPCR 0x0040 /* Line Interrupt Position Conf. */ +#define LTDC_CPSR 0x0044 /* Current Position Status */ +#define LTDC_CDSR 0x0048 /* Current Display Status */ /* Layer register offsets */ -#define LTDC_L1LC1R (0x0080) /* L1 Layer Configuration 1 */ -#define LTDC_L1LC2R (0x0084) /* L1 Layer Configuration 2 */ -#define LTDC_L1CR (0x0084 + REG_OFS) /* L1 Control */ -#define LTDC_L1WHPCR (0x0088 + REG_OFS) /* L1 Window Hor Position Config */ -#define LTDC_L1WVPCR (0x008C + REG_OFS) /* L1 Window Vert Position Config */ -#define LTDC_L1CKCR (0x0090 + REG_OFS) /* L1 Color Keying Configuration */ -#define LTDC_L1PFCR (0x0094 + REG_OFS) /* L1 Pixel Format Configuration */ -#define LTDC_L1CACR (0x0098 + REG_OFS) /* L1 Constant Alpha Config */ -#define LTDC_L1DCCR (0x009C + REG_OFS) /* L1 Default Color Configuration */ -#define LTDC_L1BFCR (0x00A0 + REG_OFS) /* L1 Blend Factors Configuration */ -#define LTDC_L1FBBCR (0x00A4 + REG_OFS) /* L1 FrameBuffer Bus Control */ -#define LTDC_L1AFBCR (0x00A8 + REG_OFS) /* L1 AuxFB Control */ -#define LTDC_L1CFBAR (0x00AC + REG_OFS) /* L1 Color FrameBuffer Address */ -#define LTDC_L1CFBLR (0x00B0 + REG_OFS) /* L1 Color FrameBuffer Length */ -#define LTDC_L1CFBLNR (0x00B4 + REG_OFS) /* L1 Color FrameBuffer Line Nb */ -#define LTDC_L1AFBAR (0x00B8 + REG_OFS) /* L1 AuxFB Address */ -#define LTDC_L1AFBLR (0x00BC + REG_OFS) /* L1 AuxFB Length */ -#define LTDC_L1AFBLNR (0x00C0 + REG_OFS) /* L1 AuxFB Line Number */ -#define LTDC_L1CLUTWR (0x00C4 + REG_OFS) /* L1 CLUT Write */ -#define LTDC_L1YS1R (0x00E0 + REG_OFS) /* L1 YCbCr Scale 1 */ -#define LTDC_L1YS2R (0x00E4 + REG_OFS) /* L1 YCbCr Scale 2 */ +#define LTDC_L1LC1R (0x80) /* L1 Layer Configuration 1 */ +#define LTDC_L1LC2R (0x84) /* L1 Layer Configuration 2 */ +#define LTDC_L1CR (0x84 + REG_OFS)/* L1 Control */ +#define LTDC_L1WHPCR (0x88 + REG_OFS)/* L1 Window Hor Position Config */ +#define LTDC_L1WVPCR (0x8C + REG_OFS)/* L1 Window Vert Position Config */ +#define LTDC_L1CKCR (0x90 + REG_OFS)/* L1 Color Keying Configuration */ +#define LTDC_L1PFCR (0x94 + REG_OFS)/* L1 Pixel Format Configuration */ +#define LTDC_L1CACR (0x98 + REG_OFS)/* L1 Constant Alpha Config */ +#define LTDC_L1DCCR (0x9C + REG_OFS)/* L1 Default Color Configuration */ +#define LTDC_L1BFCR (0xA0 + REG_OFS)/* L1 Blend Factors Configuration */ +#define LTDC_L1FBBCR (0xA4 + REG_OFS)/* L1 FrameBuffer Bus Control */ +#define LTDC_L1AFBCR (0xA8 + REG_OFS)/* L1 AuxFB Control */ +#define LTDC_L1CFBAR (0xAC + REG_OFS)/* L1 Color FrameBuffer Address */ +#define LTDC_L1CFBLR (0xB0 + REG_OFS)/* L1 Color FrameBuffer Length */ +#define LTDC_L1CFBLNR (0xB4 + REG_OFS)/* L1 Color FrameBuffer Line Nb */ +#define LTDC_L1AFBAR (0xB8 + REG_OFS)/* L1 AuxFB Address */ +#define LTDC_L1AFBLR (0xBC + REG_OFS)/* L1 AuxFB Length */ +#define LTDC_L1AFBLNR (0xC0 + REG_OFS)/* L1 AuxFB Line Number */ +#define LTDC_L1CLUTWR (0xC4 + REG_OFS)/* L1 CLUT Write */ +#define LTDC_L1YS1R (0xE0 + REG_OFS)/* L1 YCbCr Scale 1 */ +#define LTDC_L1YS2R (0xE4 + REG_OFS)/* L1 YCbCr Scale 2 */ /* Bit definitions */ #define SSCR_VSH GENMASK(10, 0) /* Vertical Synchronization Height */ @@ -104,10 +104,10 @@ #define GCR_LTDCEN BIT(0) /* LTDC ENable */ #define GCR_DEN BIT(16) /* Dither ENable */ -#define GCR_PCPOL BIT(28) /* Pixel Clock POLarity */ -#define GCR_DEPOL BIT(29) /* Data Enable POLarity */ -#define GCR_VSPOL BIT(30) /* Vertical Synchro POLarity */ -#define GCR_HSPOL BIT(31) /* Horizontal Synchro POLarity */ +#define GCR_PCPOL BIT(28) /* Pixel Clock POLarity-Inverted */ +#define GCR_DEPOL BIT(29) /* Data Enable POLarity-High */ +#define GCR_VSPOL BIT(30) /* Vertical Synchro POLarity-High */ +#define GCR_HSPOL BIT(31) /* Horizontal Synchro POLarity-High */ #define GC1R_WBCH GENMASK(3, 0) /* Width of Blue CHannel output */ #define GC1R_WGCH GENMASK(7, 4) /* Width of Green Channel output */ @@ -172,60 +172,52 @@ #define LXCFBLR_CFBLL GENMASK(12, 0) /* Color Frame Buffer Line Length */ #define LXCFBLR_CFBP GENMASK(28, 16) /* Color Frame Buffer Pitch in bytes */ -#define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */ +#define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */ -#define HSPOL_AL 0 /* Horizontal Sync POLarity Active Low */ -#define VSPOL_AL 0 /* Vertical Sync POLarity Active Low */ -#define DEPOL_AL 0 /* Data Enable POLarity Active Low */ -#define PCPOL_IPC 0 /* Input Pixel Clock */ -#define HSPOL_AH GCR_HSPOL /* Horizontal Sync POLarity Active High */ -#define VSPOL_AH GCR_VSPOL /* Vertical Sync POLarity Active High */ -#define DEPOL_AH GCR_DEPOL /* Data Enable POLarity Active High */ -#define PCPOL_IIPC GCR_PCPOL /* Inverted Input Pixel Clock */ -#define CONSTA_MAX 0xFF /* CONSTant Alpha MAX= 1.0 */ -#define BF1_PAXCA 0x600 /* Pixel Alpha x Constant Alpha */ -#define BF1_CA 0x400 /* Constant Alpha */ -#define BF2_1PAXCA 0x007 /* 1 - (Pixel Alpha x Constant Alpha) */ -#define BF2_1CA 0x005 /* 1 - Constant Alpha */ +#define CONSTA_MAX 0xFF /* CONSTant Alpha MAX= 1.0 */ +#define BF1_PAXCA 0x600 /* Pixel Alpha x Constant Alpha */ +#define BF1_CA 0x400 /* Constant Alpha */ +#define BF2_1PAXCA 0x007 /* 1 - (Pixel Alpha x Constant Alpha) */ +#define BF2_1CA 0x005 /* 1 - Constant Alpha */ -#define NB_PF 8 /* Max nb of HW pixel format */ +#define NB_PF 8 /* Max nb of HW pixel format */ enum ltdc_pix_fmt { PF_NONE, /* RGB formats */ - PF_ARGB8888, /* ARGB [32 bits] */ - PF_RGBA8888, /* RGBA [32 bits] */ - PF_RGB888, /* RGB [24 bits] */ - PF_RGB565, /* RGB [16 bits] */ - PF_ARGB1555, /* ARGB A:1 bit RGB:15 bits [16 bits] */ - PF_ARGB4444, /* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */ + PF_ARGB8888, /* ARGB [32 bits] */ + PF_RGBA8888, /* RGBA [32 bits] */ + PF_RGB888, /* RGB [24 bits] */ + PF_RGB565, /* RGB [16 bits] */ + PF_ARGB1555, /* ARGB A:1 bit RGB:15 bits [16 bits] */ + PF_ARGB4444, /* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */ /* Indexed formats */ - PF_L8, /* Indexed 8 bits [8 bits] */ - PF_AL44, /* Alpha:4 bits + indexed 4 bits [8 bits] */ - PF_AL88 /* Alpha:8 bits + indexed 8 bits [16 bits] */ + PF_L8, /* Indexed 8 bits [8 bits] */ + PF_AL44, /* Alpha:4 bits + indexed 4 bits [8 bits] */ + PF_AL88 /* Alpha:8 bits + indexed 8 bits [16 bits] */ }; /* The index gives the encoding of the pixel format for an HW version */ static const enum ltdc_pix_fmt ltdc_pix_fmt_a0[NB_PF] = { - PF_ARGB8888, /* 0x00 */ - PF_RGB888, /* 0x01 */ - PF_RGB565, /* 0x02 */ - PF_ARGB1555, /* 0x03 */ - PF_ARGB4444, /* 0x04 */ - PF_L8, /* 0x05 */ - PF_AL44, /* 0x06 */ - PF_AL88 /* 0x07 */ + PF_ARGB8888, /* 0x00 */ + PF_RGB888, /* 0x01 */ + PF_RGB565, /* 0x02 */ + PF_ARGB1555, /* 0x03 */ + PF_ARGB4444, /* 0x04 */ + PF_L8, /* 0x05 */ + PF_AL44, /* 0x06 */ + PF_AL88 /* 0x07 */ }; static const enum ltdc_pix_fmt ltdc_pix_fmt_a1[NB_PF] = { - PF_ARGB8888, /* 0x00 */ - PF_RGB888, /* 0x01 */ - PF_RGB565, /* 0x02 */ - PF_RGBA8888, /* 0x03 */ - PF_AL44, /* 0x04 */ - PF_L8, /* 0x05 */ - PF_ARGB1555, /* 0x06 */ - PF_ARGB4444 /* 0x07 */ + PF_ARGB8888, /* 0x00 */ + PF_RGB888, /* 0x01 */ + PF_RGB565, /* 0x02 */ + PF_RGBA8888, /* 0x03 */ + PF_AL44, /* 0x04 */ + PF_L8, /* 0x05 */ + PF_ARGB1555, /* 0x06 */ + PF_ARGB4444 /* 0x07 */ }; static inline u32 reg_read(void __iomem *base, u32 reg) @@ -269,11 +261,6 @@ static inline struct ltdc_device *encoder_to_ltdc(struct drm_encoder *enc) return (struct ltdc_device *)enc->dev->dev_private; } -static inline struct ltdc_device *connector_to_ltdc(struct drm_connector *con) -{ - return (struct ltdc_device *)con->dev->dev_private; -} - static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt) { enum ltdc_pix_fmt pf; @@ -307,7 +294,7 @@ static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt) default: pf = PF_NONE; break; - /* Note: There are no DRM_FORMAT for AL44 and AL88 */ + /* Note: There are no DRM_FORMAT for AL44 and AL88 */ } return pf; @@ -330,8 +317,8 @@ static inline u32 to_drm_pixelformat(enum ltdc_pix_fmt pf) return DRM_FORMAT_ARGB4444; case PF_L8: return DRM_FORMAT_C8; - case PF_AL44: /* No DRM support */ - case PF_AL88: /* No DRM support */ + case PF_AL44: /* No DRM support */ + case PF_AL88: /* No DRM support */ case PF_NONE: default: return 0; @@ -375,18 +362,8 @@ static irqreturn_t ltdc_irq(int irq, void *arg) * DRM_CRTC */ -static void ltdc_crtc_load_lut(struct drm_crtc *crtc) -{ - struct ltdc_device *ldev = crtc_to_ltdc(crtc); - unsigned int i, lay; - - for (lay = 0; lay < ldev->caps.nb_layers; lay++) - for (i = 0; i < 256; i++) - reg_write(ldev->regs, LTDC_L1CLUTWR + lay * LAY_OFS, - ldev->clut[i]); -} - -static void ltdc_crtc_enable(struct drm_crtc *crtc) +static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) { struct ltdc_device *ldev = crtc_to_ltdc(crtc); @@ -407,7 +384,8 @@ static void ltdc_crtc_enable(struct drm_crtc *crtc) drm_crtc_vblank_on(crtc); } -static void ltdc_crtc_disable(struct drm_crtc *crtc) +static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) { struct ltdc_device *ldev = crtc_to_ltdc(crtc); @@ -462,20 +440,20 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc) clk_enable(ldev->pixel_clk); - /* Configures the HS, VS, DE and PC polarities. */ - val = HSPOL_AL | VSPOL_AL | DEPOL_AL | PCPOL_IPC; + /* Configures the HS, VS, DE and PC polarities. Default Active Low */ + val = 0; if (vm.flags & DISPLAY_FLAGS_HSYNC_HIGH) - val |= HSPOL_AH; + val |= GCR_HSPOL; if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH) - val |= VSPOL_AH; + val |= GCR_VSPOL; if (vm.flags & DISPLAY_FLAGS_DE_HIGH) - val |= DEPOL_AH; + val |= GCR_DEPOL; if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) - val |= PCPOL_IIPC; + val |= GCR_PCPOL; reg_update_bits(ldev->regs, LTDC_GCR, GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val); @@ -522,12 +500,11 @@ static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc, } } -static struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = { - .load_lut = ltdc_crtc_load_lut, - .enable = ltdc_crtc_enable, - .disable = ltdc_crtc_disable, +static const struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = { .mode_set_nofb = ltdc_crtc_mode_set_nofb, .atomic_flush = ltdc_crtc_atomic_flush, + .atomic_enable = ltdc_crtc_atomic_enable, + .atomic_disable = ltdc_crtc_atomic_disable, }; int ltdc_crtc_enable_vblank(struct drm_device *ddev, unsigned int pipe) @@ -548,7 +525,7 @@ void ltdc_crtc_disable_vblank(struct drm_device *ddev, unsigned int pipe) reg_clear(ldev->regs, LTDC_IER, IER_LIE); } -static struct drm_crtc_funcs ltdc_crtc_funcs = { +static const struct drm_crtc_funcs ltdc_crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, @@ -613,11 +590,11 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, src_w = state->src_w >> 16; src_h = state->src_h >> 16; - DRM_DEBUG_DRIVER( - "plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n", - plane->base.id, fb->base.id, - src_w, src_h, src_x, src_y, - state->crtc_w, state->crtc_h, state->crtc_x, state->crtc_y); + DRM_DEBUG_DRIVER("plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n", + plane->base.id, fb->base.id, + src_w, src_h, src_x, src_y, + state->crtc_w, state->crtc_h, + state->crtc_x, state->crtc_y); bpcr = reg_read(ldev->regs, LTDC_BPCR); ahbp = (bpcr & BPCR_AHBP) >> 16; @@ -642,7 +619,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, if (val == NB_PF) { DRM_ERROR("Pixel format %.4s not supported\n", (char *)&fb->format->format); - val = 0; /* set by default ARGB 32 bits */ + val = 0; /* set by default ARGB 32 bits */ } reg_update_bits(ldev->regs, LTDC_L1PFCR + lofs, LXPFCR_PF, val); @@ -656,8 +633,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, /* Specifies the constant alpha value */ val = CONSTA_MAX; - reg_update_bits(ldev->regs, LTDC_L1CACR + lofs, - LXCACR_CONSTA, val); + reg_update_bits(ldev->regs, LTDC_L1CACR + lofs, LXCACR_CONSTA, val); /* Specifies the blending factors */ val = BF1_PAXCA | BF2_1PAXCA; @@ -666,8 +642,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, /* Configures the frame buffer line number */ val = y1 - y0 + 1; - reg_update_bits(ldev->regs, LTDC_L1CFBLNR + lofs, - LXCFBLNR_CFBLN, val); + reg_update_bits(ldev->regs, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, val); /* Sets the FB address */ paddr = (u32)drm_fb_cma_get_gem_addr(fb, state, 0); @@ -706,11 +681,10 @@ static void ltdc_plane_atomic_disable(struct drm_plane *plane, oldstate->crtc->base.id, plane->base.id); } -static struct drm_plane_funcs ltdc_plane_funcs = { +static const struct drm_plane_funcs ltdc_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, - .set_property = drm_atomic_helper_plane_set_property, .reset = drm_atomic_helper_plane_reset, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, @@ -748,7 +722,7 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev, ret = drm_universal_plane_init(ddev, plane, possible_crtcs, <dc_plane_funcs, formats, nb_fmt, - type, NULL); + NULL, type, NULL); if (ret < 0) return 0; @@ -773,7 +747,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) struct ltdc_device *ldev = ddev->dev_private; struct drm_plane *primary, *overlay; unsigned int i; - int res; + int ret; primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY); if (!primary) { @@ -781,9 +755,9 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) return -EINVAL; } - res = drm_crtc_init_with_planes(ddev, crtc, primary, NULL, + ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL, <dc_crtc_funcs, NULL); - if (res) { + if (ret) { DRM_ERROR("Can not initialize CRTC\n"); goto cleanup; } @@ -796,7 +770,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) for (i = 1; i < ldev->caps.nb_layers; i++) { overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY); if (!overlay) { - res = -ENOMEM; + ret = -ENOMEM; DRM_ERROR("Can not create overlay plane %d\n", i); goto cleanup; } @@ -806,137 +780,42 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) cleanup: ltdc_plane_destroy_all(ddev); - return res; + return ret; } /* * DRM_ENCODER */ -static void ltdc_rgb_encoder_enable(struct drm_encoder *encoder) -{ - struct ltdc_device *ldev = encoder_to_ltdc(encoder); - - DRM_DEBUG_DRIVER("\n"); - - drm_panel_prepare(ldev->panel); - drm_panel_enable(ldev->panel); -} - -static void ltdc_rgb_encoder_disable(struct drm_encoder *encoder) -{ - struct ltdc_device *ldev = encoder_to_ltdc(encoder); - - DRM_DEBUG_DRIVER("\n"); - - drm_panel_disable(ldev->panel); - drm_panel_unprepare(ldev->panel); -} - -static const struct drm_encoder_helper_funcs ltdc_rgb_encoder_helper_funcs = { - .enable = ltdc_rgb_encoder_enable, - .disable = ltdc_rgb_encoder_disable, -}; - -static const struct drm_encoder_funcs ltdc_rgb_encoder_funcs = { +static const struct drm_encoder_funcs ltdc_encoder_funcs = { .destroy = drm_encoder_cleanup, }; -static struct drm_encoder *ltdc_rgb_encoder_create(struct drm_device *ddev) +static int ltdc_encoder_init(struct drm_device *ddev) { + struct ltdc_device *ldev = ddev->dev_private; struct drm_encoder *encoder; + int ret; encoder = devm_kzalloc(ddev->dev, sizeof(*encoder), GFP_KERNEL); if (!encoder) - return NULL; + return -ENOMEM; encoder->possible_crtcs = CRTC_MASK; - encoder->possible_clones = 0; /* No cloning support */ + encoder->possible_clones = 0; /* No cloning support */ - drm_encoder_init(ddev, encoder, <dc_rgb_encoder_funcs, + drm_encoder_init(ddev, encoder, <dc_encoder_funcs, DRM_MODE_ENCODER_DPI, NULL); - drm_encoder_helper_add(encoder, <dc_rgb_encoder_helper_funcs); - - DRM_DEBUG_DRIVER("RGB encoder:%d created\n", encoder->base.id); - - return encoder; -} - -/* - * DRM_CONNECTOR - */ - -static int ltdc_rgb_connector_get_modes(struct drm_connector *connector) -{ - struct drm_device *ddev = connector->dev; - struct ltdc_device *ldev = ddev->dev_private; - int ret = 0; - - DRM_DEBUG_DRIVER("\n"); - - if (ldev->panel) - ret = drm_panel_get_modes(ldev->panel); - - return ret < 0 ? 0 : ret; -} - -static struct drm_connector_helper_funcs ltdc_rgb_connector_helper_funcs = { - .get_modes = ltdc_rgb_connector_get_modes, -}; - -static enum drm_connector_status -ltdc_rgb_connector_detect(struct drm_connector *connector, bool force) -{ - struct ltdc_device *ldev = connector_to_ltdc(connector); - - return ldev->panel ? connector_status_connected : - connector_status_disconnected; -} - -static void ltdc_rgb_connector_destroy(struct drm_connector *connector) -{ - DRM_DEBUG_DRIVER("\n"); - - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - -static const struct drm_connector_funcs ltdc_rgb_connector_funcs = { - .dpms = drm_atomic_helper_connector_dpms, - .fill_modes = drm_helper_probe_single_connector_modes, - .detect = ltdc_rgb_connector_detect, - .destroy = ltdc_rgb_connector_destroy, - .reset = drm_atomic_helper_connector_reset, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; - -struct drm_connector *ltdc_rgb_connector_create(struct drm_device *ddev) -{ - struct drm_connector *connector; - int err; - - connector = devm_kzalloc(ddev->dev, sizeof(*connector), GFP_KERNEL); - if (!connector) { - DRM_ERROR("Failed to allocate connector\n"); - return NULL; - } - - connector->polled = DRM_CONNECTOR_POLL_HPD; - - err = drm_connector_init(ddev, connector, <dc_rgb_connector_funcs, - DRM_MODE_CONNECTOR_DPI); - if (err) { - DRM_ERROR("Failed to initialize connector\n"); - return NULL; + ret = drm_bridge_attach(encoder, ldev->bridge, NULL); + if (ret) { + drm_encoder_cleanup(encoder); + return -EINVAL; } - drm_connector_helper_add(connector, <dc_rgb_connector_helper_funcs); - - DRM_DEBUG_DRIVER("RGB connector:%d created\n", connector->base.id); + DRM_DEBUG_DRIVER("Bridge encoder:%d created\n", encoder->base.id); - return connector; + return 0; } static int ltdc_get_caps(struct drm_device *ddev) @@ -972,61 +851,26 @@ static int ltdc_get_caps(struct drm_device *ddev) return 0; } -static struct drm_panel *ltdc_get_panel(struct drm_device *ddev) -{ - struct device *dev = ddev->dev; - struct device_node *np = dev->of_node; - struct device_node *entity, *port = NULL; - struct drm_panel *panel = NULL; - - DRM_DEBUG_DRIVER("\n"); - - /* - * Parse ltdc node to get remote port and find RGB panel / HDMI slave - * If a dsi or a bridge (hdmi, lvds...) is connected to ltdc, - * a remote port & RGB panel will not be found. - */ - for_each_endpoint_of_node(np, entity) { - if (!of_device_is_available(entity)) - continue; - - port = of_graph_get_remote_port_parent(entity); - if (port) { - panel = of_drm_find_panel(port); - of_node_put(port); - if (panel) { - DRM_DEBUG_DRIVER("remote panel %s\n", - port->full_name); - } else { - DRM_DEBUG_DRIVER("panel missing\n"); - of_node_put(entity); - } - } - } - - return panel; -} - int ltdc_load(struct drm_device *ddev) { struct platform_device *pdev = to_platform_device(ddev->dev); struct ltdc_device *ldev = ddev->dev_private; struct device *dev = ddev->dev; struct device_node *np = dev->of_node; - struct drm_encoder *encoder; - struct drm_connector *connector = NULL; + struct drm_bridge *bridge; + struct drm_panel *panel; struct drm_crtc *crtc; struct reset_control *rstc; - struct resource res; + struct resource *res; int irq, ret, i; DRM_DEBUG_DRIVER("\n"); - ldev->panel = ltdc_get_panel(ddev); - if (!ldev->panel) - return -EPROBE_DEFER; + ret = drm_of_find_panel_or_bridge(np, 0, 0, &panel, &bridge); + if (ret) + return ret; - rstc = of_reset_control_get(np, NULL); + rstc = devm_reset_control_get_exclusive(dev, NULL); mutex_init(&ldev->err_lock); @@ -1041,15 +885,18 @@ int ltdc_load(struct drm_device *ddev) return -ENODEV; } - if (of_address_to_resource(np, 0, &res)) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { DRM_ERROR("Unable to get resource\n"); - return -ENODEV; + ret = -ENODEV; + goto err; } - ldev->regs = devm_ioremap_resource(dev, &res); + ldev->regs = devm_ioremap_resource(dev, res); if (IS_ERR(ldev->regs)) { DRM_ERROR("Unable to get ltdc registers\n"); - return PTR_ERR(ldev->regs); + ret = PTR_ERR(ldev->regs); + goto err; } for (i = 0; i < MAX_IRQ; i++) { @@ -1062,7 +909,7 @@ int ltdc_load(struct drm_device *ddev) dev_name(dev), ddev); if (ret) { DRM_ERROR("Failed to register LTDC interrupt\n"); - return ret; + goto err; } } @@ -1077,33 +924,27 @@ int ltdc_load(struct drm_device *ddev) if (ret) { DRM_ERROR("hardware identifier (0x%08x) not supported!\n", ldev->caps.hw_version); - return ret; + goto err; } DRM_INFO("ltdc hw version 0x%08x - ready\n", ldev->caps.hw_version); - if (ldev->panel) { - encoder = ltdc_rgb_encoder_create(ddev); - if (!encoder) { - DRM_ERROR("Failed to create RGB encoder\n"); - ret = -EINVAL; + if (panel) { + bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(bridge)) { + DRM_ERROR("Failed to create panel-bridge\n"); + ret = PTR_ERR(bridge); goto err; } + ldev->is_panel_bridge = true; + } - connector = ltdc_rgb_connector_create(ddev); - if (!connector) { - DRM_ERROR("Failed to create RGB connector\n"); - ret = -EINVAL; - goto err; - } + ldev->bridge = bridge; - ret = drm_mode_connector_attach_encoder(connector, encoder); - if (ret) { - DRM_ERROR("Failed to attach connector to encoder\n"); - goto err; - } - - drm_panel_attach(ldev->panel, connector); + ret = ltdc_encoder_init(ddev); + if (ret) { + DRM_ERROR("Failed to init encoder\n"); + goto err; } crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL); @@ -1129,9 +970,10 @@ int ltdc_load(struct drm_device *ddev) ddev->irq_enabled = 1; return 0; + err: - if (ldev->panel) - drm_panel_detach(ldev->panel); + if (ldev->is_panel_bridge) + drm_panel_bridge_remove(bridge); clk_disable_unprepare(ldev->pixel_clk); @@ -1144,8 +986,8 @@ void ltdc_unload(struct drm_device *ddev) DRM_DEBUG_DRIVER("\n"); - if (ldev->panel) - drm_panel_detach(ldev->panel); + if (ldev->is_panel_bridge) + drm_panel_bridge_remove(ldev->bridge); clk_disable_unprepare(ldev->pixel_clk); } diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h index d7a9c736ac1e..bc6d6f6419a9 100644 --- a/drivers/gpu/drm/stm/ltdc.h +++ b/drivers/gpu/drm/stm/ltdc.h @@ -24,10 +24,10 @@ struct ltdc_device { struct drm_fbdev_cma *fbdev; void __iomem *regs; struct clk *pixel_clk; /* lcd pixel clock */ - struct drm_panel *panel; + struct drm_bridge *bridge; + bool is_panel_bridge; struct mutex err_lock; /* protecting error_status */ struct ltdc_caps caps; - u32 clut[256]; /* color look up table */ u32 error_status; u32 irq_status; }; |