1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * OMAP panel support
4 *
5 * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
6 */
7
8 #include <common.h>
9 #include <backlight.h>
10 #include <clk.h>
11 #include <display.h>
12 #include <dm.h>
13 #include <dm/device_compat.h>
14 #include <log.h>
15 #include <panel.h>
16 #include <asm/gpio.h>
17 #include <linux/err.h>
18 #include "tilcdc.h"
19
20 struct tilcdc_panel_priv {
21 struct tilcdc_panel_info info;
22 struct display_timing timing;
23 struct udevice *backlight;
24 struct gpio_desc enable;
25 };
26
tilcdc_panel_enable_backlight(struct udevice * dev)27 static int tilcdc_panel_enable_backlight(struct udevice *dev)
28 {
29 struct tilcdc_panel_priv *priv = dev_get_priv(dev);
30
31 if (dm_gpio_is_valid(&priv->enable))
32 dm_gpio_set_value(&priv->enable, 1);
33
34 if (priv->backlight)
35 return backlight_enable(priv->backlight);
36
37 return 0;
38 }
39
tilcdc_panel_set_backlight(struct udevice * dev,int percent)40 static int tilcdc_panel_set_backlight(struct udevice *dev, int percent)
41 {
42 struct tilcdc_panel_priv *priv = dev_get_priv(dev);
43
44 if (dm_gpio_is_valid(&priv->enable))
45 dm_gpio_set_value(&priv->enable, 1);
46
47 if (priv->backlight)
48 return backlight_set_brightness(priv->backlight, percent);
49
50 return 0;
51 }
52
tilcdc_panel_get_display_info(struct udevice * dev,struct tilcdc_panel_info * info)53 int tilcdc_panel_get_display_info(struct udevice *dev,
54 struct tilcdc_panel_info *info)
55 {
56 struct tilcdc_panel_priv *priv = dev_get_priv(dev);
57
58 memcpy(info, &priv->info, sizeof(*info));
59 return 0;
60 }
61
tilcdc_panel_get_display_timing(struct udevice * dev,struct display_timing * timing)62 static int tilcdc_panel_get_display_timing(struct udevice *dev,
63 struct display_timing *timing)
64 {
65 struct tilcdc_panel_priv *priv = dev_get_priv(dev);
66
67 memcpy(timing, &priv->timing, sizeof(*timing));
68 return 0;
69 }
70
tilcdc_panel_remove(struct udevice * dev)71 static int tilcdc_panel_remove(struct udevice *dev)
72 {
73 struct tilcdc_panel_priv *priv = dev_get_priv(dev);
74
75 if (dm_gpio_is_valid(&priv->enable))
76 dm_gpio_free(dev, &priv->enable);
77
78 return 0;
79 }
80
tilcdc_panel_probe(struct udevice * dev)81 static int tilcdc_panel_probe(struct udevice *dev)
82 {
83 struct tilcdc_panel_priv *priv = dev_get_priv(dev);
84 int err;
85
86 err = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
87 "backlight", &priv->backlight);
88 if (err)
89 dev_warn(dev, "failed to get backlight\n");
90
91 err = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable,
92 GPIOD_IS_OUT);
93 if (err) {
94 dev_warn(dev, "failed to get enable GPIO\n");
95 if (err != -ENOENT)
96 return err;
97 }
98
99 return 0;
100 }
101
tilcdc_panel_of_to_plat(struct udevice * dev)102 static int tilcdc_panel_of_to_plat(struct udevice *dev)
103 {
104 struct tilcdc_panel_priv *priv = dev_get_priv(dev);
105 ofnode node;
106 int err;
107
108 err = ofnode_decode_display_timing(dev_ofnode(dev), 0, &priv->timing);
109 if (err) {
110 dev_err(dev, "failed to get display timing\n");
111 return err;
112 }
113
114 node = dev_read_subnode(dev, "panel-info");
115 if (!ofnode_valid(node)) {
116 dev_err(dev, "missing 'panel-info' node\n");
117 return -ENXIO;
118 }
119
120 err |= ofnode_read_u32(node, "ac-bias", &priv->info.ac_bias);
121 err |= ofnode_read_u32(node, "ac-bias-intrpt",
122 &priv->info.ac_bias_intrpt);
123 err |= ofnode_read_u32(node, "dma-burst-sz", &priv->info.dma_burst_sz);
124 err |= ofnode_read_u32(node, "bpp", &priv->info.bpp);
125 err |= ofnode_read_u32(node, "fdd", &priv->info.fdd);
126 err |= ofnode_read_u32(node, "sync-edge", &priv->info.sync_edge);
127 err |= ofnode_read_u32(node, "sync-ctrl", &priv->info.sync_ctrl);
128 err |= ofnode_read_u32(node, "raster-order", &priv->info.raster_order);
129 err |= ofnode_read_u32(node, "fifo-th", &priv->info.fifo_th);
130 if (err) {
131 dev_err(dev, "failed to get panel info\n");
132 return err;
133 }
134
135 /* optional */
136 priv->info.tft_alt_mode = ofnode_read_bool(node, "tft-alt-mode");
137 priv->info.invert_pxl_clk = ofnode_read_bool(node, "invert-pxl-clk");
138
139 dev_dbg(dev, "LCD: %dx%d, bpp=%d, clk=%d Hz\n",
140 priv->timing.hactive.typ, priv->timing.vactive.typ,
141 priv->info.bpp, priv->timing.pixelclock.typ);
142 dev_dbg(dev, " hbp=%d, hfp=%d, hsw=%d\n",
143 priv->timing.hback_porch.typ, priv->timing.hfront_porch.typ,
144 priv->timing.hsync_len.typ);
145 dev_dbg(dev, " vbp=%d, vfp=%d, vsw=%d\n",
146 priv->timing.vback_porch.typ, priv->timing.vfront_porch.typ,
147 priv->timing.vsync_len.typ);
148
149 return 0;
150 }
151
152 static const struct panel_ops tilcdc_panel_ops = {
153 .enable_backlight = tilcdc_panel_enable_backlight,
154 .set_backlight = tilcdc_panel_set_backlight,
155 .get_display_timing = tilcdc_panel_get_display_timing,
156 };
157
158 static const struct udevice_id tilcdc_panel_ids[] = {
159 {.compatible = "ti,tilcdc,panel"},
160 {}
161 };
162
163 U_BOOT_DRIVER(tilcdc_panel) = {
164 .name = "tilcdc_panel",
165 .id = UCLASS_PANEL,
166 .of_match = tilcdc_panel_ids,
167 .ops = &tilcdc_panel_ops,
168 .of_to_plat = tilcdc_panel_of_to_plat,
169 .probe = tilcdc_panel_probe,
170 .remove = tilcdc_panel_remove,
171 .priv_auto = sizeof(struct tilcdc_panel_priv),
172 };
173