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