On 12/04/2016 6:59 PM, Nathan Rossi wrote:
On Mon, Apr 11, 2016 at 11:23 PM, Jason Wu <[email protected]> wrote:
Signed-off-by: Jason Wu <[email protected]>

Unfortunately I wont be able to accept this patch. The main reason is
it is impossible to maintain applying patches to a moving target, of
which linux-xlnx-dev is.
not a issue at all. I understand.

It would be best to get these changes into linux-xlnx and/or mainline
if possible.
There is no point to have these two patches in mainline if some of the v4l2 driver patches are not in mainline.

patch 11 adds the driver to linux-xlinx.

Regards,

Jason


Regards,
Nathan

---
  recipes-kernel/linux/linux-xlnx-dev.bb             |   5 +
  ...rm-xilinx-Add-encoder-for-Digilent-boards.patch | 294 ++++++++++
  ...002-clk-Add-driver-for-axi_dynclk-IP-Core.patch | 601 +++++++++++++++++++++
  3 files changed, 900 insertions(+)
  create mode 100644 
recipes-kernel/linux/linux-xlnx-dev/0001-drm-xilinx-Add-encoder-for-Digilent-boards.patch
  create mode 100644 
recipes-kernel/linux/linux-xlnx-dev/0002-clk-Add-driver-for-axi_dynclk-IP-Core.patch

diff --git a/recipes-kernel/linux/linux-xlnx-dev.bb 
b/recipes-kernel/linux/linux-xlnx-dev.bb
index 1bfed49..a145b2a 100644
--- a/recipes-kernel/linux/linux-xlnx-dev.bb
+++ b/recipes-kernel/linux/linux-xlnx-dev.bb
@@ -9,6 +9,11 @@ SRCBRANCH = "${KBRANCH}"
  # Use the SRCREV for the last tagged revision of linux-xlnx.
  SRCREV ?= '${@oe.utils.conditional("PREFERRED_PROVIDER_virtual/kernel", "linux-xlnx-dev", 
"${AUTOREV}", "3821a7bfdf7a4c697cac62f0157d8bf49467ea67", d)}'

+SRC_URI_append_zybo-linux-bd-zynq7 = " \
+       file://0001-drm-xilinx-Add-encoder-for-Digilent-boards.patch \
+       file://0002-clk-Add-driver-for-axi_dynclk-IP-Core.patch \
+"
+
  python () {
      if d.getVar("PREFERRED_PROVIDER_virtual/kernel", True) != 
"linux-xlnx-dev":
          raise bb.parse.SkipPackage("Set PREFERRED_PROVIDER_virtual/kernel to 
linux-xlnx-dev to enable it")
diff --git 
a/recipes-kernel/linux/linux-xlnx-dev/0001-drm-xilinx-Add-encoder-for-Digilent-boards.patch
 
b/recipes-kernel/linux/linux-xlnx-dev/0001-drm-xilinx-Add-encoder-for-Digilent-boards.patch
new file mode 100644
index 0000000..892455a
--- /dev/null
+++ 
b/recipes-kernel/linux/linux-xlnx-dev/0001-drm-xilinx-Add-encoder-for-Digilent-boards.patch
@@ -0,0 +1,294 @@
+From 3d1820b308dcc9344d8e7df4a8a712632fb77b97 Mon Sep 17 00:00:00 2001
+Message-Id: 
<3d1820b308dcc9344d8e7df4a8a712632fb77b97.1460291687.git.jason.wu.m...@gmail.com>
+From: Jason Wu <[email protected]>
+Date: Sun, 10 Apr 2016 13:14:13 +1000
+Subject: [LINUX] [PATCH] drm: xilinx: Add encoder for Digilent boards
+
+Add the dglnt_encoder driver that enables DRM support for the VGA and
+HDMI output ports found on many Digilent boards.
+
+Upstream-Status: Pending
+
+Signed-off-by: Sam Bobrowicz <[email protected]>
+Signed-off-by: Jason Wu <[email protected]>
+---
+github.com () linux-xlnx.git xlnx/master
+
+diff --git a/Documentation/devicetree/bindings/drm/xilinx/dglnt_encoder.txt 
b/Documentation/devicetree/bindings/drm/xilinx/dglnt_encoder.txt
+new file mode 100644
+index 0000000..242b24e
+--- /dev/null
++++ b/Documentation/devicetree/bindings/drm/xilinx/dglnt_encoder.txt
+@@ -0,0 +1,23 @@
++Device-Tree bindings for Digilent DRM Encoder Slave
++
++This driver provides support for VGA and HDMI outputs on Digilent FPGA boards.
++The VGA or HDMI port must be connected to a Xilinx display pipeline via an
++axi2vid IP core.
++
++Required properties:
++ - compatible: Should be "digilent,drm-encoder".
++
++Optional properties:
++ - dglnt,edid-i2c: The I2C device connected to the DDC bus on the video
++                   connector. This is used to obtain the supported resolutions
++                   of an attached monitor. If not defined, then a default
++                   set of resolutions is used and the display will initialize
++                   to 720p. Note most VGA connectors on Digilent boards do
++                   not have the DDC bus routed out.
++
++Example:
++
++      encoder_0: digilent_encoder {
++              compatible = "digilent,drm-encoder";
++              dglnt,edid-i2c = <&i2c1>;
++      };
+diff --git a/drivers/gpu/drm/xilinx/Kconfig b/drivers/gpu/drm/xilinx/Kconfig
+index a713b17..c32a4a6 100644
+--- a/drivers/gpu/drm/xilinx/Kconfig
++++ b/drivers/gpu/drm/xilinx/Kconfig
+@@ -21,3 +21,9 @@ config DRM_XILINX_DP_SUB
+       select DRM_XILINX_DP
+       help
+         DRM driver for Xilinx Display Port Subsystem.
++
++config DRM_DIGILENT_ENCODER
++   tristate "Digilent VGA/HDMI DRM Encoder Driver"
++   depends on DRM_XILINX
++   help
++     DRM slave encoder for Video-out on Digilent boards.
+diff --git a/drivers/gpu/drm/xilinx/Makefile b/drivers/gpu/drm/xilinx/Makefile
+index 705472c..a571bd9 100644
+--- a/drivers/gpu/drm/xilinx/Makefile
++++ b/drivers/gpu/drm/xilinx/Makefile
+@@ -10,3 +10,4 @@ xilinx_drm-y += xilinx_cresample.o xilinx_osd.o 
xilinx_rgb2yuv.o xilinx_vtc.o
+ obj-$(CONFIG_DRM_XILINX) += xilinx_drm.o
+ obj-$(CONFIG_DRM_XILINX_DP) += xilinx_drm_dp.o
+ obj-$(CONFIG_DRM_XILINX_DP_SUB) += xilinx_drm_dp_sub.o
++obj-$(CONFIG_DRM_DIGILENT_ENCODER) += dglnt_encoder.o
+diff --git a/drivers/gpu/drm/xilinx/dglnt_encoder.c 
b/drivers/gpu/drm/xilinx/dglnt_encoder.c
+new file mode 100644
+index 0000000..26a2398
+--- /dev/null
++++ b/drivers/gpu/drm/xilinx/dglnt_encoder.c
+@@ -0,0 +1,217 @@
++/*
++ * dglnt_encoder.c - DRM slave encoder for Video-out on Digilent boards
++ *
++ * Copyright (C) 2015 Digilent
++ * Author: Sam Bobrowicz <[email protected]>
++ *
++ * Based on udl_encoder.c and udl_connector.c, Copyright (C) 2012 Red Hat.
++ * Also based on xilinx_drm_dp.c, Copyright (C) 2014 Xilinx, Inc.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <drm/drmP.h>
++#include <drm/drm_edid.h>
++#include <drm/drm_encoder_slave.h>
++
++#include <linux/device.h>
++#include <linux/module.h>
++#include <linux/err.h>
++#include <linux/i2c.h>
++#include <linux/of.h>
++#include <linux/of_platform.h>
++#include <linux/platform_device.h>
++
++#define DGLNT_ENC_MAX_FREQ 150000
++#define DGLNT_ENC_MAX_H 1920
++#define DGLNT_ENC_MAX_V 1080
++#define DGLNT_ENC_PREF_H 1280
++#define DGLNT_ENC_PREF_V 720
++
++struct dglnt_encoder {
++      struct drm_encoder *encoder;
++      struct i2c_adapter *i2c_bus;
++      bool i2c_present;
++};
++
++static inline struct dglnt_encoder *to_dglnt_encoder(
++                                      struct drm_encoder *encoder)
++{
++      return to_encoder_slave(encoder)->slave_priv;
++}
++
++static bool dglnt_mode_fixup(struct drm_encoder *encoder,
++                              const struct drm_display_mode *mode,
++                              struct drm_display_mode *adjusted_mode)
++{
++      return true;
++}
++
++static void dglnt_encoder_mode_set(struct drm_encoder *encoder,
++                              struct drm_display_mode *mode,
++                              struct drm_display_mode *adjusted_mode)
++{
++}
++
++static void
++dglnt_encoder_dpms(struct drm_encoder *encoder, int mode)
++{
++}
++
++static void dglnt_encoder_save(struct drm_encoder *encoder)
++{
++}
++
++static void dglnt_encoder_restore(struct drm_encoder *encoder)
++{
++}
++
++static int dglnt_encoder_mode_valid(struct drm_encoder *encoder,
++                              struct drm_display_mode *mode)
++{
++      if (mode &&
++              !(mode->flags & ((DRM_MODE_FLAG_INTERLACE |
++                      DRM_MODE_FLAG_DBLCLK) | DRM_MODE_FLAG_3D_MASK)) &&
++              (mode->clock <= DGLNT_ENC_MAX_FREQ) &&
++              (mode->hdisplay <= DGLNT_ENC_MAX_H) &&
++              (mode->vdisplay <= DGLNT_ENC_MAX_V))
++              return MODE_OK;
++      return MODE_BAD;
++}
++
++static int dglnt_encoder_get_modes(struct drm_encoder *encoder,
++                              struct drm_connector *connector)
++{
++      struct dglnt_encoder *dglnt = to_dglnt_encoder(encoder);
++      struct edid *edid;
++      int num_modes = 0;
++
++      if (dglnt->i2c_present) {
++              edid = drm_get_edid(connector, dglnt->i2c_bus);
++              drm_mode_connector_update_edid_property(connector, edid);
++              if (edid) {
++                      num_modes = drm_add_edid_modes(connector, edid);
++                      kfree(edid);
++              }
++      } else {
++              num_modes = drm_add_modes_noedid(connector, DGLNT_ENC_MAX_H,
++                                              DGLNT_ENC_MAX_V);
++              drm_set_preferred_mode(connector, DGLNT_ENC_PREF_H,
++                                      DGLNT_ENC_PREF_V);
++      }
++      return num_modes;
++}
++
++static enum drm_connector_status dglnt_encoder_detect(
++                                      struct drm_encoder *encoder,
++                                      struct drm_connector *connector)
++{
++      struct dglnt_encoder *dglnt = to_dglnt_encoder(encoder);
++
++      if (dglnt->i2c_present) {
++              if (drm_probe_ddc(dglnt->i2c_bus))
++                      return connector_status_connected;
++              return connector_status_disconnected;
++      } else
++              return connector_status_unknown;
++}
++
++static struct drm_encoder_slave_funcs dglnt_encoder_slave_funcs = {
++      .dpms                   = dglnt_encoder_dpms,
++      .save                   = dglnt_encoder_save,
++      .restore                = dglnt_encoder_restore,
++      .mode_fixup             = dglnt_mode_fixup,
++      .mode_valid             = dglnt_encoder_mode_valid,
++      .mode_set               = dglnt_encoder_mode_set,
++      .detect                 = dglnt_encoder_detect,
++      .get_modes              = dglnt_encoder_get_modes,
++};
++
++static int dglnt_encoder_encoder_init(struct platform_device *pdev,
++                              struct drm_device *dev,
++                              struct drm_encoder_slave *encoder)
++{
++      struct dglnt_encoder *dglnt = platform_get_drvdata(pdev);
++      struct device_node *sub_node;
++
++      encoder->slave_priv = dglnt;
++      encoder->slave_funcs = &dglnt_encoder_slave_funcs;
++
++      dglnt->encoder = &encoder->base;
++
++      /* get i2c adapter for edid */
++      dglnt->i2c_present = false;
++      sub_node = of_parse_phandle(pdev->dev.of_node, "dglnt,edid-i2c", 0);
++      if (sub_node) {
++              dglnt->i2c_bus = of_find_i2c_adapter_by_node(sub_node);
++              if (!dglnt->i2c_bus)
++                      DRM_INFO("failed to get the edid i2c adapter, using default 
modes\n");
++              else
++                      dglnt->i2c_present = true;
++              of_node_put(sub_node);
++      }
++
++      return 0;
++}
++
++static int dglnt_encoder_probe(struct platform_device *pdev)
++{
++      struct dglnt_encoder *dglnt;
++
++      dglnt = devm_kzalloc(&pdev->dev, sizeof(*dglnt), GFP_KERNEL);
++      if (!dglnt)
++              return -ENOMEM;
++
++      platform_set_drvdata(pdev, dglnt);
++
++      return 0;
++}
++
++static int dglnt_encoder_remove(struct platform_device *pdev)
++{
++      return 0;
++}
++
++static const struct of_device_id dglnt_encoder_of_match[] = {
++      { .compatible = "digilent,drm-encoder", },
++      { /* end of table */ },
++};
++MODULE_DEVICE_TABLE(of, dglnt_encoder_of_match);
++
++static struct drm_platform_encoder_driver dglnt_encoder_driver = {
++      .platform_driver = {
++              .probe                  = dglnt_encoder_probe,
++              .remove                 = dglnt_encoder_remove,
++              .driver                 = {
++                      .owner          = THIS_MODULE,
++                      .name           = "dglnt-drm-enc",
++                      .of_match_table = dglnt_encoder_of_match,
++              },
++      },
++
++      .encoder_init = dglnt_encoder_encoder_init,
++};
++
++static int __init dglnt_encoder_init(void)
++{
++      return platform_driver_register(&dglnt_encoder_driver.platform_driver);
++}
++
++static void __exit dglnt_encoder_exit(void)
++{
++      platform_driver_unregister(&dglnt_encoder_driver.platform_driver);
++}
++
++module_init(dglnt_encoder_init);
++module_exit(dglnt_encoder_exit);
++
++MODULE_AUTHOR("Digilent, Inc.");
++MODULE_DESCRIPTION("DRM slave encoder for Video-out on Digilent boards");
++MODULE_LICENSE("GPL v2");
+--
+1.9.1
+
diff --git 
a/recipes-kernel/linux/linux-xlnx-dev/0002-clk-Add-driver-for-axi_dynclk-IP-Core.patch
 
b/recipes-kernel/linux/linux-xlnx-dev/0002-clk-Add-driver-for-axi_dynclk-IP-Core.patch
new file mode 100644
index 0000000..b508fc2
--- /dev/null
+++ 
b/recipes-kernel/linux/linux-xlnx-dev/0002-clk-Add-driver-for-axi_dynclk-IP-Core.patch
@@ -0,0 +1,601 @@
+From e3b9c3186bbdf9cd5d084c62ecd232b57a316d3f Mon Sep 17 00:00:00 2001
+From: Jason Wu <[email protected]>
+Date: Sun, 10 Apr 2016 13:16:06 +1000
+Subject: [PATCH] clk: Add driver for axi_dynclk IP Core
+
+Add support for the axi_dynclk IP Core available from Digilent. This IP
+core dynamically configures the clock resources inside a Xilinx FPGA to
+generate a clock with a software programmable frequency.
+
+Upstream-Status: Pending
+
+Signed-off-by: Sam Bobrowicz <[email protected]>
+Signed-off-by: Jason Wu <[email protected]>
+
+diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
+index c3e3a02..29dfd15 100644
+--- a/drivers/clk/Kconfig
++++ b/drivers/clk/Kconfig
+@@ -147,6 +147,14 @@ config CLK_QORIQ
+         This adds the clock driver support for Freescale QorIQ platforms
+         using common clock framework.
+
++config COMMON_CLK_DGLNT_DYNCLK
++      tristate "Digilent axi_dynclk Driver"
++      depends on ARCH_ZYNQ || MICROBLAZE
++      help
++      ---help---
++        Support for the Digilent AXI Dynamic Clock core for Xilinx
++        FPGAs.
++
+ config COMMON_CLK_XGENE
+       bool "Clock driver for APM XGene SoC"
+       default y
+diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
+index 820714c..9043f45 100644
+--- a/drivers/clk/Makefile
++++ b/drivers/clk/Makefile
+@@ -22,6 +22,7 @@ obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN)  += clk-axi-clkgen.o
+ obj-$(CONFIG_ARCH_AXXIA)              += clk-axm5516.o
+ obj-$(CONFIG_COMMON_CLK_CDCE706)      += clk-cdce706.o
+ obj-$(CONFIG_ARCH_CLPS711X)           += clk-clps711x.o
++obj-$(CONFIG_COMMON_CLK_DGLNT_DYNCLK) += clk-dglnt-dynclk.o
+ obj-$(CONFIG_ARCH_EFM32)              += clk-efm32gg.o
+ obj-$(CONFIG_ARCH_HIGHBANK)           += clk-highbank.o
+ obj-$(CONFIG_MACH_LOONGSON32)         += clk-ls1x.o
+diff --git a/drivers/clk/clk-dglnt-dynclk.c b/drivers/clk/clk-dglnt-dynclk.c
+new file mode 100644
+index 0000000..496ad5f
+--- /dev/null
++++ b/drivers/clk/clk-dglnt-dynclk.c
+@@ -0,0 +1,547 @@
++/*
++ * clk-dglnt-dynclk.c - Digilent AXI Dynamic Clock (axi_dynclk) Driver
++ *
++ * Copyright (C) 2015 Digilent
++ * Author: Sam Bobrowicz <[email protected]>
++ *
++ * Reused code from clk-axi-clkgen.c, Copyright (C) 2012-2013 Analog Devices 
Inc.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/platform_device.h>
++#include <linux/clk-provider.h>
++#include <linux/clk.h>
++#include <linux/slab.h>
++#include <linux/io.h>
++#include <linux/of.h>
++#include <linux/module.h>
++#include <linux/err.h>
++#include <linux/kernel.h>
++
++#define CLK_BIT_WEDGE 13
++#define CLK_BIT_NOCOUNT 12
++
++/* This value is used to signal an error */
++#define ERR_CLKCOUNTCALC 0xFFFFFFFF
++#define ERR_CLKDIVIDER (1 << CLK_BIT_WEDGE | 1 << CLK_BIT_NOCOUNT)
++
++#define DYNCLK_DIV_1_REGMASK 0x1041
++/* 25 MHz (125 KHz / 5) */
++#define DYNCLK_DEFAULT_FREQ 125000
++
++#define MMCM_FREQ_VCOMIN 600000
++#define MMCM_FREQ_VCOMAX 1200000
++#define MMCM_FREQ_PFDMIN 10000
++#define MMCM_FREQ_PFDMAX 450000
++#define MMCM_FREQ_OUTMIN 4000
++#define MMCM_FREQ_OUTMAX 800000
++#define MMCM_DIV_MAX 106
++#define MMCM_FB_MIN 2
++#define MMCM_FB_MAX 64
++#define MMCM_CLKDIV_MAX 128
++#define MMCM_CLKDIV_MIN 1
++
++#define OFST_DISPLAY_CTRL 0x0
++#define OFST_DISPLAY_STATUS 0x4
++#define OFST_DISPLAY_CLK_L 0x8
++#define OFST_DISPLAY_FB_L 0x0C
++#define OFST_DISPLAY_FB_H_CLK_H 0x10
++#define OFST_DISPLAY_DIV 0x14
++#define OFST_DISPLAY_LOCK_L 0x18
++#define OFST_DISPLAY_FLTR_LOCK_H 0x1C
++
++static const u64 lock_lookup[64] = {
++      0b0011000110111110100011111010010000000001,
++      0b0011000110111110100011111010010000000001,
++      0b0100001000111110100011111010010000000001,
++      0b0101101011111110100011111010010000000001,
++      0b0111001110111110100011111010010000000001,
++      0b1000110001111110100011111010010000000001,
++      0b1001110011111110100011111010010000000001,
++      0b1011010110111110100011111010010000000001,
++      0b1100111001111110100011111010010000000001,
++      0b1110011100111110100011111010010000000001,
++      0b1111111111111000010011111010010000000001,
++      0b1111111111110011100111111010010000000001,
++      0b1111111111101110111011111010010000000001,
++      0b1111111111101011110011111010010000000001,
++      0b1111111111101000101011111010010000000001,
++      0b1111111111100111000111111010010000000001,
++      0b1111111111100011111111111010010000000001,
++      0b1111111111100010011011111010010000000001,
++      0b1111111111100000110111111010010000000001,
++      0b1111111111011111010011111010010000000001,
++      0b1111111111011101101111111010010000000001,
++      0b1111111111011100001011111010010000000001,
++      0b1111111111011010100111111010010000000001,
++      0b1111111111011001000011111010010000000001,
++      0b1111111111011001000011111010010000000001,
++      0b1111111111010111011111111010010000000001,
++      0b1111111111010101111011111010010000000001,
++      0b1111111111010101111011111010010000000001,
++      0b1111111111010100010111111010010000000001,
++      0b1111111111010100010111111010010000000001,
++      0b1111111111010010110011111010010000000001,
++      0b1111111111010010110011111010010000000001,
++      0b1111111111010010110011111010010000000001,
++      0b1111111111010001001111111010010000000001,
++      0b1111111111010001001111111010010000000001,
++      0b1111111111010001001111111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001,
++      0b1111111111001111101011111010010000000001
++};
++
++static const u32 filter_lookup_low[64] = {
++      0b0001011111,
++      0b0001010111,
++      0b0001111011,
++      0b0001011011,
++      0b0001101011,
++      0b0001110011,
++      0b0001110011,
++      0b0001110011,
++      0b0001110011,
++      0b0001001011,
++      0b0001001011,
++      0b0001001011,
++      0b0010110011,
++      0b0001010011,
++      0b0001010011,
++      0b0001010011,
++      0b0001010011,
++      0b0001010011,
++      0b0001010011,
++      0b0001010011,
++      0b0001010011,
++      0b0001010011,
++      0b0001010011,
++      0b0001100011,
++      0b0001100011,
++      0b0001100011,
++      0b0001100011,
++      0b0001100011,
++      0b0001100011,
++      0b0001100011,
++      0b0001100011,
++      0b0001100011,
++      0b0001100011,
++      0b0001100011,
++      0b0001100011,
++      0b0001100011,
++      0b0001100011,
++      0b0010010011,
++      0b0010010011,
++      0b0010010011,
++      0b0010010011,
++      0b0010010011,
++      0b0010010011,
++      0b0010010011,
++      0b0010010011,
++      0b0010010011,
++      0b0010010011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011,
++      0b0010100011
++};
++
++struct dglnt_dynclk_reg;
++struct dglnt_dynclk_mode;
++struct dglnt_dynclk;
++
++struct dglnt_dynclk_reg {
++      u32 clk0L;
++      u32 clkFBL;
++      u32 clkFBH_clk0H;
++      u32 divclk;
++      u32 lockL;
++      u32 fltr_lockH;
++};
++
++struct dglnt_dynclk_mode {
++      u32 freq;
++      u32 fbmult;
++      u32 clkdiv;
++      u32 maindiv;
++};
++
++struct dglnt_dynclk {
++      void __iomem *base;
++      struct clk_hw clk_hw;
++      unsigned long freq;
++};
++
++u32 dglnt_dynclk_divider(u32 divide)
++{
++      u32 output = 0;
++      u32 highTime = 0;
++      u32 lowTime = 0;
++
++      if ((divide < 1) || (divide > 128))
++              return ERR_CLKDIVIDER;
++
++      if (divide == 1)
++              return DYNCLK_DIV_1_REGMASK;
++
++      highTime = divide / 2;
++      /* if divide is odd */
++      if (divide & 0x1) {
++              lowTime = highTime + 1;
++              output = 1 << CLK_BIT_WEDGE;
++      } else {
++              lowTime = highTime;
++      }
++
++      output |= 0x03F & lowTime;
++      output |= 0xFC0 & (highTime << 6);
++      return output;
++}
++
++u32 dglnt_dynclk_count_calc(u32 divide)
++{
++      u32 output = 0;
++      u32 divCalc = 0;
++
++      divCalc = dglnt_dynclk_divider(divide);
++      if (divCalc == ERR_CLKDIVIDER)
++              output = ERR_CLKCOUNTCALC;
++      else
++              output = (0xFFF & divCalc) | ((divCalc << 10) & 0x00C00000);
++      return output;
++}
++
++
++int dglnt_dynclk_find_reg(struct dglnt_dynclk_reg *regValues,
++                        struct dglnt_dynclk_mode *clkParams)
++{
++      if ((clkParams->fbmult < 2) || clkParams->fbmult > 64)
++              return -EINVAL;
++
++      regValues->clk0L = dglnt_dynclk_count_calc(clkParams->clkdiv);
++      if (regValues->clk0L == ERR_CLKCOUNTCALC)
++              return -EINVAL;
++
++      regValues->clkFBL = dglnt_dynclk_count_calc(clkParams->fbmult);
++      if (regValues->clkFBL == ERR_CLKCOUNTCALC)
++              return -EINVAL;
++
++      regValues->clkFBH_clk0H = 0;
++
++      regValues->divclk = dglnt_dynclk_divider(clkParams->maindiv);
++      if (regValues->divclk == ERR_CLKDIVIDER)
++              return -EINVAL;
++
++      regValues->lockL = (u32)(lock_lookup[clkParams->fbmult - 1] &
++                               0xFFFFFFFF);
++
++      regValues->fltr_lockH = (u32)((lock_lookup[clkParams->fbmult - 1] >>
++                                     32) & 0x000000FF);
++      regValues->fltr_lockH |= ((filter_lookup_low[clkParams->fbmult - 1] <<
++                                 16) & 0x03FF0000);
++
++      return 0;
++}
++
++void dglnt_dynclk_write_reg(struct dglnt_dynclk_reg *regValues,
++                          void __iomem *baseaddr)
++{
++      writel(regValues->clk0L, baseaddr + OFST_DISPLAY_CLK_L);
++      writel(regValues->clkFBL, baseaddr + OFST_DISPLAY_FB_L);
++      writel(regValues->clkFBH_clk0H, baseaddr + OFST_DISPLAY_FB_H_CLK_H);
++      writel(regValues->divclk, baseaddr + OFST_DISPLAY_DIV);
++      writel(regValues->lockL, baseaddr + OFST_DISPLAY_LOCK_L);
++      writel(regValues->fltr_lockH, baseaddr + OFST_DISPLAY_FLTR_LOCK_H);
++}
++
++u32 dglnt_dynclk_find_mode(u32 freq, u32 parentFreq,
++                         struct dglnt_dynclk_mode *bestPick)
++{
++      u32 bestError = MMCM_FREQ_OUTMAX;
++      u32 curError;
++      u32 curClkMult;
++      u32 curFreq;
++      u32 divVal;
++      u32 curFb, curClkDiv;
++      u32 minFb = 0;
++      u32 maxFb = 0;
++      u32 curDiv = 1;
++      u32 maxDiv;
++      bool freq_found = false;
++
++      bestPick->freq = 0;
++      if (parentFreq == 0)
++              return 0;
++
++      /* minimum frequency is actually dictated by VCOmin */
++      if (freq < MMCM_FREQ_OUTMIN)
++              freq = MMCM_FREQ_OUTMIN;
++      if (freq > MMCM_FREQ_OUTMAX)
++              freq = MMCM_FREQ_OUTMAX;
++
++      if (parentFreq > MMCM_FREQ_PFDMAX)
++              curDiv = 2;
++      maxDiv = parentFreq / MMCM_FREQ_PFDMIN;
++      if (maxDiv > MMCM_DIV_MAX)
++              maxDiv = MMCM_DIV_MAX;
++
++      while (curDiv <= maxDiv && !freq_found) {
++              minFb = curDiv * DIV_ROUND_UP(MMCM_FREQ_VCOMIN, parentFreq);
++              maxFb = curDiv * (MMCM_FREQ_VCOMAX / parentFreq);
++              if (maxFb > MMCM_FB_MAX)
++                      maxFb = MMCM_FB_MAX;
++              if (minFb < MMCM_FB_MIN)
++                      minFb = MMCM_FB_MIN;
++
++              divVal = curDiv * freq;
++              /*
++               * This multiplier is used to find the best clkDiv value for
++               * each FB value
++               */
++              curClkMult = ((parentFreq * 1000) + (divVal / 2)) / divVal;
++
++              curFb = minFb;
++              while (curFb <= maxFb && !freq_found) {
++                      curClkDiv = ((curClkMult * curFb) + 500) / 1000;
++                      if (curClkDiv > MMCM_CLKDIV_MAX)
++                              curClkDiv = MMCM_CLKDIV_MAX;
++                      if (curClkDiv < MMCM_CLKDIV_MIN)
++                              curClkDiv = MMCM_CLKDIV_MIN;
++                      curFreq = (((parentFreq * curFb) / curDiv) / curClkDiv);
++                      if (curFreq >= freq)
++                              curError = curFreq - freq;
++                      else
++                              curError = freq - curFreq;
++                      if (curError < bestError) {
++                              bestError = curError;
++                              bestPick->clkdiv = curClkDiv;
++                              bestPick->fbmult = curFb;
++                              bestPick->maindiv = curDiv;
++                              bestPick->freq = curFreq;
++                      }
++                      if (!curError)
++                              freq_found = true;
++                      curFb++;
++              }
++              curDiv++;
++      }
++      return bestPick->freq;
++}
++
++static struct dglnt_dynclk *clk_hw_to_dglnt_dynclk(struct clk_hw *clk_hw)
++{
++      return container_of(clk_hw, struct dglnt_dynclk, clk_hw);
++}
++
++
++static int dglnt_dynclk_enable(struct clk_hw *clk_hw)
++{
++      struct dglnt_dynclk *dglnt_dynclk = clk_hw_to_dglnt_dynclk(clk_hw);
++      unsigned int clock_state;
++
++      if (dglnt_dynclk->freq) {
++              writel(1, dglnt_dynclk->base + OFST_DISPLAY_CTRL);
++              do {
++                      clock_state = readl(dglnt_dynclk->base +
++                                          OFST_DISPLAY_STATUS);
++              } while (!clock_state);
++      }
++      return 0;
++}
++
++static void dglnt_dynclk_disable(struct clk_hw *clk_hw)
++{
++      struct dglnt_dynclk *dglnt_dynclk = clk_hw_to_dglnt_dynclk(clk_hw);
++
++      writel(0, dglnt_dynclk->base + OFST_DISPLAY_CTRL);
++}
++
++static int dglnt_dynclk_set_rate(struct clk_hw *clk_hw,
++      unsigned long rate, unsigned long parent_rate)
++{
++      struct dglnt_dynclk *dglnt_dynclk = clk_hw_to_dglnt_dynclk(clk_hw);
++      struct dglnt_dynclk_reg clkReg;
++      struct dglnt_dynclk_mode clkMode;
++
++      if (parent_rate == 0 || rate == 0)
++              return -EINVAL;
++      if (rate == dglnt_dynclk->freq)
++              return 0;
++
++      /*
++       * Convert from Hz to KHz, then multiply by five to account for
++       * BUFR division
++       */
++      rate = (rate + 100) / 200;
++      /* convert from Hz to KHz */
++      parent_rate = (parent_rate + 500) / 1000;
++      if (!dglnt_dynclk_find_mode(rate, parent_rate, &clkMode))
++              return -EINVAL;
++
++      /*
++       * Write to the PLL dynamic configuration registers to configure it
++       * with the calculated parameters.
++       */
++      dglnt_dynclk_find_reg(&clkReg, &clkMode);
++      dglnt_dynclk_write_reg(&clkReg, dglnt_dynclk->base);
++      dglnt_dynclk->freq = clkMode.freq * 200;
++      dglnt_dynclk_disable(clk_hw);
++      dglnt_dynclk_enable(clk_hw);
++
++      return 0;
++}
++
++static long dglnt_dynclk_round_rate(struct clk_hw *hw, unsigned long rate,
++      unsigned long *parent_rate)
++{
++      struct dglnt_dynclk_mode clkMode;
++
++      dglnt_dynclk_find_mode(((rate + 100) / 200),
++              ((*parent_rate) + 500) / 1000, &clkMode);
++
++      return (clkMode.freq * 200);
++}
++
++static unsigned long dglnt_dynclk_recalc_rate(struct clk_hw *clk_hw,
++      unsigned long parent_rate)
++{
++      struct dglnt_dynclk *dglnt_dynclk = clk_hw_to_dglnt_dynclk(clk_hw);
++
++      return dglnt_dynclk->freq;
++}
++
++
++static const struct clk_ops dglnt_dynclk_ops = {
++      .recalc_rate = dglnt_dynclk_recalc_rate,
++      .round_rate = dglnt_dynclk_round_rate,
++      .set_rate = dglnt_dynclk_set_rate,
++      .enable = dglnt_dynclk_enable,
++      .disable = dglnt_dynclk_disable,
++};
++
++static const struct of_device_id dglnt_dynclk_ids[] = {
++      { .compatible = "digilent,axi-dynclk", },
++      { },
++};
++MODULE_DEVICE_TABLE(of, dglnt_dynclk_ids);
++
++static int dglnt_dynclk_probe(struct platform_device *pdev)
++{
++      const struct of_device_id *id;
++      struct dglnt_dynclk *dglnt_dynclk;
++      struct clk_init_data init;
++      const char *parent_name;
++      const char *clk_name;
++      struct resource *mem;
++      struct clk *clk;
++
++      if (!pdev->dev.of_node)
++              return -ENODEV;
++
++      id = of_match_node(dglnt_dynclk_ids, pdev->dev.of_node);
++      if (!id)
++              return -ENODEV;
++
++      dglnt_dynclk = devm_kzalloc(&pdev->dev, sizeof(*dglnt_dynclk),
++                                  GFP_KERNEL);
++      if (!dglnt_dynclk)
++              return -ENOMEM;
++
++      mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      dglnt_dynclk->base = devm_ioremap_resource(&pdev->dev, mem);
++      if (IS_ERR(dglnt_dynclk->base))
++              return PTR_ERR(dglnt_dynclk->base);
++
++      parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
++      if (!parent_name)
++              return -EINVAL;
++
++      clk_name = pdev->dev.of_node->name;
++      of_property_read_string(pdev->dev.of_node, "clock-output-names",
++              &clk_name);
++
++      init.name = clk_name;
++      init.ops = &dglnt_dynclk_ops;
++      init.flags = 0;
++      init.parent_names = &parent_name;
++      init.num_parents = 1;
++
++      dglnt_dynclk->freq = 0;
++      dglnt_dynclk_disable(&dglnt_dynclk->clk_hw);
++
++      dglnt_dynclk->clk_hw.init = &init;
++      clk = devm_clk_register(&pdev->dev, &dglnt_dynclk->clk_hw);
++      if (IS_ERR(clk))
++              return PTR_ERR(clk);
++
++      return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
++                                 clk);
++}
++
++static int dglnt_dynclk_remove(struct platform_device *pdev)
++{
++      of_clk_del_provider(pdev->dev.of_node);
++
++      return 0;
++}
++
++static struct platform_driver dglnt_dynclk_driver = {
++      .driver = {
++              .name = "dglnt-dynclk",
++              .owner = THIS_MODULE,
++              .of_match_table = dglnt_dynclk_ids,
++      },
++      .probe = dglnt_dynclk_probe,
++      .remove = dglnt_dynclk_remove,
++};
++module_platform_driver(dglnt_dynclk_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Sam Bobrowicz <[email protected]>");
++MODULE_DESCRIPTION("CCF Driver for Digilent axi_dynclk IP Core");
+--
+1.9.1
+
--
1.9.1

--
_______________________________________________
meta-xilinx mailing list
[email protected]
https://lists.yoctoproject.org/listinfo/meta-xilinx
--
_______________________________________________
meta-xilinx mailing list
[email protected]
https://lists.yoctoproject.org/listinfo/meta-xilinx

Reply via email to