diff --git a/arch/arm/mach-imx/cpu.c b/arch/arm/mach-imx/cpu.c index cc215b771ef..20c741283cd 100644 --- a/arch/arm/mach-imx/cpu.c +++ b/arch/arm/mach-imx/cpu.c @@ -285,10 +285,10 @@ u32 get_ahb_clk(void) void arch_preboot_os(void) { -#if defined(CONFIG_IMX_AHCI) struct udevice *dev; int rc; +#if defined(CONFIG_IMX_AHCI) rc = uclass_find_device(UCLASS_AHCI, 0, &dev); if (!rc && dev) { rc = device_remove(dev, DM_REMOVE_NORMAL); @@ -308,11 +308,17 @@ void arch_preboot_os(void) #endif #if defined(CONFIG_VIDEO_IPUV3) /* disable video before launching O/S */ - ipuv3_fb_shutdown(); + rc = uclass_find_first_device(UCLASS_VIDEO, &dev); + while (!rc && dev) { + ipuv3_fb_shutdown(dev); + uclass_find_next_device(&dev); + } #endif #if defined(CONFIG_VIDEO_MXS) && !defined(CONFIG_VIDEO) lcdif_power_down(); #endif + (void)dev; + (void)rc; } #ifndef CONFIG_IMX8M diff --git a/drivers/video/imx/ipu.h b/drivers/video/imx/ipu.h index f7d9d809529..62827dc480d 100644 --- a/drivers/video/imx/ipu.h +++ b/drivers/video/imx/ipu.h @@ -1,5 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* + * Code fixes: + * + * (C) Copyright 2025 + * Brian Ruley, GE HealthCare, brian.ruley@gehealthcare.com + * * Porting to u-boot: * * (C) Copyright 2010 @@ -19,9 +24,14 @@ #define IDMA_CHAN_INVALID 0xFF #define HIGH_RESOLUTION_WIDTH 1024 +struct ipu_ctx; +struct ipu_di_config; + struct clk { const char *name; int id; + /* The IPU context of this clock */ + struct ipu_ctx *ctx; /* Source clock this clk depends on */ struct clk *parent; /* Secondary clock to enable/disable with this clock */ @@ -65,6 +75,69 @@ struct clk { int (*set_parent)(struct clk *clk, struct clk *parent); }; +struct udevice; + +/* + * Per-IPU context used by ipu_common to manage clocks and channel state. + * Lifetime is owned by the IPU DM driver + */ +struct ipu_ctx { + struct udevice *dev; + int dev_id; + + struct clk *ipu_clk; + struct clk *ldb_clk; + unsigned char ipu_clk_enabled; + struct clk *di_clk[2]; + struct clk *pixel_clk[2]; + + u8 dc_di_assignment[10]; + u32 channel_init_mask; + u32 channel_enable_mask; + + int ipu_dc_use_count; + int ipu_dp_use_count; + int ipu_dmfc_use_count; + int ipu_di_use_count[2]; +}; + +/** + * @disp: The DI the panel is attached to. + * @pixel_clk_rate: Desired pixel clock frequency in Hz. + * @pixel_fmt: Input parameter for pixel format of buffer. + * Pixel format is a FOURCC ASCII code. + * @width: The width of panel in pixels. + * @height: The height of panel in pixels. + * @h_start_width: The number of pixel clocks between the HSYNC + * signal pulse and the start of valid data. + * @h_sync_width: The width of the HSYNC signal in units of pixel + * clocks. + * @h_end_width: The number of pixel clocks between the end of + * valid data and the HSYNC signal for next line. + * @v_start_width: The number of lines between the VSYNC + * signal pulse and the start of valid data. + * @v_sync_width: The width of the VSYNC signal in units of lines + * @v_end_width: The number of lines between the end of valid + * data and the VSYNC signal for next frame. + * @ctx: The IPU context of the display. + */ +struct ipu_di_config { + int disp; + u32 pixel_clk_rate; + u32 pixel_fmt; + u16 width; + u16 height; + u16 h_start_width; + u16 h_sync_width; + u16 h_end_width; + u16 v_start_width; + u16 v_sync_width; + u16 v_end_width; + u32 v_to_h_sync; + + struct ipu_ctx *ctx; +}; + /* * Enumeration of Synchronous (Memory-less) panel types */ @@ -201,8 +274,9 @@ typedef struct { typedef enum { RGB, YCBCR, YUV } ipu_color_space_t; /* Common IPU API */ -int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params); -void ipu_uninit_channel(ipu_channel_t channel); +int32_t ipu_init_channel(struct ipu_ctx *ctx, ipu_channel_t channel, + ipu_channel_params_t *params); +void ipu_uninit_channel(struct ipu_ctx *ctx, ipu_channel_t channel); int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, u32 pixel_fmt, u16 width, u16 height, @@ -212,14 +286,10 @@ int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, void ipu_clear_buffer_ready(ipu_channel_t channel, ipu_buffer_t type, u32 buf_num); -int32_t ipu_enable_channel(ipu_channel_t channel); -int32_t ipu_disable_channel(ipu_channel_t channel); +int32_t ipu_enable_channel(struct ipu_ctx *ctx, ipu_channel_t channel); +int32_t ipu_disable_channel(struct ipu_ctx *ctx, ipu_channel_t channel); -int32_t ipu_init_sync_panel(int disp, u32 pixel_clk, u16 width, u16 height, - u32 pixel_fmt, u16 h_start_width, u16 h_sync_width, - u16 h_end_width, u16 v_start_width, - u16 v_sync_width, u16 v_end_width, u32 v_to_h_sync, - ipu_di_signal_cfg_t sig); +int32_t ipu_init_sync_panel(struct ipu_di_config *di, ipu_di_signal_cfg_t sig); int32_t ipu_disp_set_global_alpha(ipu_channel_t channel, unsigned char enable, u8 alpha); @@ -238,17 +308,18 @@ int clk_get_usecount(struct clk *clk); struct clk *clk_get_parent(struct clk *clk); void ipu_dump_registers(void); -int ipu_probe(void); -bool ipu_clk_enabled(void); +struct ipu_ctx *ipu_probe(struct udevice *dev); +bool ipu_clk_enabled(struct ipu_ctx *ctx); void ipu_dmfc_init(int dmfc_type, int first); void ipu_init_dc_mappings(void); void ipu_dmfc_set_wait4eot(int dma_chan, int width); void ipu_dc_init(int dc_chan, int di, unsigned char interlaced); void ipu_dc_uninit(int dc_chan); -void ipu_dp_dc_enable(ipu_channel_t channel); +void ipu_dp_dc_enable(struct ipu_ctx *ctx, ipu_channel_t channel); int ipu_dp_init(ipu_channel_t channel, u32 in_pixel_fmt, u32 out_pixel_fmt); void ipu_dp_uninit(ipu_channel_t channel); -void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap); +void ipu_dp_dc_disable(struct ipu_ctx *ctx, ipu_channel_t channel, + unsigned char swap); ipu_color_space_t format_to_colorspace(u32 fmt); #endif diff --git a/drivers/video/imx/ipu_common.c b/drivers/video/imx/ipu_common.c index 560ee89f0f7..e9897ee79d2 100644 --- a/drivers/video/imx/ipu_common.c +++ b/drivers/video/imx/ipu_common.c @@ -1,5 +1,10 @@ // SPDX-License-Identifier: GPL-2.0+ /* + * Code fixes: + * + * (C) Copyright 2025 + * Brian Ruley, GE HealthCare, brian.ruley@gehealthcare.com + * * Porting to u-boot: * * (C) Copyright 2010 @@ -10,7 +15,6 @@ * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. */ -/* #define DEBUG */ #include "ipu.h" #include "ipu_regs.h" #include @@ -19,6 +23,8 @@ #include #include #include +#include +#include #include #include #include @@ -206,47 +212,77 @@ static void clk_ipu_disable(struct clk *clk) #endif } -static struct clk ipu_clk = { - .name = "ipu_clk", +/* + * Function to initialize the ipu clock + * + * @param ctx The ipu context for which the function is called + * + * Return: Returns 0 on success or negative error code on error + */ +static int ipu_clk_init(struct ipu_ctx *ctx) +{ + struct clk *ipu_clk; + + ipu_clk = devm_kzalloc(ctx->dev, sizeof(*ipu_clk), GFP_KERNEL); + if (!ipu_clk) + return -ENOMEM; + + ipu_clk->name = "ipu_clk"; + ipu_clk->ctx = ctx; #if CONFIG_IS_ENABLED(MX51) || CONFIG_IS_ENABLED(MX53) - .enable_reg = - (u32 *)(CCM_BASE_ADDR + offsetof(struct mxc_ccm_reg, CCGR5)), - .enable_shift = MXC_CCM_CCGR5_IPU_OFFSET, + ipu_clk->enable_reg = + (u32 *)(CCM_BASE_ADDR + offsetof(struct mxc_ccm_reg, CCGR5)); + ipu_clk->enable_shift = MXC_CCM_CCGR5_IPU_OFFSET; #else - .enable_reg = - (u32 *)(CCM_BASE_ADDR + offsetof(struct mxc_ccm_reg, CCGR3)), - .enable_shift = MXC_CCM_CCGR3_IPU1_IPU_DI0_OFFSET, + ipu_clk->enable_reg = + (u32 *)(CCM_BASE_ADDR + offsetof(struct mxc_ccm_reg, CCGR3)); + ipu_clk->enable_shift = MXC_CCM_CCGR3_IPU1_IPU_DI0_OFFSET; #endif - .enable = clk_ipu_enable, - .disable = clk_ipu_disable, - .usecount = 0, + + ipu_clk->enable = clk_ipu_enable; + ipu_clk->disable = clk_ipu_disable; + ipu_clk->usecount = 0; + +#if CONFIG_IS_ENABLED(MX51) + ipu_clk->rate = IPUV3_CLK_MX51; +#elif CONFIG_IS_ENABLED(MX53) + ipu_clk->rate = IPUV3_CLK_MX53; +#else + ipu_clk->rate = is_mx6sdl() ? IPUV3_CLK_MX6DL : IPUV3_CLK_MX6Q; +#endif + + ctx->ipu_clk = ipu_clk; + return 0; }; #if !defined CFG_SYS_LDB_CLOCK #define CFG_SYS_LDB_CLOCK 65000000 #endif -static struct clk ldb_clk = { - .name = "ldb_clk", - .rate = CFG_SYS_LDB_CLOCK, - .usecount = 0, +/* + * Function to initialize the ldb dummy clock + * + * @param ctx The ipu context for which the function is called + * + * Return: Returns 0 on success or negative error code on error + */ +static int ipu_ldb_clk_init(struct ipu_ctx *ctx) +{ + struct clk *ldb_clk; + + ldb_clk = devm_kzalloc(ctx->dev, sizeof(*ldb_clk), GFP_KERNEL); + if (!ldb_clk) + return -ENOMEM; + + ldb_clk->name = "ldb_clk"; + ldb_clk->ctx = ctx; + ldb_clk->rate = CFG_SYS_LDB_CLOCK; + ldb_clk->usecount = 0; + + ctx->ldb_clk = ldb_clk; + return 0; }; -/* Globals */ -struct clk *g_ipu_clk; -struct clk *g_ldb_clk; -unsigned char g_ipu_clk_enabled; -struct clk *g_di_clk[2]; -struct clk *g_pixel_clk[2]; -unsigned char g_dc_di_assignment[10]; -u32 g_channel_init_mask; -u32 g_channel_enable_mask; - -static int ipu_dc_use_count; -static int ipu_dp_use_count; -static int ipu_dmfc_use_count; -static int ipu_di_use_count[2]; - u32 *ipu_cpmem_base; u32 *ipu_dc_tmpl_reg; @@ -388,10 +424,11 @@ static void ipu_pixel_clk_disable(struct clk *clk) static int ipu_pixel_clk_set_parent(struct clk *clk, struct clk *parent) { u32 di_gen = __raw_readl(DI_GENERAL(clk->id)); + struct ipu_ctx *ctx = clk->ctx; - if (parent == g_ipu_clk) + if (parent == ctx->ipu_clk) di_gen &= ~DI_GEN_DI_CLK_EXT; - else if (!IS_ERR(g_di_clk[clk->id]) && parent == g_ldb_clk) + else if (!IS_ERR(ctx->di_clk[clk->id]) && parent == ctx->ldb_clk) di_gen |= DI_GEN_DI_CLK_EXT; else return -EINVAL; @@ -401,29 +438,34 @@ static int ipu_pixel_clk_set_parent(struct clk *clk, struct clk *parent) return 0; } -static struct clk pixel_clk[] = { - { - .name = "pixel_clk", - .id = 0, - .recalc = ipu_pixel_clk_recalc, - .set_rate = ipu_pixel_clk_set_rate, - .round_rate = ipu_pixel_clk_round_rate, - .set_parent = ipu_pixel_clk_set_parent, - .enable = ipu_pixel_clk_enable, - .disable = ipu_pixel_clk_disable, - .usecount = 0, - }, - { - .name = "pixel_clk", - .id = 1, - .recalc = ipu_pixel_clk_recalc, - .set_rate = ipu_pixel_clk_set_rate, - .round_rate = ipu_pixel_clk_round_rate, - .set_parent = ipu_pixel_clk_set_parent, - .enable = ipu_pixel_clk_enable, - .disable = ipu_pixel_clk_disable, - .usecount = 0, - }, +/* + * Function to initialize the pixel clock + * + * @param ctx The ipu context for which the function is called + * + * Return: Returns 0 on success or negative error code on error + */ +static int ipu_pixel_clk_init(struct ipu_ctx *ctx, int id) +{ + struct clk *pixel_clk; + + pixel_clk = devm_kzalloc(ctx->dev, sizeof(*pixel_clk), GFP_KERNEL); + if (!pixel_clk) + return -ENOMEM; + + pixel_clk->name = "pixel_clk"; + pixel_clk->id = id; + pixel_clk->ctx = ctx; + pixel_clk->recalc = ipu_pixel_clk_recalc; + pixel_clk->set_rate = ipu_pixel_clk_set_rate; + pixel_clk->round_rate = ipu_pixel_clk_round_rate; + pixel_clk->set_parent = ipu_pixel_clk_set_parent; + pixel_clk->enable = ipu_pixel_clk_enable; + pixel_clk->disable = ipu_pixel_clk_disable; + pixel_clk->usecount = 0; + + ctx->pixel_clk[id] = pixel_clk; + return 0; }; /* @@ -456,11 +498,24 @@ static void ipu_reset(void) * @param dev The device structure for the IPU passed in by the * driver framework. * - * Return: Returns 0 on success or negative error code on error + * Return: Returns pointer to IPU context on success or pointer error code + * on error */ -int ipu_probe(void) +struct ipu_ctx *ipu_probe(struct udevice *dev) { unsigned long ipu_base; + struct ipu_ctx *ctx; + int ret = 0; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + ret = -ENOMEM; + goto err; + } + + ctx->dev = dev; + ctx->dev_id = dev_seq(dev); + #if defined CONFIG_MX51 u32 temp; @@ -481,29 +536,33 @@ int ipu_probe(void) ipu_cpmem_base = (u32 *)(ipu_base + IPU_CPMEM_REG_BASE); ipu_dc_tmpl_reg = (u32 *)(ipu_base + IPU_DC_TMPL_REG_BASE); - g_pixel_clk[0] = &pixel_clk[0]; - g_pixel_clk[1] = &pixel_clk[1]; + ret = ipu_pixel_clk_init(ctx, 0); + if (ret) + goto err; - g_ipu_clk = &ipu_clk; -#if CONFIG_IS_ENABLED(MX51) - g_ipu_clk->rate = IPUV3_CLK_MX51; -#elif CONFIG_IS_ENABLED(MX53) - g_ipu_clk->rate = IPUV3_CLK_MX53; -#else - g_ipu_clk->rate = is_mx6sdl() ? IPUV3_CLK_MX6DL : IPUV3_CLK_MX6Q; -#endif + ret = ipu_pixel_clk_init(ctx, 1); + if (ret) + goto err; - debug("ipu_clk = %u\n", clk_get_rate(g_ipu_clk)); - g_ldb_clk = &ldb_clk; - debug("ldb_clk = %u\n", clk_get_rate(g_ldb_clk)); + ret = ipu_clk_init(ctx); + if (ret) + goto err; + + debug("ipu_clk = %u\n", clk_get_rate(ctx->ipu_clk)); + + ret = ipu_ldb_clk_init(ctx); + if (ret) + goto err; + + debug("ldb_clk = %u\n", clk_get_rate(ctx->ldb_clk)); ipu_reset(); - clk_set_parent(g_pixel_clk[0], g_ipu_clk); - clk_set_parent(g_pixel_clk[1], g_ipu_clk); - clk_enable(g_ipu_clk); + clk_set_parent(ctx->pixel_clk[0], ctx->ipu_clk); + clk_set_parent(ctx->pixel_clk[1], ctx->ipu_clk); + clk_enable(ctx->ipu_clk); - g_di_clk[0] = NULL; - g_di_clk[1] = NULL; + ctx->di_clk[0] = NULL; + ctx->di_clk[1] = NULL; __raw_writel(0x807FFFFF, IPU_MEM_RST); while (__raw_readl(IPU_MEM_RST) & 0x80000000) @@ -525,9 +584,11 @@ int ipu_probe(void) /* Set MCU_T to divide MCU access window into 2 */ __raw_writel(0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN); - clk_disable(g_ipu_clk); + clk_disable(ctx->ipu_clk); - return 0; + return ctx; +err: + return ERR_PTR(ret); } void ipu_dump_registers(void) @@ -556,26 +617,32 @@ void ipu_dump_registers(void) /* * This function is called to initialize a logical IPU channel. * - * @param channel Input parameter for the logical channel ID to init. + * @param ctx The ipu context for which the function is called * - * @param params Input parameter containing union of channel + * @param channel Input parameter for the logical channel ID to init. + * + * @param params Input parameter containing union of channel * initialization parameters. * - * Return: Returns 0 on success or negative error code on fail + * Return: Returns 0 on success or negative error code on fail */ -int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params) +int32_t ipu_init_channel(struct ipu_ctx *ctx, ipu_channel_t channel, + ipu_channel_params_t *params) { + struct clk *ipu_clk = ctx->ipu_clk; + u8 *dc_di_assignment = ctx->dc_di_assignment; + u32 *channel_init_mask = &ctx->channel_init_mask; int ret = 0; u32 ipu_conf; debug("init channel = %d\n", IPU_CHAN_ID(channel)); - if (g_ipu_clk_enabled == 0) { - g_ipu_clk_enabled = 1; - clk_enable(g_ipu_clk); + if (ctx->ipu_clk_enabled == 0) { + ctx->ipu_clk_enabled = 1; + clk_enable(ipu_clk); } - if (g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) { + if (*channel_init_mask & (1L << IPU_CHAN_ID(channel))) { printf("Warning: channel already initialized %d\n", IPU_CHAN_ID(channel)); } @@ -589,12 +656,12 @@ int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params) goto err; } - g_dc_di_assignment[1] = params->mem_dc_sync.di; + dc_di_assignment[1] = params->mem_dc_sync.di; ipu_dc_init(1, params->mem_dc_sync.di, params->mem_dc_sync.interlaced); - ipu_di_use_count[params->mem_dc_sync.di]++; - ipu_dc_use_count++; - ipu_dmfc_use_count++; + ctx->ipu_di_use_count[params->mem_dc_sync.di]++; + ctx->ipu_dc_use_count++; + ctx->ipu_dmfc_use_count++; break; case MEM_BG_SYNC: if (params->mem_dp_bg_sync.di > 1) { @@ -602,23 +669,23 @@ int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params) goto err; } - g_dc_di_assignment[5] = params->mem_dp_bg_sync.di; + dc_di_assignment[5] = params->mem_dp_bg_sync.di; ipu_dp_init(channel, params->mem_dp_bg_sync.in_pixel_fmt, params->mem_dp_bg_sync.out_pixel_fmt); ipu_dc_init(5, params->mem_dp_bg_sync.di, params->mem_dp_bg_sync.interlaced); - ipu_di_use_count[params->mem_dp_bg_sync.di]++; - ipu_dc_use_count++; - ipu_dp_use_count++; - ipu_dmfc_use_count++; + ctx->ipu_di_use_count[params->mem_dp_bg_sync.di]++; + ctx->ipu_dc_use_count++; + ctx->ipu_dp_use_count++; + ctx->ipu_dmfc_use_count++; break; case MEM_FG_SYNC: ipu_dp_init(channel, params->mem_dp_fg_sync.in_pixel_fmt, params->mem_dp_fg_sync.out_pixel_fmt); - ipu_dc_use_count++; - ipu_dp_use_count++; - ipu_dmfc_use_count++; + ctx->ipu_dc_use_count++; + ctx->ipu_dp_use_count++; + ctx->ipu_dmfc_use_count++; break; default: printf("Missing channel initialization\n"); @@ -626,16 +693,16 @@ int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params) } /* Enable IPU sub module */ - g_channel_init_mask |= 1L << IPU_CHAN_ID(channel); - if (ipu_dc_use_count == 1) + *channel_init_mask |= 1L << IPU_CHAN_ID(channel); + if (ctx->ipu_dc_use_count == 1) ipu_conf |= IPU_CONF_DC_EN; - if (ipu_dp_use_count == 1) + if (ctx->ipu_dp_use_count == 1) ipu_conf |= IPU_CONF_DP_EN; - if (ipu_dmfc_use_count == 1) + if (ctx->ipu_dmfc_use_count == 1) ipu_conf |= IPU_CONF_DMFC_EN; - if (ipu_di_use_count[0] == 1) + if (ctx->ipu_di_use_count[0] == 1) ipu_conf |= IPU_CONF_DI0_EN; - if (ipu_di_use_count[1] == 1) + if (ctx->ipu_di_use_count[1] == 1) ipu_conf |= IPU_CONF_DI1_EN; __raw_writel(ipu_conf, IPU_CONF); @@ -647,15 +714,19 @@ err: /* * This function is called to uninitialize a logical IPU channel. * + * @param ctx The ipu context for which the function is called + * * @param channel Input parameter for the logical channel ID to uninit. */ -void ipu_uninit_channel(ipu_channel_t channel) +void ipu_uninit_channel(struct ipu_ctx *ctx, ipu_channel_t channel) { + u8 *dc_di_assignment = ctx->dc_di_assignment; + u32 *channel_init_mask = &ctx->channel_init_mask; u32 reg; u32 in_dma, out_dma = 0; u32 ipu_conf; - if ((g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) { + if ((*channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) { debug("Channel already uninitialized %d\n", IPU_CHAN_ID(channel)); return; @@ -686,46 +757,46 @@ void ipu_uninit_channel(ipu_channel_t channel) switch (channel) { case MEM_DC_SYNC: ipu_dc_uninit(1); - ipu_di_use_count[g_dc_di_assignment[1]]--; - ipu_dc_use_count--; - ipu_dmfc_use_count--; + ctx->ipu_di_use_count[dc_di_assignment[1]]--; + ctx->ipu_dc_use_count--; + ctx->ipu_dmfc_use_count--; break; case MEM_BG_SYNC: ipu_dp_uninit(channel); ipu_dc_uninit(5); - ipu_di_use_count[g_dc_di_assignment[5]]--; - ipu_dc_use_count--; - ipu_dp_use_count--; - ipu_dmfc_use_count--; + ctx->ipu_di_use_count[dc_di_assignment[5]]--; + ctx->ipu_dc_use_count--; + ctx->ipu_dp_use_count--; + ctx->ipu_dmfc_use_count--; break; case MEM_FG_SYNC: ipu_dp_uninit(channel); - ipu_dc_use_count--; - ipu_dp_use_count--; - ipu_dmfc_use_count--; + ctx->ipu_dc_use_count--; + ctx->ipu_dp_use_count--; + ctx->ipu_dmfc_use_count--; break; default: break; } - g_channel_init_mask &= ~(1L << IPU_CHAN_ID(channel)); + *channel_init_mask &= ~(1L << IPU_CHAN_ID(channel)); - if (ipu_dc_use_count == 0) + if (ctx->ipu_dc_use_count == 0) ipu_conf &= ~IPU_CONF_DC_EN; - if (ipu_dp_use_count == 0) + if (ctx->ipu_dp_use_count == 0) ipu_conf &= ~IPU_CONF_DP_EN; - if (ipu_dmfc_use_count == 0) + if (ctx->ipu_dmfc_use_count == 0) ipu_conf &= ~IPU_CONF_DMFC_EN; - if (ipu_di_use_count[0] == 0) + if (ctx->ipu_di_use_count[0] == 0) ipu_conf &= ~IPU_CONF_DI0_EN; - if (ipu_di_use_count[1] == 0) + if (ctx->ipu_di_use_count[1] == 0) ipu_conf &= ~IPU_CONF_DI1_EN; __raw_writel(ipu_conf, IPU_CONF); if (ipu_conf == 0) { - clk_disable(g_ipu_clk); - g_ipu_clk_enabled = 0; + clk_disable(ctx->ipu_clk); + ctx->ipu_clk_enabled = 0; } } @@ -1031,18 +1102,21 @@ int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, /* * This function enables a logical channel. * - * @param channel Input parameter for the logical channel ID. + * @param ctx The ipu context for which the function is called * - * Return: This function returns 0 on success or negative error code on - * fail. + * @param channel Input parameter for the logical channel ID. + * + * Return: This function returns 0 on success or negative error code on + * fail. */ -int32_t ipu_enable_channel(ipu_channel_t channel) +int32_t ipu_enable_channel(struct ipu_ctx *ctx, ipu_channel_t channel) { + u32 *channel_enable_mask = &ctx->channel_enable_mask; u32 reg; u32 in_dma; u32 out_dma; - if (g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) { + if (*channel_enable_mask & (1L << IPU_CHAN_ID(channel))) { printf("Warning: channel already enabled %d\n", IPU_CHAN_ID(channel)); } @@ -1062,9 +1136,9 @@ int32_t ipu_enable_channel(ipu_channel_t channel) if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC)) - ipu_dp_dc_enable(channel); + ipu_dp_dc_enable(ctx, channel); - g_channel_enable_mask |= 1L << IPU_CHAN_ID(channel); + *channel_enable_mask |= 1L << IPU_CHAN_ID(channel); return 0; } @@ -1103,21 +1177,24 @@ void ipu_clear_buffer_ready(ipu_channel_t channel, ipu_buffer_t type, /* * This function disables a logical channel. * - * @param channel Input parameter for the logical channel ID. + * @param ctx The ipu context for which the function is called * - * @param wait_for_stop Flag to set whether to wait for channel end - * of frame or return immediately. + * @param channel Input parameter for the logical channel ID. * - * Return: This function returns 0 on success or negative error code on - * fail. + * @param wait_for_stop Flag to set whether to wait for channel end + * of frame or return immediately. + * + * Return: This function returns 0 on success or negative error code on + * fail. */ -int32_t ipu_disable_channel(ipu_channel_t channel) +int32_t ipu_disable_channel(struct ipu_ctx *ctx, ipu_channel_t channel) { + u32 *channel_enable_mask = &ctx->channel_enable_mask; u32 reg; u32 in_dma; u32 out_dma; - if ((g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) { + if ((*channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) { debug("Channel already disabled %d\n", IPU_CHAN_ID(channel)); return 0; } @@ -1132,7 +1209,7 @@ int32_t ipu_disable_channel(ipu_channel_t channel) if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) || (channel == MEM_DC_SYNC)) { - ipu_dp_dc_disable(channel, 0); + ipu_dp_dc_disable(ctx, channel, 0); } /* Disable DMA channel(s) */ @@ -1147,7 +1224,7 @@ int32_t ipu_disable_channel(ipu_channel_t channel) __raw_writel(idma_mask(out_dma), IPU_CHA_CUR_BUF(out_dma)); } - g_channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel)); + *channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel)); /* Set channel buffers NOT to be ready */ if (idma_is_valid(in_dma)) { @@ -1219,7 +1296,7 @@ ipu_color_space_t format_to_colorspace(u32 fmt) return RGB; } -bool ipu_clk_enabled(void) +bool ipu_clk_enabled(struct ipu_ctx *ctx) { - return g_ipu_clk_enabled; + return ctx->ipu_clk_enabled; } diff --git a/drivers/video/imx/ipu_disp.c b/drivers/video/imx/ipu_disp.c index 89dead372cd..6a337b13af6 100644 --- a/drivers/video/imx/ipu_disp.c +++ b/drivers/video/imx/ipu_disp.c @@ -1,5 +1,10 @@ // SPDX-License-Identifier: GPL-2.0+ /* + * Code fixes: + * + * (C) Copyright 2025 + * Brian Ruley, GE HealthCare, brian.ruley@gehealthcare.com + * * Porting to u-boot: * * (C) Copyright 2010 @@ -10,8 +15,6 @@ * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. */ -/* #define DEBUG */ - #include "ipu.h" #include "ipu_regs.h" #include @@ -40,14 +43,6 @@ int dmfc_type_setup; static int dmfc_size_28, dmfc_size_29, dmfc_size_24, dmfc_size_27, dmfc_size_23; int g_di1_tvout; -extern struct clk *g_ipu_clk; -extern struct clk *g_ldb_clk; -extern struct clk *g_di_clk[2]; -extern struct clk *g_pixel_clk[2]; - -extern unsigned char g_ipu_clk_enabled; -extern unsigned char g_dc_di_assignment[]; - void ipu_dmfc_init(int dmfc_type, int first) { u32 dmfc_wr_chan, dmfc_dp_chan; @@ -579,7 +574,7 @@ void ipu_dc_uninit(int dc_chan) } } -void ipu_dp_dc_enable(ipu_channel_t channel) +void ipu_dp_dc_enable(struct ipu_ctx *ctx, ipu_channel_t channel) { int di; u32 reg; @@ -602,7 +597,7 @@ void ipu_dp_dc_enable(ipu_channel_t channel) return; } - di = g_dc_di_assignment[dc_chan]; + di = ctx->dc_di_assignment[dc_chan]; /* Make sure other DC sync channel is not assigned same DI */ reg = __raw_readl(DC_WR_CH_CONF(6 - dc_chan)); @@ -616,12 +611,13 @@ void ipu_dp_dc_enable(ipu_channel_t channel) reg |= 4 << DC_WR_CH_CONF_PROG_TYPE_OFFSET; __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); - clk_enable(g_pixel_clk[di]); + clk_enable(ctx->pixel_clk[di]); } static unsigned char dc_swap; -void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap) +void ipu_dp_dc_disable(struct ipu_ctx *ctx, ipu_channel_t channel, + unsigned char swap) { u32 reg; u32 csc; @@ -658,7 +654,7 @@ void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap) * Wait for DC triple buffer to empty, * this check is useful for tv overlay. */ - if (g_dc_di_assignment[dc_chan] == 0) + if (ctx->dc_di_assignment[dc_chan] == 0) while ((__raw_readl(DC_STAT) & 0x00000002) != 0x00000002) { udelay(2000); @@ -666,7 +662,7 @@ void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap) if (timeout <= 0) break; } - else if (g_dc_di_assignment[dc_chan] == 1) + else if (ctx->dc_di_assignment[dc_chan] == 1) while ((__raw_readl(DC_STAT) & 0x00000020) != 0x00000020) { udelay(2000); @@ -698,7 +694,7 @@ void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap) __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); reg = __raw_readl(IPU_DISP_GEN); - if (g_dc_di_assignment[dc_chan]) + if (ctx->dc_di_assignment[dc_chan]) reg &= ~DI1_COUNTER_RELEASE; else reg &= ~DI0_COUNTER_RELEASE; @@ -706,7 +702,7 @@ void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap) /* Clock is already off because it must be done quickly, but we need to fix the ref count */ - clk_disable(g_pixel_clk[g_dc_di_assignment[dc_chan]]); + clk_disable(ctx->pixel_clk[ctx->dc_di_assignment[dc_chan]]); } } @@ -765,46 +761,18 @@ static int ipu_pixfmt_to_map(u32 fmt) /* * This function is called to initialize a synchronous LCD panel. * - * @param disp The DI the panel is attached to. + * @param di Pointer to display data. * - * @param pixel_clk Desired pixel clock frequency in Hz. - * - * @param pixel_fmt Input parameter for pixel format of buffer. - * Pixel format is a FOURCC ASCII code. - * - * @param width The width of panel in pixels. - * - * @param height The height of panel in pixels. - * - * @param hStartWidth The number of pixel clocks between the HSYNC - * signal pulse and the start of valid data. - * - * @param hSyncWidth The width of the HSYNC signal in units of pixel - * clocks. - * - * @param hEndWidth The number of pixel clocks between the end of - * valid data and the HSYNC signal for next line. - * - * @param vStartWidth The number of lines between the VSYNC - * signal pulse and the start of valid data. - * - * @param vSyncWidth The width of the VSYNC signal in units of lines - * - * @param vEndWidth The number of lines between the end of valid - * data and the VSYNC signal for next frame. - * - * @param sig Bitfield of signal polarities for LCD interface. + * @param sig Bitfield of signal polarities for LCD interface. * * Return: This function returns 0 on success or negative error code on * fail. */ -int32_t ipu_init_sync_panel(int disp, u32 pixel_clk, u16 width, u16 height, - u32 pixel_fmt, u16 h_start_width, u16 h_sync_width, - u16 h_end_width, u16 v_start_width, - u16 v_sync_width, u16 v_end_width, u32 v_to_h_sync, - ipu_di_signal_cfg_t sig) +int32_t ipu_init_sync_panel(struct ipu_di_config *di, ipu_di_signal_cfg_t sig) { + struct ipu_ctx *ctx = di->ctx; + int disp = di->disp; u32 reg; u32 di_gen, vsync_cnt; u32 div, rounded_pixel_clk; @@ -812,22 +780,24 @@ int32_t ipu_init_sync_panel(int disp, u32 pixel_clk, u16 width, u16 height, int map; struct clk *di_parent; - debug("panel size = %d x %d\n", width, height); + debug("panel size = %d x %d\n", di->width, di->height); - if ((v_sync_width == 0) || (h_sync_width == 0)) + if ((di->v_sync_width == 0) || (di->h_sync_width == 0)) return -EINVAL; /* adapt panel to ipu restricitions */ - if (v_end_width < 2) { - v_end_width = 2; + if (di->v_end_width < 2) { + di->v_end_width = 2; puts("WARNING: v_end_width (lower_margin) must be >= 2, adjusted\n"); } - h_total = width + h_sync_width + h_start_width + h_end_width; - v_total = height + v_sync_width + v_start_width + v_end_width; + h_total = di->width + di->h_sync_width + di->h_start_width + + di->h_end_width; + v_total = di->height + di->v_sync_width + di->v_start_width + + di->v_end_width; /* Init clocking */ - debug("pixel clk = %dHz\n", pixel_clk); + debug("pixel clk = %dHz\n", di->pixel_clk_rate); if (sig.ext_clk) { if (!(g_di1_tvout && (disp == 1))) { /*not round div for tvout*/ @@ -835,11 +805,12 @@ int32_t ipu_init_sync_panel(int disp, u32 pixel_clk, u16 width, u16 height, * Set the PLL to be an even multiple * of the pixel clock. */ - if ((clk_get_usecount(g_pixel_clk[0]) == 0) && - (clk_get_usecount(g_pixel_clk[1]) == 0)) { - di_parent = clk_get_parent(g_di_clk[disp]); - rounded_pixel_clk = clk_round_rate( - g_pixel_clk[disp], pixel_clk); + if ((clk_get_usecount(ctx->pixel_clk[0]) == 0) && + (clk_get_usecount(ctx->pixel_clk[1]) == 0)) { + di_parent = clk_get_parent(ctx->di_clk[disp]); + rounded_pixel_clk = + clk_round_rate(ctx->pixel_clk[disp], + di->pixel_clk_rate); div = clk_get_rate(di_parent) / rounded_pixel_clk; if (div % 2) @@ -849,27 +820,28 @@ int32_t ipu_init_sync_panel(int disp, u32 pixel_clk, u16 width, u16 height, clk_set_rate(di_parent, div * rounded_pixel_clk); udelay(10000); - clk_set_rate(g_di_clk[disp], + clk_set_rate(ctx->di_clk[disp], 2 * rounded_pixel_clk); udelay(10000); } } - clk_set_parent(g_pixel_clk[disp], g_ldb_clk); + clk_set_parent(ctx->pixel_clk[disp], ctx->ldb_clk); } else { - if (clk_get_usecount(g_pixel_clk[disp]) != 0) - clk_set_parent(g_pixel_clk[disp], g_ipu_clk); + if (clk_get_usecount(ctx->pixel_clk[disp]) != 0) + clk_set_parent(ctx->pixel_clk[disp], ctx->ipu_clk); } - rounded_pixel_clk = clk_round_rate(g_pixel_clk[disp], pixel_clk); - clk_set_rate(g_pixel_clk[disp], rounded_pixel_clk); + rounded_pixel_clk = + clk_round_rate(ctx->pixel_clk[disp], di->pixel_clk_rate); + clk_set_rate(ctx->pixel_clk[disp], rounded_pixel_clk); udelay(5000); /* Get integer portion of divider */ - div = clk_get_rate(clk_get_parent(g_pixel_clk[disp])) / + div = clk_get_rate(clk_get_parent(ctx->pixel_clk[disp])) / rounded_pixel_clk; ipu_di_data_wave_config(disp, SYNC_WAVE, div - 1, div - 1); ipu_di_data_pin_config(disp, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); - map = ipu_pixfmt_to_map(pixel_fmt); + map = ipu_pixfmt_to_map(di->pixel_fmt); if (map < 0) { debug("IPU_DISP: No MAP\n"); return -EINVAL; @@ -931,7 +903,7 @@ int32_t ipu_init_sync_panel(int disp, u32 pixel_clk, u16 width, u16 height, 4, /* counter */ v_total / 2 - 1, /* run count */ DI_SYNC_HSYNC, /* run_resolution */ - v_start_width, /* offset */ + di->v_start_width, /* offset */ DI_SYNC_HSYNC, /* offset resolution */ 2, /* repeat count */ DI_SYNC_VSYNC, /* CNT_CLR_SEL */ @@ -949,7 +921,7 @@ int32_t ipu_init_sync_panel(int disp, u32 pixel_clk, u16 width, u16 height, DI_SYNC_HSYNC, /* run_resolution */ 0, /* offset */ DI_SYNC_NONE, /* offset resolution */ - height / 2, /* repeat count */ + di->height / 2, /* repeat count */ 4, /* CNT_CLR_SEL */ 0, /* CNT_POLARITY_GEN_EN */ DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ @@ -996,9 +968,9 @@ int32_t ipu_init_sync_panel(int disp, u32 pixel_clk, u16 width, u16 height, 8, /* counter */ 0, /* run count */ DI_SYNC_CLK, /* run_resolution */ - h_start_width, /* offset */ + di->h_start_width, /* offset */ DI_SYNC_CLK, /* offset resolution */ - width, /* repeat count */ + di->width, /* repeat count */ 5, /* CNT_CLR_SEL */ 0, /* CNT_POLARITY_GEN_EN */ DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ @@ -1042,26 +1014,27 @@ int32_t ipu_init_sync_panel(int disp, u32 pixel_clk, u16 width, u16 height, /* Setup external (delayed) HSYNC waveform */ ipu_di_sync_config(disp, DI_SYNC_HSYNC, h_total - 1, - DI_SYNC_CLK, div * v_to_h_sync, DI_SYNC_CLK, - 0, DI_SYNC_NONE, 1, DI_SYNC_NONE, - DI_SYNC_CLK, 0, h_sync_width * 2); + DI_SYNC_CLK, div * di->v_to_h_sync, + DI_SYNC_CLK, 0, DI_SYNC_NONE, 1, + DI_SYNC_NONE, DI_SYNC_CLK, 0, + di->h_sync_width * 2); /* Setup VSYNC waveform */ vsync_cnt = DI_SYNC_VSYNC; ipu_di_sync_config(disp, DI_SYNC_VSYNC, v_total - 1, DI_SYNC_INT_HSYNC, 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, 1, DI_SYNC_NONE, - DI_SYNC_INT_HSYNC, 0, v_sync_width * 2); + DI_SYNC_INT_HSYNC, 0, di->v_sync_width * 2); __raw_writel(v_total - 1, DI_SCR_CONF(disp)); /* Setup active data waveform to sync with DC */ ipu_di_sync_config(disp, 4, 0, DI_SYNC_HSYNC, - v_sync_width + v_start_width, DI_SYNC_HSYNC, - height, DI_SYNC_VSYNC, 0, DI_SYNC_NONE, - DI_SYNC_NONE, 0, 0); + di->v_sync_width + di->v_start_width, + DI_SYNC_HSYNC, di->height, DI_SYNC_VSYNC, 0, + DI_SYNC_NONE, DI_SYNC_NONE, 0, 0); ipu_di_sync_config(disp, 5, 0, DI_SYNC_CLK, - h_sync_width + h_start_width, DI_SYNC_CLK, - width, 4, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, - 0); + di->h_sync_width + di->h_start_width, + DI_SYNC_CLK, di->width, 4, 0, DI_SYNC_NONE, + DI_SYNC_NONE, 0, 0); /* reset all unused counters */ __raw_writel(0, DI_SW_GEN0(disp, 6)); @@ -1112,7 +1085,7 @@ int32_t ipu_init_sync_panel(int disp, u32 pixel_clk, u16 width, u16 height, reg |= DI_POL_DRDY_DATA_POLARITY; __raw_writel(reg, DI_POL(disp)); - __raw_writel(width, DC_DISP_CONF2(DC_DISP_ID_SYNC(disp))); + __raw_writel(di->width, DC_DISP_CONF2(DC_DISP_ID_SYNC(disp))); return 0; } diff --git a/drivers/video/imx/mxc_ipuv3_fb.c b/drivers/video/imx/mxc_ipuv3_fb.c index fb9d364d23a..ab416fdd33c 100644 --- a/drivers/video/imx/mxc_ipuv3_fb.c +++ b/drivers/video/imx/mxc_ipuv3_fb.c @@ -1,5 +1,10 @@ // SPDX-License-Identifier: GPL-2.0+ /* + * Code fixes: + * + * (C) Copyright 2025 + * Brian Ruley, GE HealthCare, brian.ruley@gehealthcare.com + * * Porting to u-boot: * * (C) Copyright 2010 @@ -19,16 +24,17 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include +#include #include DECLARE_GLOBAL_DATA_PTR; @@ -60,6 +66,11 @@ static void fb_videomode_to_var(struct fb_var_screeninfo *var, var->vmode = mode->vmode & FB_VMODE_MASK; } +struct ipuv3_video_priv { + struct ipu_ctx *ctx; + ulong regs; +}; + /* * Structure containing the MXC specific framebuffer information. */ @@ -67,7 +78,7 @@ struct mxcfb_info { struct udevice *udev; int blank; ipu_channel_t ipu_ch; - int ipu_di; + struct ipu_di_config *di; u32 ipu_di_pix_fmt; unsigned char overlay; unsigned char alpha_chan_en; @@ -80,6 +91,8 @@ struct mxcfb_info { u32 cur_ipu_alpha_buf; u32 pseudo_palette[16]; + + struct ipu_ctx *ctx; }; enum { BOTH_ON, SRC_ON, TGT_ON, BOTH_OFF }; @@ -118,7 +131,7 @@ static int setup_disp_channel1(struct fb_info *fbi) struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; memset(¶ms, 0, sizeof(params)); - params.mem_dp_bg_sync.di = mxc_fbi->ipu_di; + params.mem_dp_bg_sync.di = mxc_fbi->di->disp; debug("%s called\n", __func__); /* @@ -137,9 +150,7 @@ static int setup_disp_channel1(struct fb_info *fbi) if (mxc_fbi->alpha_chan_en) params.mem_dp_bg_sync.alpha_chan_en = 1; - ipu_init_channel(mxc_fbi->ipu_ch, ¶ms); - - return 0; + return ipu_init_channel(mxc_fbi->ctx, mxc_fbi->ipu_ch, ¶ms); } static int setup_disp_channel2(struct fb_info *fbi) @@ -182,8 +193,8 @@ static int mxcfb_set_par(struct fb_info *fbi) struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; u32 out_pixel_fmt; - ipu_disable_channel(mxc_fbi->ipu_ch); - ipu_uninit_channel(mxc_fbi->ipu_ch); + ipu_disable_channel(mxc_fbi->ctx, mxc_fbi->ipu_ch); + ipu_uninit_channel(mxc_fbi->ctx, mxc_fbi->ipu_ch); mem_len = fbi->var.yres_virtual * fbi->fix.line_length; if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) { @@ -225,13 +236,19 @@ static int mxcfb_set_par(struct fb_info *fbi) debug("pixclock = %lu Hz\n", PICOS2KHZ(fbi->var.pixclock) * 1000UL); - if (ipu_init_sync_panel(mxc_fbi->ipu_di, - (PICOS2KHZ(fbi->var.pixclock)) * 1000UL, - fbi->var.xres, fbi->var.yres, out_pixel_fmt, - fbi->var.left_margin, fbi->var.hsync_len, - fbi->var.right_margin, fbi->var.upper_margin, - fbi->var.vsync_len, fbi->var.lower_margin, 0, - sig_cfg) != 0) { + mxc_fbi->di->pixel_clk_rate = (PICOS2KHZ(fbi->var.pixclock)) * 1000UL; + mxc_fbi->di->pixel_fmt = out_pixel_fmt; + mxc_fbi->di->width = fbi->var.xres; + mxc_fbi->di->height = fbi->var.yres; + mxc_fbi->di->h_start_width = fbi->var.left_margin; + mxc_fbi->di->h_sync_width = fbi->var.hsync_len; + mxc_fbi->di->h_end_width = fbi->var.right_margin; + mxc_fbi->di->v_start_width = fbi->var.upper_margin; + mxc_fbi->di->v_sync_width = fbi->var.vsync_len; + mxc_fbi->di->v_end_width = fbi->var.lower_margin; + mxc_fbi->di->v_to_h_sync = 0; + + if (ipu_init_sync_panel(mxc_fbi->di, sig_cfg) != 0) { puts("mxcfb: Error initializing panel.\n"); return -EINVAL; } @@ -241,7 +258,7 @@ static int mxcfb_set_par(struct fb_info *fbi) return retval; if (mxc_fbi->blank == FB_BLANK_UNBLANK) - ipu_enable_channel(mxc_fbi->ipu_ch); + ipu_enable_channel(mxc_fbi->ctx, mxc_fbi->ipu_ch); return retval; } @@ -403,9 +420,12 @@ static int mxcfb_unmap_video_memory(struct fb_info *fbi) * structures. This includes information such as bits per pixel, * color maps, screen width/height and RGBA offsets. * + * @param dev The device structure for the IPU passed in by the + * driver framework. + * * Return: Framebuffer structure initialized with our information */ -static struct fb_info *mxcfb_init_fbinfo(void) +static struct fb_info *mxcfb_init_fbinfo(struct udevice *dev) { #define BYTES_PER_LONG 4 #define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG)) @@ -419,13 +439,10 @@ static struct fb_info *mxcfb_init_fbinfo(void) /* * Allocate sufficient memory for the fb structure */ - - p = malloc(size); + p = devm_kzalloc(dev, size, GFP_KERNEL); if (!p) return NULL; - memset(p, 0, size); - fbi = (struct fb_info *)p; fbi->par = p + sizeof(struct fb_info) + PADDING; @@ -441,8 +458,6 @@ static struct fb_info *mxcfb_init_fbinfo(void) return fbi; } -extern struct clk *g_ipu_clk; - /* * Probe routine for the framebuffer driver. It is called during the * driver binding process. The following functions are performed in @@ -454,13 +469,15 @@ extern struct clk *g_ipu_clk; static int mxcfb_probe(struct udevice *dev, u32 interface_pix_fmt, u8 disp, struct fb_videomode const *mode) { + struct ipuv3_video_priv *ipu_priv = dev_get_priv(dev); + struct ipu_ctx *ctx = ipu_priv->ctx; struct fb_info *fbi; struct mxcfb_info *mxcfbi; /* * Initialize FB structures */ - fbi = mxcfb_init_fbinfo(); + fbi = mxcfb_init_fbinfo(dev); if (!fbi) return -ENOMEM; @@ -474,18 +491,24 @@ static int mxcfb_probe(struct udevice *dev, u32 interface_pix_fmt, u8 disp, mxcfbi->blank = FB_BLANK_POWERDOWN; } - mxcfbi->ipu_di = disp; + mxcfbi->di = devm_kzalloc(ctx->dev, sizeof(*mxcfbi->di), GFP_KERNEL); + if (!mxcfbi->di) + return -ENOMEM; + + mxcfbi->di->disp = disp; + mxcfbi->di->ctx = ctx; + mxcfbi->ctx = ctx; mxcfbi->udev = dev; - if (!ipu_clk_enabled()) - clk_enable(g_ipu_clk); + if (!ipu_clk_enabled(ctx)) + clk_enable(ctx->ipu_clk); ipu_disp_set_global_alpha(mxcfbi->ipu_ch, 1, 0x80); ipu_disp_set_color_key(mxcfbi->ipu_ch, 0, 0); g_dp_in_use = 1; - mxcfb_info[mxcfbi->ipu_di] = fbi; + mxcfb_info[mxcfbi->di->disp] = fbi; /* Need dummy values until real panel is configured */ @@ -514,20 +537,22 @@ static int mxcfb_probe(struct udevice *dev, u32 interface_pix_fmt, u8 disp, return 0; } -void ipuv3_fb_shutdown(void) +void ipuv3_fb_shutdown(struct udevice *dev) { int i; struct ipu_stat *stat = (struct ipu_stat *)IPU_STAT; + struct ipuv3_video_priv *ipu_priv = dev_get_priv(dev); + struct ipu_ctx *ctx = ipu_priv->ctx; - if (!ipu_clk_enabled()) + if (!ipu_clk_enabled(ctx)) return; for (i = 0; i < ARRAY_SIZE(mxcfb_info); i++) { struct fb_info *fbi = mxcfb_info[i]; if (fbi) { struct mxcfb_info *mxc_fbi = fbi->par; - ipu_disable_channel(mxc_fbi->ipu_ch); - ipu_uninit_channel(mxc_fbi->ipu_ch); + ipu_disable_channel(ctx, mxc_fbi->ipu_ch); + ipu_uninit_channel(ctx, mxc_fbi->ipu_ch); } } for (i = 0; i < ARRAY_SIZE(stat->int_stat); i++) { @@ -556,18 +581,22 @@ static int ipuv3_video_probe(struct udevice *dev) { struct video_uc_plat *plat = dev_get_uclass_plat(dev); struct video_priv *uc_priv = dev_get_uclass_priv(dev); + struct ipuv3_video_priv *ipu_priv = dev_get_priv(dev); #if defined(CONFIG_DISPLAY) struct udevice *disp_dev; #endif + struct ipu_ctx *ctx; u32 fb_start, fb_end; int ret; debug("%s() plat: base 0x%lx, size 0x%x\n", __func__, plat->base, plat->size); - ret = ipu_probe(); - if (ret) - return ret; + ctx = ipu_probe(dev); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + ipu_priv->ctx = ctx; ret = ipu_displays_init(); if (ret < 0) @@ -607,10 +636,6 @@ static int ipuv3_video_probe(struct udevice *dev) return 0; } -struct ipuv3_video_priv { - ulong regs; -}; - static int ipuv3_video_bind(struct udevice *dev) { struct video_uc_plat *plat = dev_get_uclass_plat(dev); diff --git a/include/ipu_pixfmt.h b/include/ipu_pixfmt.h index 866ead0ec71..a485d713805 100644 --- a/include/ipu_pixfmt.h +++ b/include/ipu_pixfmt.h @@ -11,6 +11,7 @@ #ifndef __IPU_PIXFMT_H__ #define __IPU_PIXFMT_H__ +#include #include #include @@ -62,6 +63,6 @@ int ipuv3_fb_init(struct fb_videomode const *mode, uint8_t disp, uint32_t pixfmt); -void ipuv3_fb_shutdown(void); +void ipuv3_fb_shutdown(struct udevice *dev); #endif