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
20struct 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
27static 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
40static 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
53int 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
62static 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
71static 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
81static 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
102static 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
152static 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
158static const struct udevice_id tilcdc_panel_ids[] = {
159	{.compatible = "ti,tilcdc,panel"},
160	{}
161};
162
163U_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