]> git.sur5r.net Git - u-boot/blob - drivers/video/sunxi/sunxi_lcd.c
Merge tag 'signed-efi-2018.07' of git://github.com/agraf/u-boot
[u-boot] / drivers / video / sunxi / sunxi_lcd.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Allwinner LCD driver
4  *
5  * (C) Copyright 2017 Vasily Khoruzhick <anarsoul@gmail.com>
6  */
7
8 #include <common.h>
9 #include <display.h>
10 #include <video_bridge.h>
11 #include <backlight.h>
12 #include <dm.h>
13 #include <edid.h>
14 #include <asm/io.h>
15 #include <asm/arch/clock.h>
16 #include <asm/arch/lcdc.h>
17 #include <asm/arch/gpio.h>
18 #include <asm/gpio.h>
19
20 struct sunxi_lcd_priv {
21         struct display_timing timing;
22         int panel_bpp;
23 };
24
25 static void sunxi_lcdc_config_pinmux(void)
26 {
27 #ifdef CONFIG_MACH_SUN50I
28         int pin;
29
30         for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(21); pin++) {
31                 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0);
32                 sunxi_gpio_set_drv(pin, 3);
33         }
34 #endif
35 }
36
37 static int sunxi_lcd_enable(struct udevice *dev, int bpp,
38                             const struct display_timing *edid)
39 {
40         struct sunxi_ccm_reg * const ccm =
41                (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
42         struct sunxi_lcdc_reg * const lcdc =
43                (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
44         struct sunxi_lcd_priv *priv = dev_get_priv(dev);
45         struct udevice *backlight;
46         int clk_div, clk_double, ret;
47
48         /* Reset off */
49         setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
50         /* Clock on */
51         setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
52
53         lcdc_init(lcdc);
54         sunxi_lcdc_config_pinmux();
55         lcdc_pll_set(ccm, 0, edid->pixelclock.typ / 1000,
56                      &clk_div, &clk_double, false);
57         lcdc_tcon0_mode_set(lcdc, edid, clk_div, false,
58                             priv->panel_bpp, CONFIG_VIDEO_LCD_DCLK_PHASE);
59         lcdc_enable(lcdc, priv->panel_bpp);
60
61         ret = uclass_get_device(UCLASS_PANEL_BACKLIGHT, 0, &backlight);
62         if (!ret)
63                 backlight_enable(backlight);
64
65         return 0;
66 }
67
68 static int sunxi_lcd_read_timing(struct udevice *dev,
69                                  struct display_timing *timing)
70 {
71         struct sunxi_lcd_priv *priv = dev_get_priv(dev);
72
73         memcpy(timing, &priv->timing, sizeof(struct display_timing));
74
75         return 0;
76 }
77
78 static int sunxi_lcd_probe(struct udevice *dev)
79 {
80         struct udevice *cdev;
81         struct sunxi_lcd_priv *priv = dev_get_priv(dev);
82         int ret;
83         int node, timing_node, val;
84
85 #ifdef CONFIG_VIDEO_BRIDGE
86         /* Try to get timings from bridge first */
87         ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &cdev);
88         if (!ret) {
89                 u8 edid[EDID_SIZE];
90                 int channel_bpp;
91
92                 ret = video_bridge_attach(cdev);
93                 if (ret) {
94                         debug("video bridge attach failed: %d\n", ret);
95                         return ret;
96                 }
97                 ret = video_bridge_read_edid(cdev, edid, EDID_SIZE);
98                 if (ret > 0) {
99                         ret = edid_get_timing(edid, ret,
100                                               &priv->timing, &channel_bpp);
101                         priv->panel_bpp = channel_bpp * 3;
102                         if (!ret)
103                                 return ret;
104                 }
105         }
106 #endif
107
108         /* Fallback to timings from DT if there's no bridge or
109          * if reading EDID failed
110          */
111         ret = uclass_get_device(UCLASS_PANEL, 0, &cdev);
112         if (ret) {
113                 debug("video panel not found: %d\n", ret);
114                 return ret;
115         }
116
117         if (fdtdec_decode_display_timing(gd->fdt_blob, dev_of_offset(cdev),
118                                          0, &priv->timing)) {
119                 debug("%s: Failed to decode display timing\n", __func__);
120                 return -EINVAL;
121         }
122         timing_node = fdt_subnode_offset(gd->fdt_blob, dev_of_offset(cdev),
123                                          "display-timings");
124         node = fdt_first_subnode(gd->fdt_blob, timing_node);
125         val = fdtdec_get_int(gd->fdt_blob, node, "bits-per-pixel", -1);
126         if (val != -1)
127                 priv->panel_bpp = val;
128         else
129                 priv->panel_bpp = 18;
130
131         return 0;
132 }
133
134 static const struct dm_display_ops sunxi_lcd_ops = {
135         .read_timing = sunxi_lcd_read_timing,
136         .enable = sunxi_lcd_enable,
137 };
138
139 U_BOOT_DRIVER(sunxi_lcd) = {
140         .name   = "sunxi_lcd",
141         .id     = UCLASS_DISPLAY,
142         .ops    = &sunxi_lcd_ops,
143         .probe  = sunxi_lcd_probe,
144         .priv_auto_alloc_size = sizeof(struct sunxi_lcd_priv),
145 };
146
147 #ifdef CONFIG_MACH_SUN50I
148 U_BOOT_DEVICE(sunxi_lcd) = {
149         .name = "sunxi_lcd"
150 };
151 #endif