xref: /linux/drivers/gpu/drm/bridge/microchip-lvds.c (revision 4b99990cdf9560e8a071640baf19f312e6ae02f4)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2023 Microchip Technology Inc. and its subsidiaries
4  *
5  * Author: Manikandan Muralidharan <manikandan.m@microchip.com>
6  * Author: Dharma Balasubiramani <dharma.b@microchip.com>
7  *
8  */
9 
10 #include <linux/clk.h>
11 #include <linux/component.h>
12 #include <linux/delay.h>
13 #include <linux/jiffies.h>
14 #include <linux/media-bus-format.h>
15 #include <linux/mfd/syscon.h>
16 #include <linux/of_graph.h>
17 #include <linux/pinctrl/devinfo.h>
18 #include <linux/phy/phy.h>
19 #include <linux/platform_device.h>
20 #include <linux/pm_runtime.h>
21 #include <linux/regmap.h>
22 #include <linux/reset.h>
23 
24 #include <drm/drm_atomic_helper.h>
25 #include <drm/drm_bridge.h>
26 #include <drm/drm_of.h>
27 #include <drm/drm_print.h>
28 #include <drm/drm_probe_helper.h>
29 #include <drm/drm_simple_kms_helper.h>
30 
31 #define LVDS_POLL_TIMEOUT_MS 1000
32 
33 /* LVDSC register offsets */
34 #define LVDSC_CR	0x00
35 #define LVDSC_CFGR	0x04
36 #define LVDSC_SR	0x0C
37 #define LVDSC_WPMR	0xE4
38 
39 /* Bitfields in LVDSC_CR (Control Register) */
40 #define LVDSC_CR_SER_EN	BIT(0)
41 
42 /* Bitfields in LVDSC_CFGR (Configuration Register) */
43 #define LVDSC_CFGR_PIXSIZE_24BITS	0
44 #define LVDSC_CFGR_PIXSIZE_18BITS	BIT(0)
45 #define LVDSC_CFGR_DEN_POL_HIGH		0
46 #define LVDSC_CFGR_DC_UNBALANCED	0
47 #define LVDSC_CFGR_MAPPING_JEIDA	BIT(6)
48 #define LVDSC_CFGR_MAPPING_VESA		0
49 
50 /*Bitfields in LVDSC_SR */
51 #define LVDSC_SR_CS	BIT(0)
52 
53 /* Bitfields in LVDSC_WPMR (Write Protection Mode Register) */
54 #define LVDSC_WPMR_WPKEY_MASK	GENMASK(31, 8)
55 #define LVDSC_WPMR_WPKEY_PSSWD	0x4C5644
56 
57 struct mchp_lvds {
58 	struct device *dev;
59 	void __iomem *regs;
60 	struct clk *pclk;
61 	struct drm_bridge bridge;
62 	struct drm_bridge *panel_bridge;
63 };
64 
65 static inline struct mchp_lvds *bridge_to_lvds(struct drm_bridge *bridge)
66 {
67 	return container_of(bridge, struct mchp_lvds, bridge);
68 }
69 
70 static inline u32 lvds_readl(struct mchp_lvds *lvds, u32 offset)
71 {
72 	return readl_relaxed(lvds->regs + offset);
73 }
74 
75 static inline void lvds_writel(struct mchp_lvds *lvds, u32 offset, u32 val)
76 {
77 	writel_relaxed(val, lvds->regs + offset);
78 }
79 
80 static void lvds_serialiser_on(struct mchp_lvds *lvds, u32 bus_format)
81 {
82 	unsigned long timeout = jiffies + msecs_to_jiffies(LVDS_POLL_TIMEOUT_MS);
83 	u8 map, pix_size;
84 
85 	/* The LVDSC registers can only be written if WPEN is cleared */
86 	lvds_writel(lvds, LVDSC_WPMR, (LVDSC_WPMR_WPKEY_PSSWD &
87 				LVDSC_WPMR_WPKEY_MASK));
88 
89 	/* Wait for the status of configuration registers to be changed */
90 	while (lvds_readl(lvds, LVDSC_SR) & LVDSC_SR_CS) {
91 		if (time_after(jiffies, timeout)) {
92 			dev_err(lvds->dev, "%s: timeout error\n", __func__);
93 			return;
94 		}
95 		usleep_range(1000, 2000);
96 	}
97 
98 	switch (bus_format) {
99 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
100 		map = LVDSC_CFGR_MAPPING_JEIDA;
101 		pix_size = LVDSC_CFGR_PIXSIZE_18BITS;
102 		break;
103 	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
104 		map = LVDSC_CFGR_MAPPING_VESA;
105 		pix_size = LVDSC_CFGR_PIXSIZE_24BITS;
106 		break;
107 	default:
108 		map = LVDSC_CFGR_MAPPING_JEIDA;
109 		pix_size = LVDSC_CFGR_PIXSIZE_24BITS;
110 		break;
111 	}
112 
113 	/* Configure the LVDSC */
114 	lvds_writel(lvds, LVDSC_CFGR, map | LVDSC_CFGR_DC_UNBALANCED |
115 		    LVDSC_CFGR_DEN_POL_HIGH | pix_size);
116 
117 	/* Enable the LVDS serializer */
118 	lvds_writel(lvds, LVDSC_CR, LVDSC_CR_SER_EN);
119 }
120 
121 static int mchp_lvds_attach(struct drm_bridge *bridge,
122 			    struct drm_encoder *encoder,
123 			    enum drm_bridge_attach_flags flags)
124 {
125 	struct mchp_lvds *lvds = bridge_to_lvds(bridge);
126 
127 	return drm_bridge_attach(encoder, lvds->panel_bridge,
128 				 bridge, flags);
129 }
130 
131 static void mchp_lvds_atomic_enable(struct drm_bridge *bridge,
132 				    struct drm_atomic_commit *state)
133 {
134 	struct mchp_lvds *lvds = bridge_to_lvds(bridge);
135 	struct drm_connector *connector;
136 	int ret;
137 
138 	ret = clk_prepare_enable(lvds->pclk);
139 	if (ret < 0) {
140 		dev_err(lvds->dev, "failed to enable lvds pclk %d\n", ret);
141 		return;
142 	}
143 
144 	ret = pm_runtime_get_sync(lvds->dev);
145 	if (ret < 0) {
146 		dev_err(lvds->dev, "failed to get pm runtime: %d\n", ret);
147 		return;
148 	}
149 
150 	/* default to jeida-24 */
151 	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
152 
153 	connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
154 	if (connector && connector->display_info.num_bus_formats)
155 		bus_format = connector->display_info.bus_formats[0];
156 
157 	lvds_serialiser_on(lvds, bus_format);
158 }
159 
160 static void mchp_lvds_atomic_disable(struct drm_bridge *bridge,
161 				     struct drm_atomic_commit *state)
162 {
163 	struct mchp_lvds *lvds = bridge_to_lvds(bridge);
164 
165 	pm_runtime_put(lvds->dev);
166 	clk_disable_unprepare(lvds->pclk);
167 }
168 
169 static const struct drm_bridge_funcs mchp_lvds_bridge_funcs = {
170 	.attach = mchp_lvds_attach,
171 	.atomic_enable = mchp_lvds_atomic_enable,
172 	.atomic_disable = mchp_lvds_atomic_disable,
173 };
174 
175 static int mchp_lvds_probe(struct platform_device *pdev)
176 {
177 	struct device *dev = &pdev->dev;
178 	struct mchp_lvds *lvds;
179 	int ret;
180 
181 	if (!dev->of_node)
182 		return -ENODEV;
183 
184 	lvds = devm_drm_bridge_alloc(&pdev->dev, struct mchp_lvds, bridge,
185 				     &mchp_lvds_bridge_funcs);
186 	if (IS_ERR(lvds))
187 		return PTR_ERR(lvds);
188 
189 	lvds->dev = dev;
190 
191 	lvds->regs = devm_platform_ioremap_resource(pdev, 0);
192 	if (IS_ERR(lvds->regs))
193 		return PTR_ERR(lvds->regs);
194 
195 	lvds->pclk = devm_clk_get(lvds->dev, "pclk");
196 	if (IS_ERR(lvds->pclk))
197 		return dev_err_probe(lvds->dev, PTR_ERR(lvds->pclk),
198 				"could not get pclk_lvds\n");
199 
200 	lvds->panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
201 
202 	if (IS_ERR(lvds->panel_bridge))
203 		return PTR_ERR(lvds->panel_bridge);
204 
205 	lvds->bridge.of_node = dev->of_node;
206 	lvds->bridge.type = DRM_MODE_CONNECTOR_LVDS;
207 
208 	dev_set_drvdata(dev, lvds);
209 	ret = devm_pm_runtime_enable(dev);
210 	if (ret < 0) {
211 		dev_err(lvds->dev, "failed to enable pm runtime: %d\n", ret);
212 		return ret;
213 	}
214 
215 	drm_bridge_add(&lvds->bridge);
216 
217 	return 0;
218 }
219 
220 static const struct of_device_id mchp_lvds_dt_ids[] = {
221 	{
222 		.compatible = "microchip,sam9x75-lvds",
223 	},
224 	{},
225 };
226 MODULE_DEVICE_TABLE(of, mchp_lvds_dt_ids);
227 
228 static struct platform_driver mchp_lvds_driver = {
229 	.probe = mchp_lvds_probe,
230 	.driver = {
231 		   .name = "microchip-lvds",
232 		   .of_match_table = mchp_lvds_dt_ids,
233 	},
234 };
235 module_platform_driver(mchp_lvds_driver);
236 
237 MODULE_AUTHOR("Manikandan Muralidharan <manikandan.m@microchip.com>");
238 MODULE_AUTHOR("Dharma Balasubiramani <dharma.b@microchip.com>");
239 MODULE_DESCRIPTION("Low Voltage Differential Signaling Controller Driver");
240 MODULE_LICENSE("GPL");
241