video: stm32: stm32_ltdc: add bridge to display controller
Manage a bridge insert between the display controller & a panel. Signed-off-by: Yannick Fertré <yannick.fertre@st.com>
This commit is contained in:
parent
7a779ed175
commit
aeaf330649
@ -7,19 +7,18 @@
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <display.h>
|
||||
#include <dm.h>
|
||||
#include <panel.h>
|
||||
#include <reset.h>
|
||||
#include <video.h>
|
||||
#include <video_bridge.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/gpio.h>
|
||||
#include <dm/device-internal.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct stm32_ltdc_priv {
|
||||
void __iomem *regs;
|
||||
struct display_timing timing;
|
||||
enum video_log2_bpp l2bpp;
|
||||
u32 bg_col_argb;
|
||||
u32 crop_x, crop_y, crop_w, crop_h;
|
||||
@ -174,8 +173,8 @@ static u32 stm32_ltdc_get_pixel_format(enum video_log2_bpp l2bpp)
|
||||
case VIDEO_BPP2:
|
||||
case VIDEO_BPP4:
|
||||
default:
|
||||
debug("%s: warning %dbpp not supported yet, %dbpp instead\n",
|
||||
__func__, VNBITS(l2bpp), VNBITS(VIDEO_BPP16));
|
||||
pr_warn("%s: warning %dbpp not supported yet, %dbpp instead\n",
|
||||
__func__, VNBITS(l2bpp), VNBITS(VIDEO_BPP16));
|
||||
pf = PF_RGB565;
|
||||
break;
|
||||
}
|
||||
@ -209,23 +208,23 @@ static void stm32_ltdc_enable(struct stm32_ltdc_priv *priv)
|
||||
setbits_le32(priv->regs + LTDC_GCR, GCR_LTDCEN);
|
||||
}
|
||||
|
||||
static void stm32_ltdc_set_mode(struct stm32_ltdc_priv *priv)
|
||||
static void stm32_ltdc_set_mode(struct stm32_ltdc_priv *priv,
|
||||
struct display_timing *timings)
|
||||
{
|
||||
void __iomem *regs = priv->regs;
|
||||
struct display_timing *timing = &priv->timing;
|
||||
u32 hsync, vsync, acc_hbp, acc_vbp, acc_act_w, acc_act_h;
|
||||
u32 total_w, total_h;
|
||||
u32 val;
|
||||
|
||||
/* Convert video timings to ltdc timings */
|
||||
hsync = timing->hsync_len.typ - 1;
|
||||
vsync = timing->vsync_len.typ - 1;
|
||||
acc_hbp = hsync + timing->hback_porch.typ;
|
||||
acc_vbp = vsync + timing->vback_porch.typ;
|
||||
acc_act_w = acc_hbp + timing->hactive.typ;
|
||||
acc_act_h = acc_vbp + timing->vactive.typ;
|
||||
total_w = acc_act_w + timing->hfront_porch.typ;
|
||||
total_h = acc_act_h + timing->vfront_porch.typ;
|
||||
hsync = timings->hsync_len.typ - 1;
|
||||
vsync = timings->vsync_len.typ - 1;
|
||||
acc_hbp = hsync + timings->hback_porch.typ;
|
||||
acc_vbp = vsync + timings->vback_porch.typ;
|
||||
acc_act_w = acc_hbp + timings->hactive.typ;
|
||||
acc_act_h = acc_vbp + timings->vactive.typ;
|
||||
total_w = acc_act_w + timings->hfront_porch.typ;
|
||||
total_h = acc_act_h + timings->vfront_porch.typ;
|
||||
|
||||
/* Synchronization sizes */
|
||||
val = (hsync << 16) | vsync;
|
||||
@ -247,14 +246,14 @@ static void stm32_ltdc_set_mode(struct stm32_ltdc_priv *priv)
|
||||
|
||||
/* Signal polarities */
|
||||
val = 0;
|
||||
debug("%s: timing->flags 0x%08x\n", __func__, timing->flags);
|
||||
if (timing->flags & DISPLAY_FLAGS_HSYNC_HIGH)
|
||||
debug("%s: timing->flags 0x%08x\n", __func__, timings->flags);
|
||||
if (timings->flags & DISPLAY_FLAGS_HSYNC_HIGH)
|
||||
val |= GCR_HSPOL;
|
||||
if (timing->flags & DISPLAY_FLAGS_VSYNC_HIGH)
|
||||
if (timings->flags & DISPLAY_FLAGS_VSYNC_HIGH)
|
||||
val |= GCR_VSPOL;
|
||||
if (timing->flags & DISPLAY_FLAGS_DE_HIGH)
|
||||
if (timings->flags & DISPLAY_FLAGS_DE_HIGH)
|
||||
val |= GCR_DEPOL;
|
||||
if (timing->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
|
||||
if (timings->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
|
||||
val |= GCR_PCPOL;
|
||||
clrsetbits_le32(regs + LTDC_GCR,
|
||||
GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
|
||||
@ -330,96 +329,120 @@ static int stm32_ltdc_probe(struct udevice *dev)
|
||||
struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
|
||||
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
struct stm32_ltdc_priv *priv = dev_get_priv(dev);
|
||||
struct udevice *panel;
|
||||
struct udevice *bridge = NULL;
|
||||
struct udevice *panel = NULL;
|
||||
struct display_timing timings;
|
||||
struct clk pclk;
|
||||
struct reset_ctl rst;
|
||||
int rate, ret;
|
||||
int ret;
|
||||
|
||||
priv->regs = (void *)dev_read_addr(dev);
|
||||
if ((fdt_addr_t)priv->regs == FDT_ADDR_T_NONE) {
|
||||
debug("%s: ltdc dt register address error\n", __func__);
|
||||
dev_err(dev, "ltdc dt register address error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = clk_get_by_index(dev, 0, &pclk);
|
||||
if (ret) {
|
||||
debug("%s: peripheral clock get error %d\n", __func__, ret);
|
||||
dev_err(dev, "peripheral clock get error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_enable(&pclk);
|
||||
if (ret) {
|
||||
debug("%s: peripheral clock enable error %d\n",
|
||||
__func__, ret);
|
||||
dev_err(dev, "peripheral clock enable error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = uclass_first_device_err(UCLASS_PANEL, &panel);
|
||||
if (ret) {
|
||||
if (ret != -ENODEV)
|
||||
dev_err(dev, "panel device error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = panel_get_display_timing(panel, &timings);
|
||||
if (ret) {
|
||||
ret = fdtdec_decode_display_timing(gd->fdt_blob,
|
||||
dev_of_offset(panel),
|
||||
0, &timings);
|
||||
if (ret) {
|
||||
dev_err(dev, "decode display timing error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = clk_set_rate(&pclk, timings.pixelclock.typ);
|
||||
if (ret)
|
||||
dev_warn(dev, "fail to set pixel clock %d hz\n",
|
||||
timings.pixelclock.typ);
|
||||
|
||||
debug("%s: Set pixel clock req %d hz get %ld hz\n", __func__,
|
||||
timings.pixelclock.typ, clk_get_rate(&pclk));
|
||||
|
||||
ret = reset_get_by_index(dev, 0, &rst);
|
||||
if (ret) {
|
||||
debug("%s: missing ltdc hardware reset\n", __func__);
|
||||
return -ENODEV;
|
||||
dev_err(dev, "missing ltdc hardware reset\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Reset */
|
||||
reset_deassert(&rst);
|
||||
|
||||
ret = uclass_first_device(UCLASS_PANEL, &panel);
|
||||
if (ret) {
|
||||
debug("%s: panel device error %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) {
|
||||
ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge);
|
||||
if (ret)
|
||||
debug("No video bridge, or no backlight on bridge\n");
|
||||
|
||||
ret = panel_enable_backlight(panel);
|
||||
if (ret) {
|
||||
debug("%s: panel %s enable backlight error %d\n",
|
||||
__func__, panel->name, ret);
|
||||
return ret;
|
||||
if (bridge) {
|
||||
ret = video_bridge_attach(bridge);
|
||||
if (ret) {
|
||||
dev_err(dev, "fail to attach bridge\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = fdtdec_decode_display_timing(gd->fdt_blob,
|
||||
dev_of_offset(dev), 0,
|
||||
&priv->timing);
|
||||
if (ret) {
|
||||
debug("%s: decode display timing error %d\n",
|
||||
__func__, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rate = clk_set_rate(&pclk, priv->timing.pixelclock.typ);
|
||||
if (rate < 0) {
|
||||
debug("%s: fail to set pixel clock %d hz %d hz\n",
|
||||
__func__, priv->timing.pixelclock.typ, rate);
|
||||
return rate;
|
||||
}
|
||||
|
||||
debug("%s: Set pixel clock req %d hz get %d hz\n", __func__,
|
||||
priv->timing.pixelclock.typ, rate);
|
||||
|
||||
/* TODO Below parameters are hard-coded for the moment... */
|
||||
priv->l2bpp = VIDEO_BPP16;
|
||||
priv->bg_col_argb = 0xFFFFFFFF; /* white no transparency */
|
||||
priv->crop_x = 0;
|
||||
priv->crop_y = 0;
|
||||
priv->crop_w = priv->timing.hactive.typ;
|
||||
priv->crop_h = priv->timing.vactive.typ;
|
||||
priv->crop_w = timings.hactive.typ;
|
||||
priv->crop_h = timings.vactive.typ;
|
||||
priv->alpha = 0xFF;
|
||||
|
||||
debug("%s: %dx%d %dbpp frame buffer at 0x%lx\n", __func__,
|
||||
priv->timing.hactive.typ, priv->timing.vactive.typ,
|
||||
timings.hactive.typ, timings.vactive.typ,
|
||||
VNBITS(priv->l2bpp), uc_plat->base);
|
||||
debug("%s: crop %d,%d %dx%d bg 0x%08x alpha %d\n", __func__,
|
||||
priv->crop_x, priv->crop_y, priv->crop_w, priv->crop_h,
|
||||
priv->bg_col_argb, priv->alpha);
|
||||
|
||||
/* Configure & start LTDC */
|
||||
stm32_ltdc_set_mode(priv);
|
||||
stm32_ltdc_set_mode(priv, &timings);
|
||||
stm32_ltdc_set_layer1(priv, uc_plat->base);
|
||||
stm32_ltdc_enable(priv);
|
||||
|
||||
uc_priv->xsize = priv->timing.hactive.typ;
|
||||
uc_priv->ysize = priv->timing.vactive.typ;
|
||||
uc_priv->xsize = timings.hactive.typ;
|
||||
uc_priv->ysize = timings.vactive.typ;
|
||||
uc_priv->bpix = priv->l2bpp;
|
||||
|
||||
if (!bridge) {
|
||||
ret = panel_enable_backlight(panel);
|
||||
if (ret) {
|
||||
dev_err(dev, "panel %s enable backlight error %d\n",
|
||||
panel->name, ret);
|
||||
return ret;
|
||||
}
|
||||
} else if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) {
|
||||
ret = video_bridge_set_backlight(bridge, 80);
|
||||
if (ret) {
|
||||
dev_err(dev, "fail to set backlight\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
video_set_flush_dcache(dev, true);
|
||||
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user