clk: bcm2835: Register the DSI0/DSI1 pixel clocks.
authorEric Anholt <eric@anholt.net>
Tue, 17 Jan 2017 20:31:56 +0000 (07:31 +1100)
committerStephen Boyd <sboyd@codeaurora.org>
Sat, 21 Jan 2017 00:22:55 +0000 (16:22 -0800)
The DSI pixel clocks are muxed from clocks generated in the analog phy
by the DSI driver.  In order to set them as parents, we need to do the
same name lookup dance on them as we do for our root oscillator.

Signed-off-by: Eric Anholt <eric@anholt.net>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Documentation/devicetree/bindings/clock/brcm,bcm2835-cprman.txt
drivers/clk/bcm/clk-bcm2835.c
include/dt-bindings/clock/bcm2835.h

index e56a1df3a9d3ca7fefbc5058072ee392c49b4cfc..dd906db34b328a581e4f4d99d11284544ff817f4 100644 (file)
@@ -16,7 +16,20 @@ Required properties:
 - #clock-cells:        Should be <1>. The permitted clock-specifier values can be
                  found in include/dt-bindings/clock/bcm2835.h
 - reg:         Specifies base physical address and size of the registers
-- clocks:      The external oscillator clock phandle
+- clocks:      phandles to the parent clocks used as input to the module, in
+                 the following order:
+
+                 - External oscillator
+                 - DSI0 byte clock
+                 - DSI0 DDR2 clock
+                 - DSI0 DDR clock
+                 - DSI1 byte clock
+                 - DSI1 DDR2 clock
+                 - DSI1 DDR clock
+
+                 Only external oscillator is required.  The DSI clocks may
+                 not be present, in which case their children will be
+                 unusable.
 
 Example:
 
index 3d0848d535d7ff850b15160c5c591f5856d8636c..2e7423d8f5bb364296da1b82de8e2809b28fcb67 100644 (file)
 #define LOCK_TIMEOUT_NS                100000000
 #define BCM2835_MAX_FB_RATE    1750000000u
 
+/*
+ * Names of clocks used within the driver that need to be replaced
+ * with an external parent's name.  This array is in the order that
+ * the clocks node in the DT references external clocks.
+ */
+static const char *const cprman_parent_names[] = {
+       "xosc",
+       "dsi0_byte",
+       "dsi0_ddr2",
+       "dsi0_ddr",
+       "dsi1_byte",
+       "dsi1_ddr2",
+       "dsi1_ddr",
+};
+
 struct bcm2835_cprman {
        struct device *dev;
        void __iomem *regs;
        spinlock_t regs_lock; /* spinlock for all clocks */
-       const char *osc_name;
+
+       /*
+        * Real names of cprman clock parents looked up through
+        * of_clk_get_parent_name(), which will be used in the
+        * parent_names[] arrays for clock registration.
+        */
+       const char *real_parent_names[ARRAY_SIZE(cprman_parent_names)];
 
        /* Must be last */
        struct clk_hw_onecell_data onecell;
@@ -907,6 +928,9 @@ static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock,
        const struct bcm2835_clock_data *data = clock->data;
        u64 temp;
 
+       if (data->int_bits == 0 && data->frac_bits == 0)
+               return parent_rate;
+
        /*
         * The divisor is a 12.12 fixed point field, but only some of
         * the bits are populated in any given clock.
@@ -930,7 +954,12 @@ static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw,
        struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
        struct bcm2835_cprman *cprman = clock->cprman;
        const struct bcm2835_clock_data *data = clock->data;
-       u32 div = cprman_read(cprman, data->div_reg);
+       u32 div;
+
+       if (data->int_bits == 0 && data->frac_bits == 0)
+               return parent_rate;
+
+       div = cprman_read(cprman, data->div_reg);
 
        return bcm2835_clock_rate_from_divisor(clock, parent_rate, div);
 }
@@ -1209,7 +1238,7 @@ static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
        memset(&init, 0, sizeof(init));
 
        /* All of the PLLs derive from the external oscillator. */
-       init.parent_names = &cprman->osc_name;
+       init.parent_names = &cprman->real_parent_names[0];
        init.num_parents = 1;
        init.name = data->name;
        init.ops = &bcm2835_pll_clk_ops;
@@ -1295,18 +1324,22 @@ static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman,
        struct bcm2835_clock *clock;
        struct clk_init_data init;
        const char *parents[1 << CM_SRC_BITS];
-       size_t i;
+       size_t i, j;
        int ret;
 
        /*
-        * Replace our "xosc" references with the oscillator's
-        * actual name.
+        * Replace our strings referencing parent clocks with the
+        * actual clock-output-name of the parent.
         */
        for (i = 0; i < data->num_mux_parents; i++) {
-               if (strcmp(data->parents[i], "xosc") == 0)
-                       parents[i] = cprman->osc_name;
-               else
-                       parents[i] = data->parents[i];
+               parents[i] = data->parents[i];
+
+               for (j = 0; j < ARRAY_SIZE(cprman_parent_names); j++) {
+                       if (strcmp(parents[i], cprman_parent_names[j]) == 0) {
+                               parents[i] = cprman->real_parent_names[j];
+                               break;
+                       }
+               }
        }
 
        memset(&init, 0, sizeof(init));
@@ -1432,6 +1465,47 @@ static const char *const bcm2835_clock_vpu_parents[] = {
        .parents = bcm2835_clock_vpu_parents,                           \
        __VA_ARGS__)
 
+/*
+ * DSI parent clocks.  The DSI byte/DDR/DDR2 clocks come from the DSI
+ * analog PHY.  The _inv variants are generated internally to cprman,
+ * but we don't use them so they aren't hooked up.
+ */
+static const char *const bcm2835_clock_dsi0_parents[] = {
+       "gnd",
+       "xosc",
+       "testdebug0",
+       "testdebug1",
+       "dsi0_ddr",
+       "dsi0_ddr_inv",
+       "dsi0_ddr2",
+       "dsi0_ddr2_inv",
+       "dsi0_byte",
+       "dsi0_byte_inv",
+};
+
+static const char *const bcm2835_clock_dsi1_parents[] = {
+       "gnd",
+       "xosc",
+       "testdebug0",
+       "testdebug1",
+       "dsi1_ddr",
+       "dsi1_ddr_inv",
+       "dsi1_ddr2",
+       "dsi1_ddr2_inv",
+       "dsi1_byte",
+       "dsi1_byte_inv",
+};
+
+#define REGISTER_DSI0_CLK(...) REGISTER_CLK(                           \
+       .num_mux_parents = ARRAY_SIZE(bcm2835_clock_dsi0_parents),      \
+       .parents = bcm2835_clock_dsi0_parents,                          \
+       __VA_ARGS__)
+
+#define REGISTER_DSI1_CLK(...) REGISTER_CLK(                           \
+       .num_mux_parents = ARRAY_SIZE(bcm2835_clock_dsi1_parents),      \
+       .parents = bcm2835_clock_dsi1_parents,                          \
+       __VA_ARGS__)
+
 /*
  * the real definition of all the pll, pll_dividers and clocks
  * these make use of the above REGISTER_* macros
@@ -1895,6 +1969,18 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
                .div_reg = CM_DSI1EDIV,
                .int_bits = 4,
                .frac_bits = 8),
+       [BCM2835_CLOCK_DSI0P]   = REGISTER_DSI0_CLK(
+               .name = "dsi0p",
+               .ctl_reg = CM_DSI0PCTL,
+               .div_reg = CM_DSI0PDIV,
+               .int_bits = 0,
+               .frac_bits = 0),
+       [BCM2835_CLOCK_DSI1P]   = REGISTER_DSI1_CLK(
+               .name = "dsi1p",
+               .ctl_reg = CM_DSI1PCTL,
+               .div_reg = CM_DSI1PDIV,
+               .int_bits = 0,
+               .frac_bits = 0),
 
        /* the gates */
 
@@ -1953,8 +2039,19 @@ static int bcm2835_clk_probe(struct platform_device *pdev)
        if (IS_ERR(cprman->regs))
                return PTR_ERR(cprman->regs);
 
-       cprman->osc_name = of_clk_get_parent_name(dev->of_node, 0);
-       if (!cprman->osc_name)
+       memcpy(cprman->real_parent_names, cprman_parent_names,
+              sizeof(cprman_parent_names));
+       of_clk_parent_fill(dev->of_node, cprman->real_parent_names,
+                          ARRAY_SIZE(cprman_parent_names));
+
+       /*
+        * Make sure the external oscillator has been registered.
+        *
+        * The other (DSI) clocks are not present on older device
+        * trees, which we still need to support for backwards
+        * compatibility.
+        */
+       if (!cprman->real_parent_names[0])
                return -ENODEV;
 
        platform_set_drvdata(pdev, cprman);
index 360e00cefd35679b49890234b5c369fb52b89e20..a0c812b0fa391d149b4f546db39bdc4bef207960 100644 (file)
@@ -64,3 +64,5 @@
 #define BCM2835_CLOCK_CAM1             46
 #define BCM2835_CLOCK_DSI0E            47
 #define BCM2835_CLOCK_DSI1E            48
+#define BCM2835_CLOCK_DSI0P            49
+#define BCM2835_CLOCK_DSI1P            50