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