From: Dario Binacchi Date: Tue, 29 Dec 2020 23:16:27 +0000 (+0100) Subject: video: omap: add panel driver X-Git-Url: http://git.dujemihanovic.xyz/login.html?a=commitdiff_plain;h=ff94c15a3cf1ded3b851c65bb86168f6428e0d06;p=u-boot.git video: omap: add panel driver The previous version of am335x-fb.c contained the functionalities of two drivers that this patch has split. It was a video type driver that used the same registration compatible string that now registers a panel type driver. The proof of this is that two compatible strings were referred to within the same driver. There are now two drivers, each with its own compatible string, functions and API. Furthermore, the panel driver, in addition to decoding the display timings, is now also able to manage the backlight. Signed-off-by: Dario Binacchi Reviewed-by: Simon Glass --- diff --git a/arch/arm/dts/am335x-brppt1-mmc.dts b/arch/arm/dts/am335x-brppt1-mmc.dts index 6f919711f0..bd2f6c2e3e 100644 --- a/arch/arm/dts/am335x-brppt1-mmc.dts +++ b/arch/arm/dts/am335x-brppt1-mmc.dts @@ -53,8 +53,6 @@ bkl-pwm = <&pwmbacklight>; bkl-tps = <&tps_bl>; - u-boot,dm-pre-reloc; - panel-info { ac-bias = <255>; ac-bias-intrpt = <0>; @@ -238,8 +236,19 @@ status = "okay"; }; -&lcdc { - status = "disabled"; +&l4_per { + + segment@300000 { + + target-module@e000 { + u-boot,dm-pre-reloc; + + lcdc: lcdc@0 { + u-boot,dm-pre-reloc; + status = "disabled"; + }; + }; + }; }; &elm { diff --git a/arch/arm/dts/am335x-brppt1-nand.dts b/arch/arm/dts/am335x-brppt1-nand.dts index 9d4340f591..67c609739f 100644 --- a/arch/arm/dts/am335x-brppt1-nand.dts +++ b/arch/arm/dts/am335x-brppt1-nand.dts @@ -53,8 +53,6 @@ bkl-pwm = <&pwmbacklight>; bkl-tps = <&tps_bl>; - u-boot,dm-pre-reloc; - panel-info { ac-bias = <255>; ac-bias-intrpt = <0>; @@ -228,8 +226,19 @@ status = "disabled"; }; -&lcdc { - status = "disabled"; +&l4_per { + + segment@300000 { + + target-module@e000 { + u-boot,dm-pre-reloc; + + lcdc: lcdc@0 { + u-boot,dm-pre-reloc; + status = "disabled"; + }; + }; + }; }; &elm { diff --git a/arch/arm/dts/am335x-brppt1-spi.dts b/arch/arm/dts/am335x-brppt1-spi.dts index c078af8fba..ce3dce204d 100644 --- a/arch/arm/dts/am335x-brppt1-spi.dts +++ b/arch/arm/dts/am335x-brppt1-spi.dts @@ -54,8 +54,6 @@ bkl-pwm = <&pwmbacklight>; bkl-tps = <&tps_bl>; - u-boot,dm-pre-reloc; - panel-info { ac-bias = <255>; ac-bias-intrpt = <0>; @@ -259,8 +257,19 @@ status = "okay"; }; -&lcdc { - status = "disabled"; +&l4_per { + + segment@300000 { + + target-module@e000 { + u-boot,dm-pre-reloc; + + lcdc: lcdc@0 { + u-boot,dm-pre-reloc; + status = "disabled"; + }; + }; + }; }; &elm { diff --git a/arch/arm/dts/am335x-brsmarc1.dts b/arch/arm/dts/am335x-brsmarc1.dts index 7e9516e8f8..25cdb11164 100644 --- a/arch/arm/dts/am335x-brsmarc1.dts +++ b/arch/arm/dts/am335x-brsmarc1.dts @@ -59,7 +59,6 @@ /*backlight = <&tps_bl>; */ compatible = "ti,tilcdc,panel"; status = "okay"; - u-boot,dm-pre-reloc; panel-info { ac-bias = <255>; @@ -298,10 +297,21 @@ status = "okay"; }; -&lcdc { - status = "okay"; - ti,no-reset-on-init; - ti,no-idle-on-init; +&l4_per { + + segment@300000 { + + target-module@e000 { + u-boot,dm-pre-reloc; + + lcdc: lcdc@0 { + u-boot,dm-pre-reloc; + status = "okay"; + ti,no-reset-on-init; + ti,no-idle-on-init; + }; + }; + }; }; &elm { diff --git a/arch/arm/dts/am335x-brxre1.dts b/arch/arm/dts/am335x-brxre1.dts index 6091a12fb7..485c8e3613 100644 --- a/arch/arm/dts/am335x-brxre1.dts +++ b/arch/arm/dts/am335x-brxre1.dts @@ -79,8 +79,6 @@ backlight = <&tps_bl>; - u-boot,dm-pre-reloc; - panel-info { ac-bias = <255>; ac-bias-intrpt = <0>; @@ -254,10 +252,21 @@ status = "okay"; }; -&lcdc { - status = "okay"; - ti,no-reset-on-init; - ti,no-idle-on-init; +&l4_per { + + segment@300000 { + + target-module@e000 { + u-boot,dm-pre-reloc; + + lcdc: lcdc@0 { + u-boot,dm-pre-reloc; + status = "okay"; + ti,no-reset-on-init; + ti,no-idle-on-init; + }; + }; + }; }; &elm { diff --git a/arch/arm/dts/am335x-evm-u-boot.dtsi b/arch/arm/dts/am335x-evm-u-boot.dtsi index 400a1d2cec..4cf5f9928d 100644 --- a/arch/arm/dts/am335x-evm-u-boot.dtsi +++ b/arch/arm/dts/am335x-evm-u-boot.dtsi @@ -5,13 +5,20 @@ #include "am33xx-u-boot.dtsi" -/ { - panel { - u-boot,dm-pre-reloc; +&l4_per { + + segment@300000 { + + target-module@e000 { + u-boot,dm-pre-reloc; + + lcdc: lcdc@0 { + u-boot,dm-pre-reloc; + }; + }; }; }; - &mmc3 { status = "disabled"; }; diff --git a/arch/arm/dts/am335x-evmsk-u-boot.dtsi b/arch/arm/dts/am335x-evmsk-u-boot.dtsi index 96798330b1..1003f4d31a 100644 --- a/arch/arm/dts/am335x-evmsk-u-boot.dtsi +++ b/arch/arm/dts/am335x-evmsk-u-boot.dtsi @@ -7,8 +7,16 @@ #include "am33xx-u-boot.dtsi" -/ { - panel { - u-boot,dm-pre-reloc; +&l4_per { + + segment@300000 { + + target-module@e000 { + u-boot,dm-pre-reloc; + + lcdc: lcdc@0 { + u-boot,dm-pre-reloc; + }; + }; }; }; diff --git a/arch/arm/dts/am335x-guardian-u-boot.dtsi b/arch/arm/dts/am335x-guardian-u-boot.dtsi index c866ce83f3..986f58e664 100644 --- a/arch/arm/dts/am335x-guardian-u-boot.dtsi +++ b/arch/arm/dts/am335x-guardian-u-boot.dtsi @@ -10,16 +10,26 @@ ocp { u-boot,dm-pre-reloc; }; - - panel { - u-boot,dm-pre-reloc; - }; }; &l4_wkup { u-boot,dm-pre-reloc; }; +&l4_per { + + segment@300000 { + + target-module@e000 { + u-boot,dm-pre-reloc; + + lcdc: lcdc@0 { + u-boot,dm-pre-reloc; + }; + }; + }; +}; + &mmc1 { u-boot,dm-pre-reloc; }; diff --git a/arch/arm/dts/am335x-pdu001-u-boot.dtsi b/arch/arm/dts/am335x-pdu001-u-boot.dtsi index 4f4fc411f9..686a152fd9 100644 --- a/arch/arm/dts/am335x-pdu001-u-boot.dtsi +++ b/arch/arm/dts/am335x-pdu001-u-boot.dtsi @@ -9,16 +9,26 @@ ocp { u-boot,dm-pre-reloc; }; - - panel { - u-boot,dm-pre-reloc; - }; }; &l4_wkup { u-boot,dm-pre-reloc; }; +&l4_per { + + segment@300000 { + + target-module@e000 { + u-boot,dm-pre-reloc; + + lcdc: lcdc@0 { + u-boot,dm-pre-reloc; + }; + }; + }; +}; + &scm { u-boot,dm-pre-reloc; }; diff --git a/arch/arm/dts/am335x-pxm50-u-boot.dtsi b/arch/arm/dts/am335x-pxm50-u-boot.dtsi index 65ed948c58..e5af9fdf89 100644 --- a/arch/arm/dts/am335x-pxm50-u-boot.dtsi +++ b/arch/arm/dts/am335x-pxm50-u-boot.dtsi @@ -7,8 +7,16 @@ #include "am33xx-u-boot.dtsi" -/ { - panel { - u-boot,dm-pre-reloc; +&l4_per { + + segment@300000 { + + target-module@e000 { + u-boot,dm-pre-reloc; + + lcdc: lcdc@0 { + u-boot,dm-pre-reloc; + }; + }; }; }; diff --git a/arch/arm/dts/am335x-rut-u-boot.dtsi b/arch/arm/dts/am335x-rut-u-boot.dtsi index b16f75a764..a38c2dc607 100644 --- a/arch/arm/dts/am335x-rut-u-boot.dtsi +++ b/arch/arm/dts/am335x-rut-u-boot.dtsi @@ -7,8 +7,16 @@ #include "am33xx-u-boot.dtsi" -/ { - panel { - u-boot,dm-pre-reloc; +&l4_per { + + segment@300000 { + + target-module@e000 { + u-boot,dm-pre-reloc; + + lcdc: lcdc@0 { + u-boot,dm-pre-reloc; + }; + }; }; }; diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9db96aa891..4629f28cbb 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o obj-$(CONFIG_DM_VIDEO) += video_bmp.o obj-$(CONFIG_PANEL) += panel-uclass.o obj-$(CONFIG_SIMPLE_PANEL) += simple_panel.o +obj-$(CONFIG_AM335X_LCD) += tilcdc-panel.o endif obj-${CONFIG_EXYNOS_FB} += exynos/ diff --git a/drivers/video/am335x-fb.c b/drivers/video/am335x-fb.c index e99a9185a2..a3464ae6a0 100644 --- a/drivers/video/am335x-fb.c +++ b/drivers/video/am335x-fb.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include "am335x-fb.h" +#include "tilcdc-panel.h" #define LCDC_FMAX 200000000 @@ -323,7 +325,7 @@ int am335xfb_init(struct am335x_lcdpanel *panel) #else /* CONFIG_DM_VIDEO */ -#define FBSIZE(t, p) (((t)->hactive.typ * (t)->vactive.typ * (p)->bpp) >> 3) +#define FBSIZE(t, p) (((t).hactive.typ * (t).vactive.typ * (p).bpp) >> 3) enum { LCD_MAX_WIDTH = 2048, @@ -331,39 +333,8 @@ enum { LCD_MAX_LOG2_BPP = VIDEO_BPP32, }; -/** - * tilcdc_panel_info: Panel parameters - * - * @ac_bias: AC Bias Pin Frequency - * @ac_bias_intrpt: AC Bias Pin Transitions per Interrupt - * @dma_burst_sz: DMA burst size - * @bpp: Bits per pixel - * @fdd: FIFO DMA Request Delay - * @tft_alt_mode: TFT Alternative Signal Mapping (Only for active) - * @invert_pxl_clk: Invert pixel clock - * @sync_edge: Horizontal and Vertical Sync Edge: 0=rising 1=falling - * @sync_ctrl: Horizontal and Vertical Sync: Control: 0=ignore - * @raster_order: Raster Data Order Select: 1=Most-to-least 0=Least-to-most - * @fifo_th: DMA FIFO threshold - */ -struct tilcdc_panel_info { - u32 ac_bias; - u32 ac_bias_intrpt; - u32 dma_burst_sz; - u32 bpp; - u32 fdd; - bool tft_alt_mode; - bool invert_pxl_clk; - u32 sync_edge; - u32 sync_ctrl; - u32 raster_order; - u32 fifo_th; -}; - struct am335x_fb_priv { struct am335x_lcdhw *regs; - struct tilcdc_panel_info panel; - struct display_timing timing; }; static int am335x_fb_remove(struct udevice *dev) @@ -381,16 +352,71 @@ static int am335x_fb_probe(struct udevice *dev) struct video_priv *uc_priv = dev_get_uclass_priv(dev); struct am335x_fb_priv *priv = dev_get_priv(dev); struct am335x_lcdhw *regs = priv->regs; - struct tilcdc_panel_info *panel = &priv->panel; - struct display_timing *timing = &priv->timing; + struct udevice *panel; + struct tilcdc_panel_info info; + struct display_timing timing; struct cm_dpll *const cmdpll = (struct cm_dpll *)CM_DPLL; u32 reg; + int err; /* Before relocation we don't need to do anything */ if (!(gd->flags & GD_FLG_RELOC)) return 0; - am335x_fb_set_pixel_clk_rate(regs, timing->pixelclock.typ); + err = uclass_get_device(UCLASS_PANEL, 0, &panel); + if (err) { + dev_err(dev, "failed to get panel\n"); + return err; + } + + err = panel_get_display_timing(panel, &timing); + if (err) { + dev_err(dev, "failed to get display timing\n"); + return err; + } + + if (timing.pixelclock.typ > (LCDC_FMAX / 2)) { + dev_err(dev, "invalid display clock-frequency: %d Hz\n", + timing.pixelclock.typ); + return -EINVAL; + } + + if (timing.hactive.typ > LCD_MAX_WIDTH) + timing.hactive.typ = LCD_MAX_WIDTH; + + if (timing.vactive.typ > LCD_MAX_HEIGHT) + timing.vactive.typ = LCD_MAX_HEIGHT; + + err = tilcdc_panel_get_display_info(panel, &info); + if (err) { + dev_err(dev, "failed to get panel info\n"); + return err; + } + + switch (info.bpp) { + case 16: + case 24: + case 32: + break; + default: + dev_err(dev, "invalid seting, bpp: %d\n", info.bpp); + return -EINVAL; + } + + switch (info.dma_burst_sz) { + case 1: + case 2: + case 4: + case 8: + case 16: + break; + default: + dev_err(dev, "invalid setting, dma-burst-sz: %d\n", + info.dma_burst_sz); + return -EINVAL; + } + + am335x_fb_set_pixel_clk_rate(regs, timing.pixelclock.typ); /* clock source for LCDC from dispPLL M2 */ writel(0, &cmdpll->clklcdcpixelclk); @@ -411,14 +437,14 @@ static int am335x_fb_probe(struct udevice *dev) writel(reg, ®s->ctrl); writel(uc_plat->base, ®s->lcddma_fb0_base); - writel(uc_plat->base + FBSIZE(timing, panel), + writel(uc_plat->base + FBSIZE(timing, info), ®s->lcddma_fb0_ceiling); writel(uc_plat->base, ®s->lcddma_fb1_base); - writel(uc_plat->base + FBSIZE(timing, panel), + writel(uc_plat->base + FBSIZE(timing, info), ®s->lcddma_fb1_ceiling); - reg = LCDC_DMA_CTRL_FIFO_TH(panel->fifo_th); - switch (panel->dma_burst_sz) { + reg = LCDC_DMA_CTRL_FIFO_TH(info.fifo_th); + switch (info.dma_burst_sz) { case 1: reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_1); break; @@ -438,155 +464,84 @@ static int am335x_fb_probe(struct udevice *dev) writel(reg, ®s->lcddma_ctrl); - writel(LCDC_RASTER_TIMING_0_HORLSB(timing->hactive.typ) | - LCDC_RASTER_TIMING_0_HORMSB(timing->hactive.typ) | - LCDC_RASTER_TIMING_0_HFPLSB(timing->hfront_porch.typ) | - LCDC_RASTER_TIMING_0_HBPLSB(timing->hback_porch.typ) | - LCDC_RASTER_TIMING_0_HSWLSB(timing->hsync_len.typ), + writel(LCDC_RASTER_TIMING_0_HORLSB(timing.hactive.typ) | + LCDC_RASTER_TIMING_0_HORMSB(timing.hactive.typ) | + LCDC_RASTER_TIMING_0_HFPLSB(timing.hfront_porch.typ) | + LCDC_RASTER_TIMING_0_HBPLSB(timing.hback_porch.typ) | + LCDC_RASTER_TIMING_0_HSWLSB(timing.hsync_len.typ), ®s->raster_timing0); - writel(LCDC_RASTER_TIMING_1_VBP(timing->vback_porch.typ) | - LCDC_RASTER_TIMING_1_VFP(timing->vfront_porch.typ) | - LCDC_RASTER_TIMING_1_VSW(timing->vsync_len.typ) | - LCDC_RASTER_TIMING_1_VERLSB(timing->vactive.typ), + writel(LCDC_RASTER_TIMING_1_VBP(timing.vback_porch.typ) | + LCDC_RASTER_TIMING_1_VFP(timing.vfront_porch.typ) | + LCDC_RASTER_TIMING_1_VSW(timing.vsync_len.typ) | + LCDC_RASTER_TIMING_1_VERLSB(timing.vactive.typ), ®s->raster_timing1); - reg = LCDC_RASTER_TIMING_2_ACB(panel->ac_bias) | - LCDC_RASTER_TIMING_2_ACBI(panel->ac_bias_intrpt) | - LCDC_RASTER_TIMING_2_HSWMSB(timing->hsync_len.typ) | - LCDC_RASTER_TIMING_2_VERMSB(timing->vactive.typ) | - LCDC_RASTER_TIMING_2_HBPMSB(timing->hback_porch.typ) | - LCDC_RASTER_TIMING_2_HFPMSB(timing->hfront_porch.typ); + reg = LCDC_RASTER_TIMING_2_ACB(info.ac_bias) | + LCDC_RASTER_TIMING_2_ACBI(info.ac_bias_intrpt) | + LCDC_RASTER_TIMING_2_HSWMSB(timing.hsync_len.typ) | + LCDC_RASTER_TIMING_2_VERMSB(timing.vactive.typ) | + LCDC_RASTER_TIMING_2_HBPMSB(timing.hback_porch.typ) | + LCDC_RASTER_TIMING_2_HFPMSB(timing.hfront_porch.typ); - if (timing->flags & DISPLAY_FLAGS_VSYNC_LOW) + if (timing.flags & DISPLAY_FLAGS_VSYNC_LOW) reg |= LCDC_RASTER_TIMING_2_VSYNC_INVERT; - if (timing->flags & DISPLAY_FLAGS_HSYNC_LOW) + if (timing.flags & DISPLAY_FLAGS_HSYNC_LOW) reg |= LCDC_RASTER_TIMING_2_HSYNC_INVERT; - if (panel->invert_pxl_clk) + if (info.invert_pxl_clk) reg |= LCDC_RASTER_TIMING_2_PXCLK_INVERT; - if (panel->sync_edge) + if (info.sync_edge) reg |= LCDC_RASTER_TIMING_2_HSVS_RISEFALL; - if (panel->sync_ctrl) + if (info.sync_ctrl) reg |= LCDC_RASTER_TIMING_2_HSVS_CONTROL; writel(reg, ®s->raster_timing2); reg = LCDC_RASTER_CTRL_PALMODE_RAWDATA | LCDC_RASTER_CTRL_TFT_MODE | - LCDC_RASTER_CTRL_ENABLE | LCDC_RASTER_CTRL_REQDLY(panel->fdd); + LCDC_RASTER_CTRL_ENABLE | LCDC_RASTER_CTRL_REQDLY(info.fdd); - if (panel->tft_alt_mode) + if (info.tft_alt_mode) reg |= LCDC_RASTER_CTRL_TFT_ALT_ENABLE; - if (panel->bpp == 24) + if (info.bpp == 24) reg |= LCDC_RASTER_CTRL_TFT_24BPP_MODE; - else if (panel->bpp == 32) + else if (info.bpp == 32) reg |= LCDC_RASTER_CTRL_TFT_24BPP_MODE | LCDC_RASTER_CTRL_TFT_24BPP_UNPACK; - if (panel->raster_order) + if (info.raster_order) reg |= LCDC_RASTER_CTRL_DATA_ORDER; writel(reg, ®s->raster_ctrl); - uc_priv->xsize = timing->hactive.typ; - uc_priv->ysize = timing->vactive.typ; - uc_priv->bpix = log_2_n_round_up(panel->bpp); - return 0; -} - -static int am335x_fb_of_to_plat(struct udevice *dev) -{ - struct am335x_fb_priv *priv = dev_get_priv(dev); - struct tilcdc_panel_info *panel = &priv->panel; - struct display_timing *timing = &priv->timing; - ofnode node; - int err; + uc_priv->xsize = timing.hactive.typ; + uc_priv->ysize = timing.vactive.typ; + uc_priv->bpix = log_2_n_round_up(info.bpp); - node = ofnode_by_compatible(ofnode_null(), "ti,am33xx-tilcdc"); - if (!ofnode_valid(node)) { - dev_err(dev, "missing 'ti,am33xx-tilcdc' node\n"); - return -ENXIO; - } - - priv->regs = (struct am335x_lcdhw *)ofnode_get_addr(node); - dev_dbg(dev, "LCD: base address=0x%x\n", (unsigned int)priv->regs); - - err = ofnode_decode_display_timing(dev_ofnode(dev), 0, timing); + err = panel_enable_backlight(panel); if (err) { - dev_err(dev, "failed to get display timing\n"); + dev_err(dev, "failed to enable panel backlight\n"); return err; } - if (timing->pixelclock.typ > (LCDC_FMAX / 2)) { - dev_err(dev, "invalid display clock-frequency: %d Hz\n", - timing->pixelclock.typ); - return -EINVAL; - } - - if (timing->hactive.typ > LCD_MAX_WIDTH) - timing->hactive.typ = LCD_MAX_WIDTH; - - if (timing->vactive.typ > LCD_MAX_HEIGHT) - timing->vactive.typ = LCD_MAX_HEIGHT; - - node = ofnode_find_subnode(dev_ofnode(dev), "panel-info"); - if (!ofnode_valid(node)) { - dev_err(dev, "missing 'panel-info' node\n"); - return -ENXIO; - } - - err |= ofnode_read_u32(node, "ac-bias", &panel->ac_bias); - err |= ofnode_read_u32(node, "ac-bias-intrpt", &panel->ac_bias_intrpt); - err |= ofnode_read_u32(node, "dma-burst-sz", &panel->dma_burst_sz); - err |= ofnode_read_u32(node, "bpp", &panel->bpp); - err |= ofnode_read_u32(node, "fdd", &panel->fdd); - err |= ofnode_read_u32(node, "sync-edge", &panel->sync_edge); - err |= ofnode_read_u32(node, "sync-ctrl", &panel->sync_ctrl); - err |= ofnode_read_u32(node, "raster-order", &panel->raster_order); - err |= ofnode_read_u32(node, "fifo-th", &panel->fifo_th); - if (err) { - dev_err(dev, "failed to get panel info\n"); - return err; - } + return 0; +} - switch (panel->bpp) { - case 16: - case 24: - case 32: - break; - default: - dev_err(dev, "invalid seting, bpp: %d\n", panel->bpp); - return -EINVAL; - } +static int am335x_fb_ofdata_to_platdata(struct udevice *dev) +{ + struct am335x_fb_priv *priv = dev_get_priv(dev); - switch (panel->dma_burst_sz) { - case 1: - case 2: - case 4: - case 8: - case 16: - break; - default: - dev_err(dev, "invalid setting, dma-burst-sz: %d\n", - panel->dma_burst_sz); + priv->regs = (struct am335x_lcdhw *)dev_read_addr(dev); + if ((fdt_addr_t)priv->regs == FDT_ADDR_T_NONE) { + dev_err(dev, "failed to get base address\n"); return -EINVAL; } - /* optional */ - panel->tft_alt_mode = ofnode_read_bool(node, "tft-alt-mode"); - panel->invert_pxl_clk = ofnode_read_bool(node, "invert-pxl-clk"); - - dev_dbg(dev, "LCD: %dx%d, bpp=%d, clk=%d Hz\n", timing->hactive.typ, - timing->vactive.typ, panel->bpp, timing->pixelclock.typ); - dev_dbg(dev, " hbp=%d, hfp=%d, hsw=%d\n", timing->hback_porch.typ, - timing->hfront_porch.typ, timing->hsync_len.typ); - dev_dbg(dev, " vbp=%d, vfp=%d, vsw=%d\n", timing->vback_porch.typ, - timing->vfront_porch.typ, timing->vsync_len.typ); - + dev_dbg(dev, "LCD: base address=0x%x\n", (unsigned int)priv->regs); return 0; } @@ -602,7 +557,7 @@ static int am335x_fb_bind(struct udevice *dev) } static const struct udevice_id am335x_fb_ids[] = { - { .compatible = "ti,tilcdc,panel" }, + { .compatible = "ti,am33xx-tilcdc" }, { } }; diff --git a/drivers/video/am335x-fb.h b/drivers/video/am335x-fb.h index c9f92bc389..4952dd96e9 100644 --- a/drivers/video/am335x-fb.h +++ b/drivers/video/am335x-fb.h @@ -70,6 +70,37 @@ struct am335x_lcdpanel { int am335xfb_init(struct am335x_lcdpanel *panel); +#else /* CONFIG_DM_VIDEO */ + +/** + * tilcdc_panel_info: Panel parameters + * + * @ac_bias: AC Bias Pin Frequency + * @ac_bias_intrpt: AC Bias Pin Transitions per Interrupt + * @dma_burst_sz: DMA burst size + * @bpp: Bits per pixel + * @fdd: FIFO DMA Request Delay + * @tft_alt_mode: TFT Alternative Signal Mapping (Only for active) + * @invert_pxl_clk: Invert pixel clock + * @sync_edge: Horizontal and Vertical Sync Edge: 0=rising 1=falling + * @sync_ctrl: Horizontal and Vertical Sync: Control: 0=ignore + * @raster_order: Raster Data Order Select: 1=Most-to-least 0=Least-to-most + * @fifo_th: DMA FIFO threshold + */ +struct tilcdc_panel_info { + u32 ac_bias; + u32 ac_bias_intrpt; + u32 dma_burst_sz; + u32 bpp; + u32 fdd; + bool tft_alt_mode; + bool invert_pxl_clk; + u32 sync_edge; + u32 sync_ctrl; + u32 raster_order; + u32 fifo_th; +}; + #endif /* CONFIG_DM_VIDEO */ #endif /* AM335X_FB_H */ diff --git a/drivers/video/tilcdc-panel.c b/drivers/video/tilcdc-panel.c new file mode 100644 index 0000000000..84303a6b71 --- /dev/null +++ b/drivers/video/tilcdc-panel.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * OMAP panel support + * + * Copyright (C) 2020 Dario Binacchi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "am335x-fb.h" + +struct tilcdc_panel_priv { + struct tilcdc_panel_info info; + struct display_timing timing; + struct udevice *backlight; + struct gpio_desc enable; +}; + +static int tilcdc_panel_enable_backlight(struct udevice *dev) +{ + struct tilcdc_panel_priv *priv = dev_get_priv(dev); + + if (dm_gpio_is_valid(&priv->enable)) + dm_gpio_set_value(&priv->enable, 1); + + if (priv->backlight) + return backlight_enable(priv->backlight); + + return 0; +} + +static int tilcdc_panel_set_backlight(struct udevice *dev, int percent) +{ + struct tilcdc_panel_priv *priv = dev_get_priv(dev); + + if (dm_gpio_is_valid(&priv->enable)) + dm_gpio_set_value(&priv->enable, 1); + + if (priv->backlight) + return backlight_set_brightness(priv->backlight, percent); + + return 0; +} + +int tilcdc_panel_get_display_info(struct udevice *dev, + struct tilcdc_panel_info *info) +{ + struct tilcdc_panel_priv *priv = dev_get_priv(dev); + + memcpy(info, &priv->info, sizeof(*info)); + return 0; +} + +static int tilcdc_panel_get_display_timing(struct udevice *dev, + struct display_timing *timing) +{ + struct tilcdc_panel_priv *priv = dev_get_priv(dev); + + memcpy(timing, &priv->timing, sizeof(*timing)); + return 0; +} + +static int tilcdc_panel_remove(struct udevice *dev) +{ + struct tilcdc_panel_priv *priv = dev_get_priv(dev); + + if (dm_gpio_is_valid(&priv->enable)) + dm_gpio_free(dev, &priv->enable); + + return 0; +} + +static int tilcdc_panel_probe(struct udevice *dev) +{ + struct tilcdc_panel_priv *priv = dev_get_priv(dev); + int err; + + err = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (err) + dev_warn(dev, "failed to get backlight\n"); + + err = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable, + GPIOD_IS_OUT); + if (err) { + dev_warn(dev, "failed to get enable GPIO\n"); + if (err != -ENOENT) + return err; + } + + return 0; +} + +static int tilcdc_panel_of_to_plat(struct udevice *dev) +{ + struct tilcdc_panel_priv *priv = dev_get_priv(dev); + ofnode node; + int err; + + err = ofnode_decode_display_timing(dev_ofnode(dev), 0, &priv->timing); + if (err) { + dev_err(dev, "failed to get display timing\n"); + return err; + } + + node = dev_read_subnode(dev, "panel-info"); + if (!ofnode_valid(node)) { + dev_err(dev, "missing 'panel-info' node\n"); + return -ENXIO; + } + + err |= ofnode_read_u32(node, "ac-bias", &priv->info.ac_bias); + err |= ofnode_read_u32(node, "ac-bias-intrpt", + &priv->info.ac_bias_intrpt); + err |= ofnode_read_u32(node, "dma-burst-sz", &priv->info.dma_burst_sz); + err |= ofnode_read_u32(node, "bpp", &priv->info.bpp); + err |= ofnode_read_u32(node, "fdd", &priv->info.fdd); + err |= ofnode_read_u32(node, "sync-edge", &priv->info.sync_edge); + err |= ofnode_read_u32(node, "sync-ctrl", &priv->info.sync_ctrl); + err |= ofnode_read_u32(node, "raster-order", &priv->info.raster_order); + err |= ofnode_read_u32(node, "fifo-th", &priv->info.fifo_th); + if (err) { + dev_err(dev, "failed to get panel info\n"); + return err; + } + + /* optional */ + priv->info.tft_alt_mode = ofnode_read_bool(node, "tft-alt-mode"); + priv->info.invert_pxl_clk = ofnode_read_bool(node, "invert-pxl-clk"); + + dev_dbg(dev, "LCD: %dx%d, bpp=%d, clk=%d Hz\n", + priv->timing.hactive.typ, priv->timing.vactive.typ, + priv->info.bpp, priv->timing.pixelclock.typ); + dev_dbg(dev, " hbp=%d, hfp=%d, hsw=%d\n", + priv->timing.hback_porch.typ, priv->timing.hfront_porch.typ, + priv->timing.hsync_len.typ); + dev_dbg(dev, " vbp=%d, vfp=%d, vsw=%d\n", + priv->timing.vback_porch.typ, priv->timing.vfront_porch.typ, + priv->timing.vsync_len.typ); + + return 0; +} + +static const struct panel_ops tilcdc_panel_ops = { + .enable_backlight = tilcdc_panel_enable_backlight, + .set_backlight = tilcdc_panel_set_backlight, + .get_display_timing = tilcdc_panel_get_display_timing, +}; + +static const struct udevice_id tilcdc_panel_ids[] = { + {.compatible = "ti,tilcdc,panel"}, + {} +}; + +U_BOOT_DRIVER(tilcdc_panel) = { + .name = "tilcdc_panel", + .id = UCLASS_PANEL, + .of_match = tilcdc_panel_ids, + .ops = &tilcdc_panel_ops, + .ofdata_to_platdata = tilcdc_panel_of_to_plat, + .probe = tilcdc_panel_probe, + .remove = tilcdc_panel_remove, + .priv_auto = sizeof(struct tilcdc_panel_priv), +}; diff --git a/drivers/video/tilcdc-panel.h b/drivers/video/tilcdc-panel.h new file mode 100644 index 0000000000..6b40731304 --- /dev/null +++ b/drivers/video/tilcdc-panel.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2020 Dario Binacchi + */ + +#ifndef _TILCDC_PANEL_H +#define _TILCDC_PANEL_H + +#include "am335x-fb.h" + +int tilcdc_panel_get_display_info(struct udevice *dev, + struct tilcdc_panel_info *info); + +#endif /* _TILCDC_PANEL_H */