summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Wu <jason.wu.misc@gmail.com>2016-04-11 00:14:17 +1000
committerNathan Rossi <nathan@nathanrossi.com>2016-05-03 21:51:05 +1000
commitb2261ffc3dc58f317c8b8aa290d4c36f53d5a176 (patch)
tree88448a2728282e11fb578eb9037b699e14508cf9
parent5066fef048366ced30a346e2dca67b009f0d32be (diff)
downloadmeta-xilinx-b2261ffc3dc58f317c8b8aa290d4c36f53d5a176.tar.gz
linux-xlnx: Add Digilent encoder and axi dynclk drivers
Add to both linux-xlnx_4.0.bb and linux-xlnx_4.4.bb. Signed-off-by: Jason Wu <jason.wu.misc@gmail.com> Signed-off-by: Nathan Rossi <nathan@nathanrossi.com>
-rw-r--r--recipes-kernel/linux/linux-xlnx/0001-drm-xilinx-Add-encoder-for-Digilent-boards.patch294
-rw-r--r--recipes-kernel/linux/linux-xlnx/0002-clk-Add-driver-for-axi_dynclk-IP-Core.patch601
-rw-r--r--recipes-kernel/linux/linux-xlnx_4.0.bb4
-rw-r--r--recipes-kernel/linux/linux-xlnx_4.4.bb4
4 files changed, 903 insertions, 0 deletions
diff --git a/recipes-kernel/linux/linux-xlnx/0001-drm-xilinx-Add-encoder-for-Digilent-boards.patch b/recipes-kernel/linux/linux-xlnx/0001-drm-xilinx-Add-encoder-for-Digilent-boards.patch
new file mode 100644
index 00000000..892455af
--- /dev/null
+++ b/recipes-kernel/linux/linux-xlnx/0001-drm-xilinx-Add-encoder-for-Digilent-boards.patch
@@ -0,0 +1,294 @@
1From 3d1820b308dcc9344d8e7df4a8a712632fb77b97 Mon Sep 17 00:00:00 2001
2Message-Id: <3d1820b308dcc9344d8e7df4a8a712632fb77b97.1460291687.git.jason.wu.misc@gmail.com>
3From: Jason Wu <jason.wu.misc@gmail.com>
4Date: Sun, 10 Apr 2016 13:14:13 +1000
5Subject: [LINUX] [PATCH] drm: xilinx: Add encoder for Digilent boards
6
7Add the dglnt_encoder driver that enables DRM support for the VGA and
8HDMI output ports found on many Digilent boards.
9
10Upstream-Status: Pending
11
12Signed-off-by: Sam Bobrowicz <sbobrowicz@digilentinc.com>
13Signed-off-by: Jason Wu <jason.wu.misc@gmail.com>
14---
15github.com () linux-xlnx.git xlnx/master
16
17diff --git a/Documentation/devicetree/bindings/drm/xilinx/dglnt_encoder.txt b/Documentation/devicetree/bindings/drm/xilinx/dglnt_encoder.txt
18new file mode 100644
19index 0000000..242b24e
20--- /dev/null
21+++ b/Documentation/devicetree/bindings/drm/xilinx/dglnt_encoder.txt
22@@ -0,0 +1,23 @@
23+Device-Tree bindings for Digilent DRM Encoder Slave
24+
25+This driver provides support for VGA and HDMI outputs on Digilent FPGA boards.
26+The VGA or HDMI port must be connected to a Xilinx display pipeline via an
27+axi2vid IP core.
28+
29+Required properties:
30+ - compatible: Should be "digilent,drm-encoder".
31+
32+Optional properties:
33+ - dglnt,edid-i2c: The I2C device connected to the DDC bus on the video
34+ connector. This is used to obtain the supported resolutions
35+ of an attached monitor. If not defined, then a default
36+ set of resolutions is used and the display will initialize
37+ to 720p. Note most VGA connectors on Digilent boards do
38+ not have the DDC bus routed out.
39+
40+Example:
41+
42+ encoder_0: digilent_encoder {
43+ compatible = "digilent,drm-encoder";
44+ dglnt,edid-i2c = <&i2c1>;
45+ };
46diff --git a/drivers/gpu/drm/xilinx/Kconfig b/drivers/gpu/drm/xilinx/Kconfig
47index a713b17..c32a4a6 100644
48--- a/drivers/gpu/drm/xilinx/Kconfig
49+++ b/drivers/gpu/drm/xilinx/Kconfig
50@@ -21,3 +21,9 @@ config DRM_XILINX_DP_SUB
51 select DRM_XILINX_DP
52 help
53 DRM driver for Xilinx Display Port Subsystem.
54+
55+config DRM_DIGILENT_ENCODER
56+ tristate "Digilent VGA/HDMI DRM Encoder Driver"
57+ depends on DRM_XILINX
58+ help
59+ DRM slave encoder for Video-out on Digilent boards.
60diff --git a/drivers/gpu/drm/xilinx/Makefile b/drivers/gpu/drm/xilinx/Makefile
61index 705472c..a571bd9 100644
62--- a/drivers/gpu/drm/xilinx/Makefile
63+++ b/drivers/gpu/drm/xilinx/Makefile
64@@ -10,3 +10,4 @@ xilinx_drm-y += xilinx_cresample.o xilinx_osd.o xilinx_rgb2yuv.o xilinx_vtc.o
65 obj-$(CONFIG_DRM_XILINX) += xilinx_drm.o
66 obj-$(CONFIG_DRM_XILINX_DP) += xilinx_drm_dp.o
67 obj-$(CONFIG_DRM_XILINX_DP_SUB) += xilinx_drm_dp_sub.o
68+obj-$(CONFIG_DRM_DIGILENT_ENCODER) += dglnt_encoder.o
69diff --git a/drivers/gpu/drm/xilinx/dglnt_encoder.c b/drivers/gpu/drm/xilinx/dglnt_encoder.c
70new file mode 100644
71index 0000000..26a2398
72--- /dev/null
73+++ b/drivers/gpu/drm/xilinx/dglnt_encoder.c
74@@ -0,0 +1,217 @@
75+/*
76+ * dglnt_encoder.c - DRM slave encoder for Video-out on Digilent boards
77+ *
78+ * Copyright (C) 2015 Digilent
79+ * Author: Sam Bobrowicz <sbobrowicz@digilentinc.com>
80+ *
81+ * Based on udl_encoder.c and udl_connector.c, Copyright (C) 2012 Red Hat.
82+ * Also based on xilinx_drm_dp.c, Copyright (C) 2014 Xilinx, Inc.
83+ *
84+ * This software is licensed under the terms of the GNU General Public
85+ * License version 2, as published by the Free Software Foundation, and
86+ * may be copied, distributed, and modified under those terms.
87+ *
88+ * This program is distributed in the hope that it will be useful,
89+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
90+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
91+ * GNU General Public License for more details.
92+ */
93+
94+#include <drm/drmP.h>
95+#include <drm/drm_edid.h>
96+#include <drm/drm_encoder_slave.h>
97+
98+#include <linux/device.h>
99+#include <linux/module.h>
100+#include <linux/err.h>
101+#include <linux/i2c.h>
102+#include <linux/of.h>
103+#include <linux/of_platform.h>
104+#include <linux/platform_device.h>
105+
106+#define DGLNT_ENC_MAX_FREQ 150000
107+#define DGLNT_ENC_MAX_H 1920
108+#define DGLNT_ENC_MAX_V 1080
109+#define DGLNT_ENC_PREF_H 1280
110+#define DGLNT_ENC_PREF_V 720
111+
112+struct dglnt_encoder {
113+ struct drm_encoder *encoder;
114+ struct i2c_adapter *i2c_bus;
115+ bool i2c_present;
116+};
117+
118+static inline struct dglnt_encoder *to_dglnt_encoder(
119+ struct drm_encoder *encoder)
120+{
121+ return to_encoder_slave(encoder)->slave_priv;
122+}
123+
124+static bool dglnt_mode_fixup(struct drm_encoder *encoder,
125+ const struct drm_display_mode *mode,
126+ struct drm_display_mode *adjusted_mode)
127+{
128+ return true;
129+}
130+
131+static void dglnt_encoder_mode_set(struct drm_encoder *encoder,
132+ struct drm_display_mode *mode,
133+ struct drm_display_mode *adjusted_mode)
134+{
135+}
136+
137+static void
138+dglnt_encoder_dpms(struct drm_encoder *encoder, int mode)
139+{
140+}
141+
142+static void dglnt_encoder_save(struct drm_encoder *encoder)
143+{
144+}
145+
146+static void dglnt_encoder_restore(struct drm_encoder *encoder)
147+{
148+}
149+
150+static int dglnt_encoder_mode_valid(struct drm_encoder *encoder,
151+ struct drm_display_mode *mode)
152+{
153+ if (mode &&
154+ !(mode->flags & ((DRM_MODE_FLAG_INTERLACE |
155+ DRM_MODE_FLAG_DBLCLK) | DRM_MODE_FLAG_3D_MASK)) &&
156+ (mode->clock <= DGLNT_ENC_MAX_FREQ) &&
157+ (mode->hdisplay <= DGLNT_ENC_MAX_H) &&
158+ (mode->vdisplay <= DGLNT_ENC_MAX_V))
159+ return MODE_OK;
160+ return MODE_BAD;
161+}
162+
163+static int dglnt_encoder_get_modes(struct drm_encoder *encoder,
164+ struct drm_connector *connector)
165+{
166+ struct dglnt_encoder *dglnt = to_dglnt_encoder(encoder);
167+ struct edid *edid;
168+ int num_modes = 0;
169+
170+ if (dglnt->i2c_present) {
171+ edid = drm_get_edid(connector, dglnt->i2c_bus);
172+ drm_mode_connector_update_edid_property(connector, edid);
173+ if (edid) {
174+ num_modes = drm_add_edid_modes(connector, edid);
175+ kfree(edid);
176+ }
177+ } else {
178+ num_modes = drm_add_modes_noedid(connector, DGLNT_ENC_MAX_H,
179+ DGLNT_ENC_MAX_V);
180+ drm_set_preferred_mode(connector, DGLNT_ENC_PREF_H,
181+ DGLNT_ENC_PREF_V);
182+ }
183+ return num_modes;
184+}
185+
186+static enum drm_connector_status dglnt_encoder_detect(
187+ struct drm_encoder *encoder,
188+ struct drm_connector *connector)
189+{
190+ struct dglnt_encoder *dglnt = to_dglnt_encoder(encoder);
191+
192+ if (dglnt->i2c_present) {
193+ if (drm_probe_ddc(dglnt->i2c_bus))
194+ return connector_status_connected;
195+ return connector_status_disconnected;
196+ } else
197+ return connector_status_unknown;
198+}
199+
200+static struct drm_encoder_slave_funcs dglnt_encoder_slave_funcs = {
201+ .dpms = dglnt_encoder_dpms,
202+ .save = dglnt_encoder_save,
203+ .restore = dglnt_encoder_restore,
204+ .mode_fixup = dglnt_mode_fixup,
205+ .mode_valid = dglnt_encoder_mode_valid,
206+ .mode_set = dglnt_encoder_mode_set,
207+ .detect = dglnt_encoder_detect,
208+ .get_modes = dglnt_encoder_get_modes,
209+};
210+
211+static int dglnt_encoder_encoder_init(struct platform_device *pdev,
212+ struct drm_device *dev,
213+ struct drm_encoder_slave *encoder)
214+{
215+ struct dglnt_encoder *dglnt = platform_get_drvdata(pdev);
216+ struct device_node *sub_node;
217+
218+ encoder->slave_priv = dglnt;
219+ encoder->slave_funcs = &dglnt_encoder_slave_funcs;
220+
221+ dglnt->encoder = &encoder->base;
222+
223+ /* get i2c adapter for edid */
224+ dglnt->i2c_present = false;
225+ sub_node = of_parse_phandle(pdev->dev.of_node, "dglnt,edid-i2c", 0);
226+ if (sub_node) {
227+ dglnt->i2c_bus = of_find_i2c_adapter_by_node(sub_node);
228+ if (!dglnt->i2c_bus)
229+ DRM_INFO("failed to get the edid i2c adapter, using default modes\n");
230+ else
231+ dglnt->i2c_present = true;
232+ of_node_put(sub_node);
233+ }
234+
235+ return 0;
236+}
237+
238+static int dglnt_encoder_probe(struct platform_device *pdev)
239+{
240+ struct dglnt_encoder *dglnt;
241+
242+ dglnt = devm_kzalloc(&pdev->dev, sizeof(*dglnt), GFP_KERNEL);
243+ if (!dglnt)
244+ return -ENOMEM;
245+
246+ platform_set_drvdata(pdev, dglnt);
247+
248+ return 0;
249+}
250+
251+static int dglnt_encoder_remove(struct platform_device *pdev)
252+{
253+ return 0;
254+}
255+
256+static const struct of_device_id dglnt_encoder_of_match[] = {
257+ { .compatible = "digilent,drm-encoder", },
258+ { /* end of table */ },
259+};
260+MODULE_DEVICE_TABLE(of, dglnt_encoder_of_match);
261+
262+static struct drm_platform_encoder_driver dglnt_encoder_driver = {
263+ .platform_driver = {
264+ .probe = dglnt_encoder_probe,
265+ .remove = dglnt_encoder_remove,
266+ .driver = {
267+ .owner = THIS_MODULE,
268+ .name = "dglnt-drm-enc",
269+ .of_match_table = dglnt_encoder_of_match,
270+ },
271+ },
272+
273+ .encoder_init = dglnt_encoder_encoder_init,
274+};
275+
276+static int __init dglnt_encoder_init(void)
277+{
278+ return platform_driver_register(&dglnt_encoder_driver.platform_driver);
279+}
280+
281+static void __exit dglnt_encoder_exit(void)
282+{
283+ platform_driver_unregister(&dglnt_encoder_driver.platform_driver);
284+}
285+
286+module_init(dglnt_encoder_init);
287+module_exit(dglnt_encoder_exit);
288+
289+MODULE_AUTHOR("Digilent, Inc.");
290+MODULE_DESCRIPTION("DRM slave encoder for Video-out on Digilent boards");
291+MODULE_LICENSE("GPL v2");
292--
2931.9.1
294
diff --git a/recipes-kernel/linux/linux-xlnx/0002-clk-Add-driver-for-axi_dynclk-IP-Core.patch b/recipes-kernel/linux/linux-xlnx/0002-clk-Add-driver-for-axi_dynclk-IP-Core.patch
new file mode 100644
index 00000000..b508fc27
--- /dev/null
+++ b/recipes-kernel/linux/linux-xlnx/0002-clk-Add-driver-for-axi_dynclk-IP-Core.patch
@@ -0,0 +1,601 @@
1From e3b9c3186bbdf9cd5d084c62ecd232b57a316d3f Mon Sep 17 00:00:00 2001
2From: Jason Wu <jason.wu.misc@gmail.com>
3Date: Sun, 10 Apr 2016 13:16:06 +1000
4Subject: [PATCH] clk: Add driver for axi_dynclk IP Core
5
6Add support for the axi_dynclk IP Core available from Digilent. This IP
7core dynamically configures the clock resources inside a Xilinx FPGA to
8generate a clock with a software programmable frequency.
9
10Upstream-Status: Pending
11
12Signed-off-by: Sam Bobrowicz <sbobrowicz@digilentinc.com>
13Signed-off-by: Jason Wu <jason.wu.misc@gmail.com>
14
15diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
16index c3e3a02..29dfd15 100644
17--- a/drivers/clk/Kconfig
18+++ b/drivers/clk/Kconfig
19@@ -147,6 +147,14 @@ config CLK_QORIQ
20 This adds the clock driver support for Freescale QorIQ platforms
21 using common clock framework.
22
23+config COMMON_CLK_DGLNT_DYNCLK
24+ tristate "Digilent axi_dynclk Driver"
25+ depends on ARCH_ZYNQ || MICROBLAZE
26+ help
27+ ---help---
28+ Support for the Digilent AXI Dynamic Clock core for Xilinx
29+ FPGAs.
30+
31 config COMMON_CLK_XGENE
32 bool "Clock driver for APM XGene SoC"
33 default y
34diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
35index 820714c..9043f45 100644
36--- a/drivers/clk/Makefile
37+++ b/drivers/clk/Makefile
38@@ -22,6 +22,7 @@ obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
39 obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o
40 obj-$(CONFIG_COMMON_CLK_CDCE706) += clk-cdce706.o
41 obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
42+obj-$(CONFIG_COMMON_CLK_DGLNT_DYNCLK) += clk-dglnt-dynclk.o
43 obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
44 obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
45 obj-$(CONFIG_MACH_LOONGSON32) += clk-ls1x.o
46diff --git a/drivers/clk/clk-dglnt-dynclk.c b/drivers/clk/clk-dglnt-dynclk.c
47new file mode 100644
48index 0000000..496ad5f
49--- /dev/null
50+++ b/drivers/clk/clk-dglnt-dynclk.c
51@@ -0,0 +1,547 @@
52+/*
53+ * clk-dglnt-dynclk.c - Digilent AXI Dynamic Clock (axi_dynclk) Driver
54+ *
55+ * Copyright (C) 2015 Digilent
56+ * Author: Sam Bobrowicz <sbobrowicz@digilentinc.com>
57+ *
58+ * Reused code from clk-axi-clkgen.c, Copyright (C) 2012-2013 Analog Devices Inc.
59+ *
60+ * This software is licensed under the terms of the GNU General Public
61+ * License version 2, as published by the Free Software Foundation, and
62+ * may be copied, distributed, and modified under those terms.
63+ *
64+ * This program is distributed in the hope that it will be useful,
65+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
66+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
67+ * GNU General Public License for more details.
68+ */
69+
70+#include <linux/platform_device.h>
71+#include <linux/clk-provider.h>
72+#include <linux/clk.h>
73+#include <linux/slab.h>
74+#include <linux/io.h>
75+#include <linux/of.h>
76+#include <linux/module.h>
77+#include <linux/err.h>
78+#include <linux/kernel.h>
79+
80+#define CLK_BIT_WEDGE 13
81+#define CLK_BIT_NOCOUNT 12
82+
83+/* This value is used to signal an error */
84+#define ERR_CLKCOUNTCALC 0xFFFFFFFF
85+#define ERR_CLKDIVIDER (1 << CLK_BIT_WEDGE | 1 << CLK_BIT_NOCOUNT)
86+
87+#define DYNCLK_DIV_1_REGMASK 0x1041
88+/* 25 MHz (125 KHz / 5) */
89+#define DYNCLK_DEFAULT_FREQ 125000
90+
91+#define MMCM_FREQ_VCOMIN 600000
92+#define MMCM_FREQ_VCOMAX 1200000
93+#define MMCM_FREQ_PFDMIN 10000
94+#define MMCM_FREQ_PFDMAX 450000
95+#define MMCM_FREQ_OUTMIN 4000
96+#define MMCM_FREQ_OUTMAX 800000
97+#define MMCM_DIV_MAX 106
98+#define MMCM_FB_MIN 2
99+#define MMCM_FB_MAX 64
100+#define MMCM_CLKDIV_MAX 128
101+#define MMCM_CLKDIV_MIN 1
102+
103+#define OFST_DISPLAY_CTRL 0x0
104+#define OFST_DISPLAY_STATUS 0x4
105+#define OFST_DISPLAY_CLK_L 0x8
106+#define OFST_DISPLAY_FB_L 0x0C
107+#define OFST_DISPLAY_FB_H_CLK_H 0x10
108+#define OFST_DISPLAY_DIV 0x14
109+#define OFST_DISPLAY_LOCK_L 0x18
110+#define OFST_DISPLAY_FLTR_LOCK_H 0x1C
111+
112+static const u64 lock_lookup[64] = {
113+ 0b0011000110111110100011111010010000000001,
114+ 0b0011000110111110100011111010010000000001,
115+ 0b0100001000111110100011111010010000000001,
116+ 0b0101101011111110100011111010010000000001,
117+ 0b0111001110111110100011111010010000000001,
118+ 0b1000110001111110100011111010010000000001,
119+ 0b1001110011111110100011111010010000000001,
120+ 0b1011010110111110100011111010010000000001,
121+ 0b1100111001111110100011111010010000000001,
122+ 0b1110011100111110100011111010010000000001,
123+ 0b1111111111111000010011111010010000000001,
124+ 0b1111111111110011100111111010010000000001,
125+ 0b1111111111101110111011111010010000000001,
126+ 0b1111111111101011110011111010010000000001,
127+ 0b1111111111101000101011111010010000000001,
128+ 0b1111111111100111000111111010010000000001,
129+ 0b1111111111100011111111111010010000000001,
130+ 0b1111111111100010011011111010010000000001,
131+ 0b1111111111100000110111111010010000000001,
132+ 0b1111111111011111010011111010010000000001,
133+ 0b1111111111011101101111111010010000000001,
134+ 0b1111111111011100001011111010010000000001,
135+ 0b1111111111011010100111111010010000000001,
136+ 0b1111111111011001000011111010010000000001,
137+ 0b1111111111011001000011111010010000000001,
138+ 0b1111111111010111011111111010010000000001,
139+ 0b1111111111010101111011111010010000000001,
140+ 0b1111111111010101111011111010010000000001,
141+ 0b1111111111010100010111111010010000000001,
142+ 0b1111111111010100010111111010010000000001,
143+ 0b1111111111010010110011111010010000000001,
144+ 0b1111111111010010110011111010010000000001,
145+ 0b1111111111010010110011111010010000000001,
146+ 0b1111111111010001001111111010010000000001,
147+ 0b1111111111010001001111111010010000000001,
148+ 0b1111111111010001001111111010010000000001,
149+ 0b1111111111001111101011111010010000000001,
150+ 0b1111111111001111101011111010010000000001,
151+ 0b1111111111001111101011111010010000000001,
152+ 0b1111111111001111101011111010010000000001,
153+ 0b1111111111001111101011111010010000000001,
154+ 0b1111111111001111101011111010010000000001,
155+ 0b1111111111001111101011111010010000000001,
156+ 0b1111111111001111101011111010010000000001,
157+ 0b1111111111001111101011111010010000000001,
158+ 0b1111111111001111101011111010010000000001,
159+ 0b1111111111001111101011111010010000000001,
160+ 0b1111111111001111101011111010010000000001,
161+ 0b1111111111001111101011111010010000000001,
162+ 0b1111111111001111101011111010010000000001,
163+ 0b1111111111001111101011111010010000000001,
164+ 0b1111111111001111101011111010010000000001,
165+ 0b1111111111001111101011111010010000000001,
166+ 0b1111111111001111101011111010010000000001,
167+ 0b1111111111001111101011111010010000000001,
168+ 0b1111111111001111101011111010010000000001,
169+ 0b1111111111001111101011111010010000000001,
170+ 0b1111111111001111101011111010010000000001,
171+ 0b1111111111001111101011111010010000000001,
172+ 0b1111111111001111101011111010010000000001,
173+ 0b1111111111001111101011111010010000000001,
174+ 0b1111111111001111101011111010010000000001,
175+ 0b1111111111001111101011111010010000000001,
176+ 0b1111111111001111101011111010010000000001
177+};
178+
179+static const u32 filter_lookup_low[64] = {
180+ 0b0001011111,
181+ 0b0001010111,
182+ 0b0001111011,
183+ 0b0001011011,
184+ 0b0001101011,
185+ 0b0001110011,
186+ 0b0001110011,
187+ 0b0001110011,
188+ 0b0001110011,
189+ 0b0001001011,
190+ 0b0001001011,
191+ 0b0001001011,
192+ 0b0010110011,
193+ 0b0001010011,
194+ 0b0001010011,
195+ 0b0001010011,
196+ 0b0001010011,
197+ 0b0001010011,
198+ 0b0001010011,
199+ 0b0001010011,
200+ 0b0001010011,
201+ 0b0001010011,
202+ 0b0001010011,
203+ 0b0001100011,
204+ 0b0001100011,
205+ 0b0001100011,
206+ 0b0001100011,
207+ 0b0001100011,
208+ 0b0001100011,
209+ 0b0001100011,
210+ 0b0001100011,
211+ 0b0001100011,
212+ 0b0001100011,
213+ 0b0001100011,
214+ 0b0001100011,
215+ 0b0001100011,
216+ 0b0001100011,
217+ 0b0010010011,
218+ 0b0010010011,
219+ 0b0010010011,
220+ 0b0010010011,
221+ 0b0010010011,
222+ 0b0010010011,
223+ 0b0010010011,
224+ 0b0010010011,
225+ 0b0010010011,
226+ 0b0010010011,
227+ 0b0010100011,
228+ 0b0010100011,
229+ 0b0010100011,
230+ 0b0010100011,
231+ 0b0010100011,
232+ 0b0010100011,
233+ 0b0010100011,
234+ 0b0010100011,
235+ 0b0010100011,
236+ 0b0010100011,
237+ 0b0010100011,
238+ 0b0010100011,
239+ 0b0010100011,
240+ 0b0010100011,
241+ 0b0010100011,
242+ 0b0010100011,
243+ 0b0010100011
244+};
245+
246+struct dglnt_dynclk_reg;
247+struct dglnt_dynclk_mode;
248+struct dglnt_dynclk;
249+
250+struct dglnt_dynclk_reg {
251+ u32 clk0L;
252+ u32 clkFBL;
253+ u32 clkFBH_clk0H;
254+ u32 divclk;
255+ u32 lockL;
256+ u32 fltr_lockH;
257+};
258+
259+struct dglnt_dynclk_mode {
260+ u32 freq;
261+ u32 fbmult;
262+ u32 clkdiv;
263+ u32 maindiv;
264+};
265+
266+struct dglnt_dynclk {
267+ void __iomem *base;
268+ struct clk_hw clk_hw;
269+ unsigned long freq;
270+};
271+
272+u32 dglnt_dynclk_divider(u32 divide)
273+{
274+ u32 output = 0;
275+ u32 highTime = 0;
276+ u32 lowTime = 0;
277+
278+ if ((divide < 1) || (divide > 128))
279+ return ERR_CLKDIVIDER;
280+
281+ if (divide == 1)
282+ return DYNCLK_DIV_1_REGMASK;
283+
284+ highTime = divide / 2;
285+ /* if divide is odd */
286+ if (divide & 0x1) {
287+ lowTime = highTime + 1;
288+ output = 1 << CLK_BIT_WEDGE;
289+ } else {
290+ lowTime = highTime;
291+ }
292+
293+ output |= 0x03F & lowTime;
294+ output |= 0xFC0 & (highTime << 6);
295+ return output;
296+}
297+
298+u32 dglnt_dynclk_count_calc(u32 divide)
299+{
300+ u32 output = 0;
301+ u32 divCalc = 0;
302+
303+ divCalc = dglnt_dynclk_divider(divide);
304+ if (divCalc == ERR_CLKDIVIDER)
305+ output = ERR_CLKCOUNTCALC;
306+ else
307+ output = (0xFFF & divCalc) | ((divCalc << 10) & 0x00C00000);
308+ return output;
309+}
310+
311+
312+int dglnt_dynclk_find_reg(struct dglnt_dynclk_reg *regValues,
313+ struct dglnt_dynclk_mode *clkParams)
314+{
315+ if ((clkParams->fbmult < 2) || clkParams->fbmult > 64)
316+ return -EINVAL;
317+
318+ regValues->clk0L = dglnt_dynclk_count_calc(clkParams->clkdiv);
319+ if (regValues->clk0L == ERR_CLKCOUNTCALC)
320+ return -EINVAL;
321+
322+ regValues->clkFBL = dglnt_dynclk_count_calc(clkParams->fbmult);
323+ if (regValues->clkFBL == ERR_CLKCOUNTCALC)
324+ return -EINVAL;
325+
326+ regValues->clkFBH_clk0H = 0;
327+
328+ regValues->divclk = dglnt_dynclk_divider(clkParams->maindiv);
329+ if (regValues->divclk == ERR_CLKDIVIDER)
330+ return -EINVAL;
331+
332+ regValues->lockL = (u32)(lock_lookup[clkParams->fbmult - 1] &
333+ 0xFFFFFFFF);
334+
335+ regValues->fltr_lockH = (u32)((lock_lookup[clkParams->fbmult - 1] >>
336+ 32) & 0x000000FF);
337+ regValues->fltr_lockH |= ((filter_lookup_low[clkParams->fbmult - 1] <<
338+ 16) & 0x03FF0000);
339+
340+ return 0;
341+}
342+
343+void dglnt_dynclk_write_reg(struct dglnt_dynclk_reg *regValues,
344+ void __iomem *baseaddr)
345+{
346+ writel(regValues->clk0L, baseaddr + OFST_DISPLAY_CLK_L);
347+ writel(regValues->clkFBL, baseaddr + OFST_DISPLAY_FB_L);
348+ writel(regValues->clkFBH_clk0H, baseaddr + OFST_DISPLAY_FB_H_CLK_H);
349+ writel(regValues->divclk, baseaddr + OFST_DISPLAY_DIV);
350+ writel(regValues->lockL, baseaddr + OFST_DISPLAY_LOCK_L);
351+ writel(regValues->fltr_lockH, baseaddr + OFST_DISPLAY_FLTR_LOCK_H);
352+}
353+
354+u32 dglnt_dynclk_find_mode(u32 freq, u32 parentFreq,
355+ struct dglnt_dynclk_mode *bestPick)
356+{
357+ u32 bestError = MMCM_FREQ_OUTMAX;
358+ u32 curError;
359+ u32 curClkMult;
360+ u32 curFreq;
361+ u32 divVal;
362+ u32 curFb, curClkDiv;
363+ u32 minFb = 0;
364+ u32 maxFb = 0;
365+ u32 curDiv = 1;
366+ u32 maxDiv;
367+ bool freq_found = false;
368+
369+ bestPick->freq = 0;
370+ if (parentFreq == 0)
371+ return 0;
372+
373+ /* minimum frequency is actually dictated by VCOmin */
374+ if (freq < MMCM_FREQ_OUTMIN)
375+ freq = MMCM_FREQ_OUTMIN;
376+ if (freq > MMCM_FREQ_OUTMAX)
377+ freq = MMCM_FREQ_OUTMAX;
378+
379+ if (parentFreq > MMCM_FREQ_PFDMAX)
380+ curDiv = 2;
381+ maxDiv = parentFreq / MMCM_FREQ_PFDMIN;
382+ if (maxDiv > MMCM_DIV_MAX)
383+ maxDiv = MMCM_DIV_MAX;
384+
385+ while (curDiv <= maxDiv && !freq_found) {
386+ minFb = curDiv * DIV_ROUND_UP(MMCM_FREQ_VCOMIN, parentFreq);
387+ maxFb = curDiv * (MMCM_FREQ_VCOMAX / parentFreq);
388+ if (maxFb > MMCM_FB_MAX)
389+ maxFb = MMCM_FB_MAX;
390+ if (minFb < MMCM_FB_MIN)
391+ minFb = MMCM_FB_MIN;
392+
393+ divVal = curDiv * freq;
394+ /*
395+ * This multiplier is used to find the best clkDiv value for
396+ * each FB value
397+ */
398+ curClkMult = ((parentFreq * 1000) + (divVal / 2)) / divVal;
399+
400+ curFb = minFb;
401+ while (curFb <= maxFb && !freq_found) {
402+ curClkDiv = ((curClkMult * curFb) + 500) / 1000;
403+ if (curClkDiv > MMCM_CLKDIV_MAX)
404+ curClkDiv = MMCM_CLKDIV_MAX;
405+ if (curClkDiv < MMCM_CLKDIV_MIN)
406+ curClkDiv = MMCM_CLKDIV_MIN;
407+ curFreq = (((parentFreq * curFb) / curDiv) / curClkDiv);
408+ if (curFreq >= freq)
409+ curError = curFreq - freq;
410+ else
411+ curError = freq - curFreq;
412+ if (curError < bestError) {
413+ bestError = curError;
414+ bestPick->clkdiv = curClkDiv;
415+ bestPick->fbmult = curFb;
416+ bestPick->maindiv = curDiv;
417+ bestPick->freq = curFreq;
418+ }
419+ if (!curError)
420+ freq_found = true;
421+ curFb++;
422+ }
423+ curDiv++;
424+ }
425+ return bestPick->freq;
426+}
427+
428+static struct dglnt_dynclk *clk_hw_to_dglnt_dynclk(struct clk_hw *clk_hw)
429+{
430+ return container_of(clk_hw, struct dglnt_dynclk, clk_hw);
431+}
432+
433+
434+static int dglnt_dynclk_enable(struct clk_hw *clk_hw)
435+{
436+ struct dglnt_dynclk *dglnt_dynclk = clk_hw_to_dglnt_dynclk(clk_hw);
437+ unsigned int clock_state;
438+
439+ if (dglnt_dynclk->freq) {
440+ writel(1, dglnt_dynclk->base + OFST_DISPLAY_CTRL);
441+ do {
442+ clock_state = readl(dglnt_dynclk->base +
443+ OFST_DISPLAY_STATUS);
444+ } while (!clock_state);
445+ }
446+ return 0;
447+}
448+
449+static void dglnt_dynclk_disable(struct clk_hw *clk_hw)
450+{
451+ struct dglnt_dynclk *dglnt_dynclk = clk_hw_to_dglnt_dynclk(clk_hw);
452+
453+ writel(0, dglnt_dynclk->base + OFST_DISPLAY_CTRL);
454+}
455+
456+static int dglnt_dynclk_set_rate(struct clk_hw *clk_hw,
457+ unsigned long rate, unsigned long parent_rate)
458+{
459+ struct dglnt_dynclk *dglnt_dynclk = clk_hw_to_dglnt_dynclk(clk_hw);
460+ struct dglnt_dynclk_reg clkReg;
461+ struct dglnt_dynclk_mode clkMode;
462+
463+ if (parent_rate == 0 || rate == 0)
464+ return -EINVAL;
465+ if (rate == dglnt_dynclk->freq)
466+ return 0;
467+
468+ /*
469+ * Convert from Hz to KHz, then multiply by five to account for
470+ * BUFR division
471+ */
472+ rate = (rate + 100) / 200;
473+ /* convert from Hz to KHz */
474+ parent_rate = (parent_rate + 500) / 1000;
475+ if (!dglnt_dynclk_find_mode(rate, parent_rate, &clkMode))
476+ return -EINVAL;
477+
478+ /*
479+ * Write to the PLL dynamic configuration registers to configure it
480+ * with the calculated parameters.
481+ */
482+ dglnt_dynclk_find_reg(&clkReg, &clkMode);
483+ dglnt_dynclk_write_reg(&clkReg, dglnt_dynclk->base);
484+ dglnt_dynclk->freq = clkMode.freq * 200;
485+ dglnt_dynclk_disable(clk_hw);
486+ dglnt_dynclk_enable(clk_hw);
487+
488+ return 0;
489+}
490+
491+static long dglnt_dynclk_round_rate(struct clk_hw *hw, unsigned long rate,
492+ unsigned long *parent_rate)
493+{
494+ struct dglnt_dynclk_mode clkMode;
495+
496+ dglnt_dynclk_find_mode(((rate + 100) / 200),
497+ ((*parent_rate) + 500) / 1000, &clkMode);
498+
499+ return (clkMode.freq * 200);
500+}
501+
502+static unsigned long dglnt_dynclk_recalc_rate(struct clk_hw *clk_hw,
503+ unsigned long parent_rate)
504+{
505+ struct dglnt_dynclk *dglnt_dynclk = clk_hw_to_dglnt_dynclk(clk_hw);
506+
507+ return dglnt_dynclk->freq;
508+}
509+
510+
511+static const struct clk_ops dglnt_dynclk_ops = {
512+ .recalc_rate = dglnt_dynclk_recalc_rate,
513+ .round_rate = dglnt_dynclk_round_rate,
514+ .set_rate = dglnt_dynclk_set_rate,
515+ .enable = dglnt_dynclk_enable,
516+ .disable = dglnt_dynclk_disable,
517+};
518+
519+static const struct of_device_id dglnt_dynclk_ids[] = {
520+ { .compatible = "digilent,axi-dynclk", },
521+ { },
522+};
523+MODULE_DEVICE_TABLE(of, dglnt_dynclk_ids);
524+
525+static int dglnt_dynclk_probe(struct platform_device *pdev)
526+{
527+ const struct of_device_id *id;
528+ struct dglnt_dynclk *dglnt_dynclk;
529+ struct clk_init_data init;
530+ const char *parent_name;
531+ const char *clk_name;
532+ struct resource *mem;
533+ struct clk *clk;
534+
535+ if (!pdev->dev.of_node)
536+ return -ENODEV;
537+
538+ id = of_match_node(dglnt_dynclk_ids, pdev->dev.of_node);
539+ if (!id)
540+ return -ENODEV;
541+
542+ dglnt_dynclk = devm_kzalloc(&pdev->dev, sizeof(*dglnt_dynclk),
543+ GFP_KERNEL);
544+ if (!dglnt_dynclk)
545+ return -ENOMEM;
546+
547+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
548+ dglnt_dynclk->base = devm_ioremap_resource(&pdev->dev, mem);
549+ if (IS_ERR(dglnt_dynclk->base))
550+ return PTR_ERR(dglnt_dynclk->base);
551+
552+ parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
553+ if (!parent_name)
554+ return -EINVAL;
555+
556+ clk_name = pdev->dev.of_node->name;
557+ of_property_read_string(pdev->dev.of_node, "clock-output-names",
558+ &clk_name);
559+
560+ init.name = clk_name;
561+ init.ops = &dglnt_dynclk_ops;
562+ init.flags = 0;
563+ init.parent_names = &parent_name;
564+ init.num_parents = 1;
565+
566+ dglnt_dynclk->freq = 0;
567+ dglnt_dynclk_disable(&dglnt_dynclk->clk_hw);
568+
569+ dglnt_dynclk->clk_hw.init = &init;
570+ clk = devm_clk_register(&pdev->dev, &dglnt_dynclk->clk_hw);
571+ if (IS_ERR(clk))
572+ return PTR_ERR(clk);
573+
574+ return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
575+ clk);
576+}
577+
578+static int dglnt_dynclk_remove(struct platform_device *pdev)
579+{
580+ of_clk_del_provider(pdev->dev.of_node);
581+
582+ return 0;
583+}
584+
585+static struct platform_driver dglnt_dynclk_driver = {
586+ .driver = {
587+ .name = "dglnt-dynclk",
588+ .owner = THIS_MODULE,
589+ .of_match_table = dglnt_dynclk_ids,
590+ },
591+ .probe = dglnt_dynclk_probe,
592+ .remove = dglnt_dynclk_remove,
593+};
594+module_platform_driver(dglnt_dynclk_driver);
595+
596+MODULE_LICENSE("GPL v2");
597+MODULE_AUTHOR("Sam Bobrowicz <sbobrowicz@digilentinc.com>");
598+MODULE_DESCRIPTION("CCF Driver for Digilent axi_dynclk IP Core");
599--
6001.9.1
601
diff --git a/recipes-kernel/linux/linux-xlnx_4.0.bb b/recipes-kernel/linux/linux-xlnx_4.0.bb
index 75b01efd..689f0b9d 100644
--- a/recipes-kernel/linux/linux-xlnx_4.0.bb
+++ b/recipes-kernel/linux/linux-xlnx_4.0.bb
@@ -4,3 +4,7 @@ SRCREV ?= "468329e7fac2b22e76897fbd40532cc0884ded2a"
4 4
5include linux-xlnx.inc 5include linux-xlnx.inc
6 6
7SRC_URI_append_zybo-linux-bd-zynq7 = " \
8 file://0001-drm-xilinx-Add-encoder-for-Digilent-boards.patch \
9 file://0002-clk-Add-driver-for-axi_dynclk-IP-Core.patch \
10" \ No newline at end of file
diff --git a/recipes-kernel/linux/linux-xlnx_4.4.bb b/recipes-kernel/linux/linux-xlnx_4.4.bb
index a8019e52..5d78d3ac 100644
--- a/recipes-kernel/linux/linux-xlnx_4.4.bb
+++ b/recipes-kernel/linux/linux-xlnx_4.4.bb
@@ -4,3 +4,7 @@ SRCREV ?="dd7c1f0b5c23bcac5046d77bd5e0631e657003a4"
4 4
5include linux-xlnx.inc 5include linux-xlnx.inc
6 6
7SRC_URI_append_zybo-linux-bd-zynq7 = " \
8 file://0001-drm-xilinx-Add-encoder-for-Digilent-boards.patch \
9 file://0002-clk-Add-driver-for-axi_dynclk-IP-Core.patch \
10" \ No newline at end of file