aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/layerscape/patches-5.4/805-display-0056-LF-794-3-gpu-cdn-imx8qm-Add-firmware-loading-support.patch
blob: ca7f488b8b31e5d36f25d6e89d7b1091c59c40ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
From 020578dd022e5d869db52e79c5aba95c1f1a84ec Mon Sep 17 00:00:00 2001
From: Abel Vesa <abel.vesa@nxp.com>
Date: Wed, 11 Dec 2019 09:21:23 +0200
Subject: [PATCH] LF-794-3 gpu: cdn: imx8qm: Add firmware loading support

This allows the  HDP i.MX8QM driver to load the firmware on init
and resume. In order to have backward compatibility, if there is
no firmware-name property defined in the hdmi node, the driver
probing sequence skips the firmware loading.

Also, if u-boot has loaded already a firmware, we run with that
but when probing the driver, the request_firmware_nowait is used
to locate and keep safe the firmware for when suspend/resume happens.

This leads to 4 possible scenarios:

1. u-boot loads the firmware, the kernel driver finds the firmware
when rootfs is mounted. This is the most desirable scenario. Also
this is the only scenario that allows the hdmi to work after resume.

2. u-boot loads the firmware, the kernel driver _doesn't_ find
the firmware in rootfs. If there is no suspend ever happening,
the kernel driver will keep using the firmware that was loaded by
u-boot. On the first suspend/resume, the firmware is lost
because the HDMI IP gets powered down.

3. u-boot doesn't load the firmare, the kernel driver probing
tries to load the firmware, assuming this is available
(see CONFIG_EXTRA_FIRMWARE).

4. u-boot doesn't load the firmware and the kernel driver is not
able to find it either. The probing fails and there is no HDMI
available in linux.

Signed-off-by: Abel Vesa <abel.vesa@nxp.com>
Reviewed-by: Sandor Yu <sandor.yu@nxp.com>
Acked-by: Wen He <wen.he_1@nxp.com>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
---
 drivers/gpu/drm/imx/cdn-mhdp-imx8qm.c | 78 +++++++++++++++++++++++++++++++++--
 drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c | 30 ++++++++++++++
 drivers/gpu/drm/imx/cdns-mhdp-imx.h   |  4 ++
 include/drm/bridge/cdns-mhdp-common.h |  3 ++
 4 files changed, 111 insertions(+), 4 deletions(-)

--- a/drivers/gpu/drm/imx/cdn-mhdp-imx8qm.c
+++ b/drivers/gpu/drm/imx/cdn-mhdp-imx8qm.c
@@ -7,12 +7,17 @@
  */
 #include <dt-bindings/firmware/imx/rsrc.h>
 #include <linux/firmware/imx/sci.h>
+#include <linux/firmware.h>
 #include <linux/pm_domain.h>
 #include <linux/clk.h>
 #include <drm/drmP.h>
 
 #include "cdns-mhdp-imx.h"
 
+#define FW_IRAM_OFFSET		0x2000
+#define FW_IRAM_SIZE		0x10000
+#define FW_DRAM_SIZE		0x8000
+
 #define PLL_800MHZ (800000000)
 
 #define HDP_DUAL_MODE_MIN_PCLK_RATE	300000	/* KHz */
@@ -517,24 +522,69 @@ void cdns_mhdp_pclk_rate_imx8qm(struct c
 	imx8qm_pixel_link_mux(imx_mhdp);
 }
 
-int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp)
+static void cdns_mhdp_firmware_load_cont(const struct firmware *fw, void *context)
 {
-	struct imx_mhdp_device *imx_mhdp =
-				container_of(mhdp, struct imx_mhdp_device, mhdp);
+	struct imx_mhdp_device *imx_mhdp = context;
+
+	imx_mhdp->fw = fw;
+}
+
+static int cdns_mhdp_load_firmware_imx8qm(struct imx_mhdp_device *imx_mhdp)
+{
+	const u8 *iram;
+	const u8 *dram;
 	u32 rate;
 	int ret;
 
 	/* configure HDMI/DP core clock */
 	rate = clk_get_rate(imx_mhdp->clks.clk_core);
-	if (mhdp->is_ls1028a)
+	if (imx_mhdp->mhdp.is_ls1028a)
 		rate = rate / 4;
 
 	cdns_mhdp_set_fw_clk(&imx_mhdp->mhdp, rate);
 
+	/* skip fw loading if none is specified */
+	if (!imx_mhdp->firmware_name)
+		goto out;
+
+	if (!imx_mhdp->fw) {
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
+						imx_mhdp->firmware_name,
+						imx_mhdp->mhdp.dev, GFP_KERNEL,
+						imx_mhdp,
+						cdns_mhdp_firmware_load_cont);
+		if (ret < 0) {
+			DRM_ERROR("failed to load firmware\n");
+			return -ENOENT;
+		}
+	} else {
+		iram = imx_mhdp->fw->data + FW_IRAM_OFFSET;
+		dram = iram + FW_IRAM_SIZE;
+
+		cdns_mhdp_load_firmware(&imx_mhdp->mhdp,
+					(const u32 *) iram, FW_IRAM_SIZE,
+					(const u32 *) dram, FW_DRAM_SIZE);
+	}
+
+out:
 	/* un-reset ucpu */
 	cdns_mhdp_bus_write(0, &imx_mhdp->mhdp, APB_CTRL);
 	DRM_INFO("Started firmware!\n");
 
+	return 0;
+}
+
+int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp)
+{
+	struct imx_mhdp_device *imx_mhdp =
+				container_of(mhdp, struct imx_mhdp_device, mhdp);
+	int ret;
+
+	/* load firmware */
+	ret = cdns_mhdp_load_firmware_imx8qm(imx_mhdp);
+	if (ret)
+		return ret;
+
 	ret = cdns_mhdp_check_alive(&imx_mhdp->mhdp);
 	if (ret == false) {
 		DRM_ERROR("NO HDMI FW running\n");
@@ -550,3 +600,23 @@ int cdns_mhdp_firmware_init_imx8qm(struc
 
 	return 0;
 }
+
+int cdns_mhdp_suspend_imx8qm(struct cdns_mhdp_device *mhdp)
+{
+	struct imx_mhdp_device *imx_mhdp =
+				container_of(mhdp, struct imx_mhdp_device, mhdp);
+
+	imx8qm_pixel_clk_disable(imx_mhdp);
+
+	return 0;
+}
+
+int cdns_mhdp_resume_imx8qm(struct cdns_mhdp_device *mhdp)
+{
+	struct imx_mhdp_device *imx_mhdp =
+				container_of(mhdp, struct imx_mhdp_device, mhdp);
+
+	imx8qm_pixel_clk_enable(imx_mhdp);
+
+	return cdns_mhdp_firmware_init_imx8qm(mhdp);
+}
--- a/drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c
+++ b/drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c
@@ -82,6 +82,8 @@ static struct cdns_plat_data imx8qm_hdmi
 	.phy_video_valid = cdns_hdmi_phy_video_valid_imx8qm,
 	.power_on = cdns_mhdp_power_on_imx8qm,
 	.firmware_init = cdns_mhdp_firmware_init_imx8qm,
+	.resume = cdns_mhdp_resume_imx8qm,
+	.suspend = cdns_mhdp_suspend_imx8qm,
 	.pclk_rate = cdns_mhdp_pclk_rate_imx8qm,
 	.plat_init = cdns_mhdp_plat_init_imx8qm,
 	.plat_deinit = cdns_mhdp_plat_deinit_imx8qm,
@@ -96,6 +98,8 @@ static struct cdns_plat_data imx8qm_dp_d
 	.phy_set = cdns_dp_phy_set_imx8qm,
 	.power_on = cdns_mhdp_power_on_imx8qm,
 	.firmware_init = cdns_mhdp_firmware_init_imx8qm,
+	.resume = cdns_mhdp_resume_imx8qm,
+	.suspend = cdns_mhdp_suspend_imx8qm,
 	.pclk_rate = cdns_mhdp_pclk_rate_imx8qm,
 	.plat_init = cdns_mhdp_plat_init_imx8qm,
 	.plat_deinit = cdns_mhdp_plat_deinit_imx8qm,
@@ -157,6 +161,9 @@ static int cdns_mhdp_imx_bind(struct dev
 	encoder = &imx_mhdp->encoder;
 
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	ret = of_property_read_string(pdev->dev.of_node, "firmware-name",
+					&imx_mhdp->firmware_name);
 	/*
 	 * If we failed to find the CRTC(s) which this encoder is
 	 * supposed to be connected to, it's because the CRTC has
@@ -198,6 +205,24 @@ static const struct component_ops cdns_m
 	.unbind	= cdns_mhdp_imx_unbind,
 };
 
+static int cdns_mhdp_imx_suspend(struct device *dev)
+{
+	struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev);
+
+	cdns_mhdp_plat_call(&imx_mhdp->mhdp, suspend);
+
+	return 0;
+}
+
+static int cdns_mhdp_imx_resume(struct device *dev)
+{
+	struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev);
+
+	cdns_mhdp_plat_call(&imx_mhdp->mhdp, resume);
+
+	return 0;
+}
+
 static int cdns_mhdp_imx_probe(struct platform_device *pdev)
 {
 	return component_add(&pdev->dev, &cdns_mhdp_imx_ops);
@@ -210,12 +235,17 @@ static int cdns_mhdp_imx_remove(struct p
 	return 0;
 }
 
+static const struct dev_pm_ops cdns_mhdp_imx_pm_ops = {
+        SET_LATE_SYSTEM_SLEEP_PM_OPS(cdns_mhdp_imx_suspend, cdns_mhdp_imx_resume)
+};
+
 static struct platform_driver cdns_mhdp_imx_platform_driver = {
 	.probe  = cdns_mhdp_imx_probe,
 	.remove = cdns_mhdp_imx_remove,
 	.driver = {
 		.name = "cdns-mhdp-imx",
 		.of_match_table = cdns_mhdp_imx_dt_ids,
+		.pm = &cdns_mhdp_imx_pm_ops,
 	},
 };
 
--- a/drivers/gpu/drm/imx/cdns-mhdp-imx.h
+++ b/drivers/gpu/drm/imx/cdns-mhdp-imx.h
@@ -50,6 +50,8 @@ struct imx_mhdp_device {
 	bool active;
 	bool suspended;
 	struct imx_hdp_clks clks;
+	const struct firmware *fw;
+	const char *firmware_name;
 
 	int bus_type;
 
@@ -65,6 +67,8 @@ void cdns_mhdp_plat_init_imx8qm(struct c
 void cdns_mhdp_plat_deinit_imx8qm(struct cdns_mhdp_device *mhdp);
 void cdns_mhdp_pclk_rate_imx8qm(struct cdns_mhdp_device *mhdp);
 int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp);
+int cdns_mhdp_resume_imx8qm(struct cdns_mhdp_device *mhdp);
+int cdns_mhdp_suspend_imx8qm(struct cdns_mhdp_device *mhdp);
 int cdns_mhdp_power_on_imx8qm(struct cdns_mhdp_device *mhdp);
 int cdns_mhdp_power_on_ls1028a(struct cdns_mhdp_device *mhdp);
 void cdns_mhdp_pclk_rate_ls1028a(struct cdns_mhdp_device *mhdp);
--- a/include/drm/bridge/cdns-mhdp-common.h
+++ b/include/drm/bridge/cdns-mhdp-common.h
@@ -645,6 +645,9 @@ struct cdns_plat_data {
 	int (*firmware_init)(struct cdns_mhdp_device *mhdp);
 	void (*pclk_rate)(struct cdns_mhdp_device *mhdp);
 
+	int (*suspend)(struct cdns_mhdp_device *mhdp);
+	int (*resume)(struct cdns_mhdp_device *mhdp);
+
 	int (*power_on)(struct cdns_mhdp_device *mhdp);
 	int (*power_off)(struct cdns_mhdp_device *mhdp);