summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Makefile3
-rw-r--r--drivers/clk/clk-ar933x.c175
-rw-r--r--drivers/clk/clk-divider-table.c119
-rw-r--r--drivers/clk/clk-divider.c234
-rw-r--r--drivers/clk/clk-fixed-factor.c33
-rw-r--r--drivers/clk/clk-gate.c13
-rw-r--r--drivers/clk/clk-mux.c10
-rw-r--r--drivers/clk/clk.c30
-rw-r--r--drivers/clk/mvebu/common.c4
-rw-r--r--drivers/clk/mxs/clk-imx28.c2
-rw-r--r--drivers/clk/mxs/clk.h6
-rw-r--r--drivers/clk/tegra/clk-periph.c4
-rw-r--r--drivers/clk/tegra/clk-tegra20.c2
-rw-r--r--drivers/clocksource/Kconfig23
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/dummy.c56
-rw-r--r--drivers/gpio/Kconfig7
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-davinci.c211
-rw-r--r--drivers/i2c/busses/i2c-imx.c2
-rw-r--r--drivers/i2c/i2c.c11
-rw-r--r--drivers/input/gpio_keys.c2
-rw-r--r--drivers/mci/mci-core.c2
-rw-r--r--drivers/mtd/nand/nand_omap_gpmc.c2
-rw-r--r--drivers/mtd/ubi/build.c2
-rw-r--r--drivers/net/ar231x.c6
-rw-r--r--drivers/net/at91_ether.c5
-rw-r--r--drivers/net/designware.c5
-rw-r--r--drivers/of/base.c7
-rw-r--r--drivers/serial/Kconfig7
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/serial_ar933x.c204
-rw-r--r--drivers/serial/serial_ar933x.h69
-rw-r--r--drivers/serial/serial_ns16550.c51
-rw-r--r--drivers/usb/gadget/composite.c9
-rw-r--r--drivers/usb/gadget/dfu.c4
-rw-r--r--drivers/video/Kconfig8
-rw-r--r--drivers/video/Makefile3
-rw-r--r--drivers/video/atmel_lcdfb_core.c6
-rw-r--r--drivers/video/edid.c909
-rw-r--r--drivers/video/edid.h138
-rw-r--r--drivers/video/fb.c113
-rw-r--r--drivers/video/imx-ipu-fb.c9
-rw-r--r--drivers/video/imx-ipu-v3/Kconfig14
-rw-r--r--drivers/video/imx-ipu-v3/Makefile5
-rw-r--r--drivers/video/imx-ipu-v3/imx-hdmi.c1297
-rw-r--r--drivers/video/imx-ipu-v3/imx-hdmi.h1032
-rw-r--r--drivers/video/imx-ipu-v3/imx-ipu-v3.h344
-rw-r--r--drivers/video/imx-ipu-v3/imx-ldb.c310
-rw-r--r--drivers/video/imx-ipu-v3/ipu-common.c836
-rw-r--r--drivers/video/imx-ipu-v3/ipu-dc.c392
-rw-r--r--drivers/video/imx-ipu-v3/ipu-di.c762
-rw-r--r--drivers/video/imx-ipu-v3/ipu-dmfc.c397
-rw-r--r--drivers/video/imx-ipu-v3/ipu-dp.c313
-rw-r--r--drivers/video/imx-ipu-v3/ipu-prv.h204
-rw-r--r--drivers/video/imx-ipu-v3/ipufb.c353
-rw-r--r--drivers/video/imx-ipu-v3/ipuv3-plane.c239
-rw-r--r--drivers/video/imx-ipu-v3/ipuv3-plane.h51
-rw-r--r--drivers/video/imx.c11
-rw-r--r--drivers/video/of_display_timing.c238
-rw-r--r--drivers/video/omap.c4
-rw-r--r--drivers/video/pxa.c5
-rw-r--r--drivers/video/s3c24xx.c6
-rw-r--r--drivers/video/sdl.c4
-rw-r--r--drivers/video/stm.c6
65 files changed, 9066 insertions, 266 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index f5c05920b1..0687b3cddd 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -1,8 +1,9 @@
obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed.o clk-divider.o clk-fixed-factor.o \
- clk-mux.o clk-gate.o clk-divider-table.o
+ clk-mux.o clk-gate.o
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
obj-$(CONFIG_ARCH_MVEBU) += mvebu/
obj-$(CONFIG_ARCH_MXS) += mxs/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_CLK_SOCFPGA) += socfpga.o
+obj-$(CONFIG_MACH_MIPS_ATH79) += clk-ar933x.o
diff --git a/drivers/clk/clk-ar933x.c b/drivers/clk/clk-ar933x.c
new file mode 100644
index 0000000000..d983387a93
--- /dev/null
+++ b/drivers/clk/clk-ar933x.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2013 Lucas Stach <l.stach@pengutronix.de>
+ *
+ * Based on the Linux Tegra clock code
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+
+#include <mach/ath79.h>
+#include <dt-bindings/clock/ar933x-clk.h>
+
+static struct clk *clks[AR933X_CLK_END];
+static struct clk_onecell_data clk_data;
+
+struct clk_ar933x {
+ struct clk clk;
+ void __iomem *base;
+ u32 div_shift;
+ u32 div_mask;
+ const char *parent;
+};
+
+static unsigned long clk_ar933x_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct clk_ar933x *f = container_of(clk, struct clk_ar933x, clk);
+ unsigned long rate;
+ unsigned long freq;
+ u32 clock_ctrl;
+ u32 cpu_config;
+ u32 t;
+
+ clock_ctrl = __raw_readl(f->base + AR933X_PLL_CLOCK_CTRL_REG);
+
+ if (clock_ctrl & AR933X_PLL_CLOCK_CTRL_BYPASS) {
+ rate = parent_rate;
+ } else {
+ cpu_config = __raw_readl(f->base + AR933X_PLL_CPU_CONFIG_REG);
+
+ t = (cpu_config >> AR933X_PLL_CPU_CONFIG_REFDIV_SHIFT) &
+ AR933X_PLL_CPU_CONFIG_REFDIV_MASK;
+ freq = parent_rate / t;
+
+ t = (cpu_config >> AR933X_PLL_CPU_CONFIG_NINT_SHIFT) &
+ AR933X_PLL_CPU_CONFIG_NINT_MASK;
+ freq *= t;
+
+ t = (cpu_config >> AR933X_PLL_CPU_CONFIG_OUTDIV_SHIFT) &
+ AR933X_PLL_CPU_CONFIG_OUTDIV_MASK;
+ if (t == 0)
+ t = 1;
+
+ freq >>= t;
+
+ t = ((clock_ctrl >> f->div_shift) & f->div_mask) + 1;
+ rate = freq / t;
+ }
+
+ return rate;
+}
+
+struct clk_ops clk_ar933x_ops = {
+ .recalc_rate = clk_ar933x_recalc_rate,
+};
+
+static struct clk *clk_ar933x(const char *name, const char *parent,
+ void __iomem *base, u32 div_shift, u32 div_mask)
+{
+ struct clk_ar933x *f = xzalloc(sizeof(*f));
+
+ f->parent = parent;
+ f->base = base;
+ f->div_shift = div_shift;
+ f->div_mask = div_mask;
+
+ f->clk.ops = &clk_ar933x_ops;
+ f->clk.name = name;
+ f->clk.parent_names = &f->parent;
+ f->clk.num_parents = 1;
+
+ clk_register(&f->clk);
+
+ return &f->clk;
+}
+
+static void ar933x_ref_clk_init(void __iomem *base)
+{
+ u32 t;
+ unsigned long ref_rate;
+
+ t = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP);
+ if (t & AR933X_BOOTSTRAP_REF_CLK_40)
+ ref_rate = (40 * 1000 * 1000);
+ else
+ ref_rate = (25 * 1000 * 1000);
+
+ clks[AR933X_CLK_REF] = clk_fixed("ref", ref_rate);
+}
+
+static void ar933x_pll_init(void __iomem *base)
+{
+ clks[AR933X_CLK_UART] = clk_fixed_factor("uart", "ref", 1, 1,
+ CLK_SET_RATE_PARENT);
+
+ clks[AR933X_CLK_CPU] = clk_ar933x("cpu", "ref", base,
+ AR933X_PLL_CLOCK_CTRL_CPU_DIV_SHIFT,
+ AR933X_PLL_CLOCK_CTRL_CPU_DIV_MASK);
+
+ clks[AR933X_CLK_DDR] = clk_ar933x("ddr", "ref", base,
+ AR933X_PLL_CLOCK_CTRL_DDR_DIV_SHIFT,
+ AR933X_PLL_CLOCK_CTRL_DDR_DIV_MASK);
+
+ clks[AR933X_CLK_AHB] = clk_ar933x("ahb", "ref", base,
+ AR933X_PLL_CLOCK_CTRL_AHB_DIV_SHIFT,
+ AR933X_PLL_CLOCK_CTRL_AHB_DIV_MASK);
+
+ clks[AR933X_CLK_WDT] = clk_fixed_factor("wdt", "ahb", 1, 1,
+ CLK_SET_RATE_PARENT);
+}
+
+static int ar933x_clk_probe(struct device_d *dev)
+{
+ void __iomem *base;
+
+ base = dev_request_mem_region(dev, 0);
+ if (!base)
+ return -EBUSY;
+
+ ar933x_ref_clk_init(base);
+ ar933x_pll_init(base);
+
+ clk_data.clks = clks;
+ clk_data.clk_num = ARRAY_SIZE(clks);
+ of_clk_add_provider(dev->device_node, of_clk_src_onecell_get,
+ &clk_data);
+
+ return 0;
+}
+
+static __maybe_unused struct of_device_id ar933x_clk_dt_ids[] = {
+ {
+ .compatible = "qca,ar933x-clk",
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct driver_d ar933x_clk_driver = {
+ .probe = ar933x_clk_probe,
+ .name = "ar933x_clk",
+ .of_compatible = DRV_OF_COMPAT(ar933x_clk_dt_ids),
+};
+
+static int ar933x_clk_init(void)
+{
+ return platform_driver_register(&ar933x_clk_driver);
+}
+postcore_initcall(ar933x_clk_init);
diff --git a/drivers/clk/clk-divider-table.c b/drivers/clk/clk-divider-table.c
deleted file mode 100644
index fd2d3fc3c1..0000000000
--- a/drivers/clk/clk-divider-table.c
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * clk-divider-table.c - generic barebox clock support. Based on Linux clk support
- *
- * Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * 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 <common.h>
-#include <io.h>
-#include <malloc.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-
-struct clk_divider_table {
- struct clk clk;
- u8 shift;
- u8 width;
- void __iomem *reg;
- const char *parent;
- const struct clk_div_table *table;
- int table_size;
- int max_div_index;
-};
-
-static int clk_divider_set_rate(struct clk *clk, unsigned long rate,
- unsigned long parent_rate)
-{
- struct clk_divider_table *div =
- container_of(clk, struct clk_divider_table, clk);
- unsigned int val;
- int i, div_index = -1;
- unsigned long best = 0;
-
- if (rate > parent_rate)
- rate = parent_rate;
- if (rate < parent_rate / div->table[div->max_div_index].div)
- rate = parent_rate / div->table[div->max_div_index].div;
-
- for (i = 0; i < div->table_size; i++) {
- unsigned long now = parent_rate / div->table[i].div;
-
- if (now <= rate && now >= best) {
- best = now;
- div_index = i;
- }
- }
-
- val = readl(div->reg);
- val &= ~(((1 << div->width) - 1) << div->shift);
- val |= div_index << div->shift;
- writel(val, div->reg);
-
- return 0;
-}
-
-static unsigned long clk_divider_recalc_rate(struct clk *clk,
- unsigned long parent_rate)
-{
- struct clk_divider_table *div =
- container_of(clk, struct clk_divider_table, clk);
- unsigned int val;
-
- val = readl(div->reg) >> div->shift;
- val &= (1 << div->width) - 1;
-
- if (val >= div->table_size)
- return 0;
-
- return parent_rate / div->table[val].div;
-}
-
-static struct clk_ops clk_divider_table_ops = {
- .set_rate = clk_divider_set_rate,
- .recalc_rate = clk_divider_recalc_rate,
-};
-
-struct clk *clk_divider_table(const char *name,
- const char *parent, void __iomem *reg, u8 shift, u8 width,
- const struct clk_div_table *table)
-{
- struct clk_divider_table *div = xzalloc(sizeof(*div));
- const struct clk_div_table *clkt;
- int ret, max_div = 0;
-
- div->shift = shift;
- div->reg = reg;
- div->width = width;
- div->parent = parent;
- div->clk.ops = &clk_divider_table_ops;
- div->clk.name = name;
- div->clk.parent_names = &div->parent;
- div->clk.num_parents = 1;
- div->table = table;
-
- for (clkt = div->table; clkt->div; clkt++) {
- if (clkt->div > max_div) {
- max_div = clkt->div;
- div->max_div_index = div->table_size;
- }
- div->table_size++;
- }
-
- ret = clk_register(&div->clk);
- if (ret) {
- free(div);
- return ERR_PTR(ret);
- }
-
- return &div->clk;
-}
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 3bf8105a8b..67783daabb 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -20,65 +20,196 @@
#include <linux/clk.h>
#include <linux/err.h>
-static unsigned int clk_divider_maxdiv(struct clk_divider *div)
+#define div_mask(d) ((1 << ((d)->width)) - 1)
+
+static unsigned int _get_maxdiv(struct clk_divider *divider)
{
- if (div->flags & CLK_DIVIDER_ONE_BASED)
- return (1 << div->width) - 1;
- return 1 << div->width;
+ if (divider->flags & CLK_DIVIDER_ONE_BASED)
+ return div_mask(divider);
+ return div_mask(divider) + 1;
}
-static int clk_divider_set_rate(struct clk *clk, unsigned long rate,
+static unsigned int _get_table_div(const struct clk_div_table *table,
+ unsigned int val)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->val == val)
+ return clkt->div;
+ return 0;
+}
+
+static unsigned int _get_div(struct clk_divider *divider, unsigned int val)
+{
+ if (divider->flags & CLK_DIVIDER_ONE_BASED)
+ return val;
+ if (divider->table)
+ return _get_table_div(divider->table, val);
+ return val + 1;
+}
+
+static unsigned int _get_table_val(const struct clk_div_table *table,
+ unsigned int div)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->div == div)
+ return clkt->val;
+ return 0;
+}
+
+static unsigned int _get_val(struct clk_divider *divider, unsigned int div)
+{
+ if (divider->flags & CLK_DIVIDER_ONE_BASED)
+ return div;
+ if (divider->table)
+ return _get_table_val(divider->table, div);
+ return div - 1;
+}
+
+static unsigned long clk_divider_recalc_rate(struct clk *clk,
unsigned long parent_rate)
{
- struct clk_divider *div = container_of(clk, struct clk_divider, clk);
- unsigned int val, divval;
+ struct clk_divider *divider = container_of(clk, struct clk_divider, clk);
+ unsigned int div, val;
+
+ val = readl(divider->reg) >> divider->shift;
+ val &= div_mask(divider);
+
+ div = _get_div(divider, val);
+
+ return parent_rate / div;
+}
+
+/*
+ * The reverse of DIV_ROUND_UP: The maximum number which
+ * divided by m is r
+ */
+#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
+
+static bool _is_valid_table_div(const struct clk_div_table *table,
+ unsigned int div)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->div == div)
+ return true;
+ return false;
+}
+
+static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
+{
+ if (divider->table)
+ return _is_valid_table_div(divider->table, div);
+ return true;
+}
+
+static int clk_divider_bestdiv(struct clk *clk, unsigned long rate,
+ unsigned long *best_parent_rate)
+{
+ struct clk_divider *divider = container_of(clk, struct clk_divider, clk);
+ int i, bestdiv = 0;
+ unsigned long parent_rate, best = 0, now, maxdiv;
+ unsigned long parent_rate_saved = *best_parent_rate;
- if (rate > parent_rate)
- rate = parent_rate;
if (!rate)
rate = 1;
- divval = DIV_ROUND_UP(parent_rate, rate);
- if (divval > clk_divider_maxdiv(div))
- divval = clk_divider_maxdiv(div);
+ maxdiv = _get_maxdiv(divider);
- if (!(div->flags & CLK_DIVIDER_ONE_BASED))
- divval--;
+ if (!(clk->flags & CLK_SET_RATE_PARENT)) {
+ parent_rate = *best_parent_rate;
+ bestdiv = DIV_ROUND_UP(parent_rate, rate);
+ bestdiv = bestdiv == 0 ? 1 : bestdiv;
+ bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
+ return bestdiv;
+ }
- val = readl(div->reg);
- val &= ~(((1 << div->width) - 1) << div->shift);
- val |= divval << div->shift;
- writel(val, div->reg);
+ /*
+ * The maximum divider we can use without overflowing
+ * unsigned long in rate * i below
+ */
+ maxdiv = min(ULONG_MAX / rate, maxdiv);
- return 0;
+ for (i = 1; i <= maxdiv; i++) {
+ if (!_is_valid_div(divider, i))
+ continue;
+ if (rate * i == parent_rate_saved) {
+ /*
+ * It's the most ideal case if the requested rate can be
+ * divided from parent clock without needing to change
+ * parent rate, so return the divider immediately.
+ */
+ *best_parent_rate = parent_rate_saved;
+ return i;
+ }
+ parent_rate = clk_round_rate(clk_get_parent(clk),
+ MULT_ROUND_UP(rate, i));
+ now = parent_rate / i;
+ if (now <= rate && now > best) {
+ bestdiv = i;
+ best = now;
+ *best_parent_rate = parent_rate;
+ }
+ }
+
+ if (!bestdiv) {
+ bestdiv = _get_maxdiv(divider);
+ *best_parent_rate = clk_round_rate(clk_get_parent(clk), 1);
+ }
+
+ return bestdiv;
}
-static unsigned long clk_divider_recalc_rate(struct clk *clk,
- unsigned long parent_rate)
+static long clk_divider_round_rate(struct clk *clk, unsigned long rate,
+ unsigned long *parent_rate)
{
- struct clk_divider *div = container_of(clk, struct clk_divider, clk);
- unsigned int val;
+ int div;
- val = readl(div->reg) >> div->shift;
- val &= (1 << div->width) - 1;
+ div = clk_divider_bestdiv(clk, rate, parent_rate);
- if (div->flags & CLK_DIVIDER_ONE_BASED) {
- if (!val)
- val++;
+ return *parent_rate / div;
+}
+
+static int clk_divider_set_rate(struct clk *clk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_divider *divider = container_of(clk, struct clk_divider, clk);
+ unsigned int div, value;
+ u32 val;
+
+ if (clk->flags & CLK_SET_RATE_PARENT) {
+ unsigned long best_parent_rate = parent_rate;
+ div = clk_divider_bestdiv(clk, rate, &best_parent_rate);
+ clk_set_rate(clk_get_parent(clk), best_parent_rate);
} else {
- val++;
+ div = parent_rate / rate;
}
- return parent_rate / val;
+ value = _get_val(divider, div);
+
+ if (value > div_mask(divider))
+ value = div_mask(divider);
+
+ val = readl(divider->reg);
+ val &= ~(div_mask(divider) << divider->shift);
+ val |= value << divider->shift;
+ writel(val, divider->reg);
+
+ return 0;
}
struct clk_ops clk_divider_ops = {
.set_rate = clk_divider_set_rate,
.recalc_rate = clk_divider_recalc_rate,
+ .round_rate = clk_divider_round_rate,
};
struct clk *clk_divider(const char *name, const char *parent,
- void __iomem *reg, u8 shift, u8 width)
+ void __iomem *reg, u8 shift, u8 width, unsigned flags)
{
struct clk_divider *div = xzalloc(sizeof(*div));
int ret;
@@ -89,6 +220,7 @@ struct clk *clk_divider(const char *name, const char *parent,
div->parent = parent;
div->clk.ops = &clk_divider_ops;
div->clk.name = name;
+ div->clk.flags = flags;
div->clk.parent_names = &div->parent;
div->clk.num_parents = 1;
@@ -102,12 +234,12 @@ struct clk *clk_divider(const char *name, const char *parent,
}
struct clk *clk_divider_one_based(const char *name, const char *parent,
- void __iomem *reg, u8 shift, u8 width)
+ void __iomem *reg, u8 shift, u8 width, unsigned flags)
{
struct clk_divider *div;
struct clk *clk;
- clk = clk_divider(name, parent, reg, shift, width);
+ clk = clk_divider(name, parent, reg, shift, width, flags);
if (IS_ERR(clk))
return clk;
@@ -116,3 +248,39 @@ struct clk *clk_divider_one_based(const char *name, const char *parent,
return clk;
}
+
+struct clk *clk_divider_table(const char *name,
+ const char *parent, void __iomem *reg, u8 shift, u8 width,
+ const struct clk_div_table *table, unsigned flags)
+{
+ struct clk_divider *div = xzalloc(sizeof(*div));
+ const struct clk_div_table *clkt;
+ int ret, max_div = 0;
+
+ div->shift = shift;
+ div->reg = reg;
+ div->width = width;
+ div->parent = parent;
+ div->clk.ops = &clk_divider_ops;
+ div->clk.name = name;
+ div->clk.flags = flags;
+ div->clk.parent_names = &div->parent;
+ div->clk.num_parents = 1;
+ div->table = table;
+
+ for (clkt = div->table; clkt->div; clkt++) {
+ if (clkt->div > max_div) {
+ max_div = clkt->div;
+ div->max_div_index = div->table_size;
+ }
+ div->table_size++;
+ }
+
+ ret = clk_register(&div->clk);
+ if (ret) {
+ free(div);
+ return ERR_PTR(ret);
+ }
+
+ return &div->clk;
+}
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
index f0ecbd3e59..cb531b146b 100644
--- a/drivers/clk/clk-fixed-factor.c
+++ b/drivers/clk/clk-fixed-factor.c
@@ -35,12 +35,42 @@ static unsigned long clk_fixed_factor_recalc_rate(struct clk *clk,
return (parent_rate / f->div) * f->mult;
}
+static long clk_factor_round_rate(struct clk *clk, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_fixed_factor *fix = container_of(clk, struct clk_fixed_factor, clk);
+
+ if (clk->flags & CLK_SET_RATE_PARENT) {
+ unsigned long best_parent;
+
+ best_parent = (rate / fix->mult) * fix->div;
+ *prate = clk_round_rate(clk_get_parent(clk), best_parent);
+ }
+
+ return (*prate / fix->div) * fix->mult;
+}
+
+static int clk_factor_set_rate(struct clk *clk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_fixed_factor *fix = container_of(clk, struct clk_fixed_factor, clk);
+
+ if (clk->flags & CLK_SET_RATE_PARENT) {
+ printk("%s: %ld -> parent %ld\n", __func__, rate, rate * fix->div / fix->mult);
+ return clk_set_rate(clk_get_parent(clk), rate * fix->div / fix->mult);
+ }
+
+ return 0;
+}
+
static struct clk_ops clk_fixed_factor_ops = {
+ .set_rate = clk_factor_set_rate,
+ .round_rate = clk_factor_round_rate,
.recalc_rate = clk_fixed_factor_recalc_rate,
};
struct clk *clk_fixed_factor(const char *name,
- const char *parent, unsigned int mult, unsigned int div)
+ const char *parent, unsigned int mult, unsigned int div, unsigned flags)
{
struct clk_fixed_factor *f = xzalloc(sizeof(*f));
int ret;
@@ -50,6 +80,7 @@ struct clk *clk_fixed_factor(const char *name,
f->parent = parent;
f->clk.ops = &clk_fixed_factor_ops;
f->clk.name = name;
+ f->clk.flags = flags;
f->clk.parent_names = &f->parent;
f->clk.num_parents = 1;
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index baec85598d..b298b193dc 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -77,13 +77,15 @@ static int clk_gate_is_enabled(struct clk *clk)
}
static struct clk_ops clk_gate_ops = {
+ .set_rate = clk_parent_set_rate,
+ .round_rate = clk_parent_round_rate,
.enable = clk_gate_enable,
.disable = clk_gate_disable,
.is_enabled = clk_gate_is_enabled,
};
struct clk *clk_gate_alloc(const char *name, const char *parent,
- void __iomem *reg, u8 shift)
+ void __iomem *reg, u8 shift, unsigned flags)
{
struct clk_gate *g = xzalloc(sizeof(*g));
@@ -92,6 +94,7 @@ struct clk *clk_gate_alloc(const char *name, const char *parent,
g->shift = shift;
g->clk.ops = &clk_gate_ops;
g->clk.name = name;
+ g->clk.flags = flags;
g->clk.parent_names = &g->parent;
g->clk.num_parents = 1;
@@ -106,12 +109,12 @@ void clk_gate_free(struct clk *clk_gate)
}
struct clk *clk_gate(const char *name, const char *parent, void __iomem *reg,
- u8 shift)
+ u8 shift, unsigned flags)
{
struct clk *g;
int ret;
- g = clk_gate_alloc(name , parent, reg, shift);
+ g = clk_gate_alloc(name , parent, reg, shift, flags);
ret = clk_register(g);
if (ret) {
@@ -123,12 +126,12 @@ struct clk *clk_gate(const char *name, const char *parent, void __iomem *reg,
}
struct clk *clk_gate_inverted(const char *name, const char *parent,
- void __iomem *reg, u8 shift)
+ void __iomem *reg, u8 shift, unsigned flags)
{
struct clk *clk;
struct clk_gate *g;
- clk = clk_gate(name, parent, reg, shift);
+ clk = clk_gate(name, parent, reg, shift, flags);
if (IS_ERR(clk))
return clk;
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index b22bdd1ec7..4ce86f43d1 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -51,12 +51,15 @@ static int clk_mux_set_parent(struct clk *clk, u8 idx)
}
static struct clk_ops clk_mux_ops = {
+ .set_rate = clk_parent_set_rate,
+ .round_rate = clk_parent_round_rate,
.get_parent = clk_mux_get_parent,
.set_parent = clk_mux_set_parent,
};
struct clk *clk_mux_alloc(const char *name, void __iomem *reg,
- u8 shift, u8 width, const char **parents, u8 num_parents)
+ u8 shift, u8 width, const char **parents, u8 num_parents,
+ unsigned flags)
{
struct clk_mux *m = xzalloc(sizeof(*m));
@@ -65,6 +68,7 @@ struct clk *clk_mux_alloc(const char *name, void __iomem *reg,
m->width = width;
m->clk.ops = &clk_mux_ops;
m->clk.name = name;
+ m->clk.flags = flags;
m->clk.parent_names = parents;
m->clk.num_parents = num_parents;
@@ -79,12 +83,12 @@ void clk_mux_free(struct clk *clk_mux)
}
struct clk *clk_mux(const char *name, void __iomem *reg,
- u8 shift, u8 width, const char **parents, u8 num_parents)
+ u8 shift, u8 width, const char **parents, u8 num_parents, unsigned flags)
{
struct clk *m;
int ret;
- m = clk_mux_alloc(name, reg, shift, width, parents, num_parents);
+ m = clk_mux_alloc(name, reg, shift, width, parents, num_parents, flags);
ret = clk_register(m);
if (ret) {
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 31d73c028c..0d259413a9 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -104,9 +104,19 @@ unsigned long clk_get_rate(struct clk *clk)
long clk_round_rate(struct clk *clk, unsigned long rate)
{
+ unsigned long parent_rate = 0;
+ struct clk *parent;
+
if (IS_ERR(clk))
return 0;
+ parent = clk_get_parent(clk);
+ if (parent)
+ parent_rate = clk_get_rate(parent);
+
+ if (clk->ops->round_rate)
+ return clk->ops->round_rate(clk, rate, &parent_rate);
+
return clk_get_rate(clk);
}
@@ -249,11 +259,31 @@ int clk_is_enabled(struct clk *clk)
return clk_is_enabled(clk);
}
+/*
+ * Generic struct clk_ops callbacks
+ */
int clk_is_enabled_always(struct clk *clk)
{
return 1;
}
+long clk_parent_round_rate(struct clk *clk, unsigned long rate,
+ unsigned long *prate)
+{
+ if (!(clk->flags & CLK_SET_RATE_PARENT))
+ return *prate;
+
+ return clk_round_rate(clk_get_parent(clk), rate);
+}
+
+int clk_parent_set_rate(struct clk *clk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ if (!(clk->flags & CLK_SET_RATE_PARENT))
+ return 0;
+ return clk_set_rate(clk_get_parent(clk), rate);
+}
+
#if defined(CONFIG_OFTREE) && defined(CONFIG_COMMON_CLK_OF_PROVIDER)
/**
* struct of_clk_provider - Clock provider registration structure
diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
index 37cc1568a3..658ce3e81a 100644
--- a/drivers/clk/mvebu/common.c
+++ b/drivers/clk/mvebu/common.c
@@ -87,7 +87,7 @@ int mvebu_coreclk_probe(struct device_d *dev)
2+n, &rclk_name);
desc->get_clk_ratio(base, desc->ratios[n].id, &mult, &div);
clk_data.clks[2+n] = clk_fixed_factor(rclk_name, cpuclk_name,
- mult, div);
+ mult, div, 0);
WARN_ON(IS_ERR(clk_data.clks[2+n]));
};
@@ -188,7 +188,7 @@ int mvebu_clk_gating_probe(struct device_d *dev)
(desc[n].parent) ? desc[n].parent : default_parent;
gate->bit_idx = desc[n].bit_idx;
gate->clk = clk_gate(desc[n].name, parent,
- base, desc[n].bit_idx);
+ base, desc[n].bit_idx, 0);
WARN_ON(IS_ERR(gate->clk));
}
diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c
index 05b3c2a04d..934a1940fb 100644
--- a/drivers/clk/mxs/clk-imx28.c
+++ b/drivers/clk/mxs/clk-imx28.c
@@ -128,7 +128,7 @@ int __init mx28_clocks_init(void __iomem *regs)
clks[fec] = mxs_clk_gate("fec", "fec_sleep", ENET, 30);
clks[usb0_phy] = mxs_clk_gate("usb0_phy", "pll0", PLL0CTRL0, 18);
clks[usb1_phy] = mxs_clk_gate("usb1_phy", "pll1", PLL1CTRL0, 18);
- clks[enet_out] = clk_gate("enet_out", "pll2", ENET, 18);
+ clks[enet_out] = clk_gate("enet_out", "pll2", ENET, 18, 0);
clks[lcdif_comp] = mxs_clk_lcdif("lcdif_comp", clks[ref_pix],
clks[lcdif_div], clks[lcdif]);
diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h
index b4fcfa0090..7bab7b5e6e 100644
--- a/drivers/clk/mxs/clk.h
+++ b/drivers/clk/mxs/clk.h
@@ -34,19 +34,19 @@ static inline struct clk *mxs_clk_fixed(const char *name, int rate)
static inline struct clk *mxs_clk_gate(const char *name,
const char *parent_name, void __iomem *reg, u8 shift)
{
- return clk_gate_inverted(name, parent_name, reg, shift);
+ return clk_gate_inverted(name, parent_name, reg, shift, 0);
}
static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parent_names, int num_parents)
{
- return clk_mux(name, reg, shift, width, parent_names, num_parents);
+ return clk_mux(name, reg, shift, width, parent_names, num_parents, 0);
}
static inline struct clk *mxs_clk_fixed_factor(const char *name,
const char *parent_name, unsigned int mult, unsigned int div)
{
- return clk_fixed_factor(name, parent_name, mult, div);
+ return clk_fixed_factor(name, parent_name, mult, div, 0);
}
#endif /* __MXS_CLK_H */
diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
index e969495875..c970f63afa 100644
--- a/drivers/clk/tegra/clk-periph.c
+++ b/drivers/clk/tegra/clk-periph.c
@@ -140,12 +140,12 @@ struct clk *_tegra_clk_register_periph(const char *name,
}
periph->mux = clk_mux_alloc(NULL, clk_base + reg_offset, 30, 2,
- parent_names, num_parents);
+ parent_names, num_parents, 0);
if (!periph->mux)
goto out_mux;
periph->gate = clk_gate_alloc(NULL, NULL, clk_base + 0x10 +
- ((id >> 3) & 0xc), id & 0x1f);
+ ((id >> 3) & 0xc), id & 0x1f, 0);
if (!periph->gate)
goto out_gate;
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
index f68c811a8d..cfb719f431 100644
--- a/drivers/clk/tegra/clk-tegra20.c
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -62,7 +62,7 @@ static void tegra20_osc_clk_init(void)
clks[clk_32k] = clk_fixed("clk_32k", 32768);
clks[pll_ref] = clk_fixed_factor("pll_ref", "clk_m", 1,
- get_pll_ref_div());
+ get_pll_ref_div(), 0);
}
/* PLL frequency tables */
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 4ef25ec45b..43974f03c2 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -14,6 +14,29 @@ config CLOCKSOURCE_CLPS711X
bool
depends on ARCH_CLPS711X
+config CLOCKSOURCE_DUMMY
+ bool "Enable dummy software-only clocksource"
+ help
+ When porting barebox to a new SoC there might be a case
+ of broken or absent clocksource. This causes barebox serial
+ console to be non functional.
+ To solve the problem this software-only clocksource driver is used.
+ WARNING!!! This clocksource doesn't provide correct timing.
+ To adjust this clocksource please use CONFIG_CLOCKSOURCE_DUMMY_RATE.
+ The bigger rate valuest makes clocksource "faster".
+ It's possible to add this clocksource unconditionally.
+ This clocksource starts very early (pure_initcall) so
+ real clocksource will take over.
+ This can help if initialization order is wrong so that
+ the time functions are used before the real clocksource
+ was initialized.
+
+config CLOCKSOURCE_DUMMY_RATE
+ int
+ prompt "dummy clocksource rate"
+ depends on CLOCKSOURCE_DUMMY
+ default 1000
+
config CLOCKSOURCE_MVEBU
bool
depends on ARCH_MVEBU
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 25b7f460da..834a15d1e9 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_AMBA_SP804) += amba-sp804.o
obj-$(CONFIG_ARM_SMP_TWD) += arm_smp_twd.o
obj-$(CONFIG_CLOCKSOURCE_BCM2835) += bcm2835.o
obj-$(CONFIG_CLOCKSOURCE_CLPS711X) += clps711x.o
+obj-$(CONFIG_CLOCKSOURCE_DUMMY) += dummy.o
obj-$(CONFIG_CLOCKSOURCE_MVEBU) += mvebu.o
obj-$(CONFIG_CLOCKSOURCE_NOMADIK) += nomadik.o
obj-$(CONFIG_CLOCKSOURCE_ORION) += orion.o
diff --git a/drivers/clocksource/dummy.c b/drivers/clocksource/dummy.c
new file mode 100644
index 0000000000..154a8cd672
--- /dev/null
+++ b/drivers/clocksource/dummy.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * This file is part of barebox.
+ * See file CREDITS for list of people who contributed to this project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 <common.h>
+#include <init.h>
+#include <clock.h>
+
+static uint64_t dummy_counter;
+
+static uint64_t dummy_cs_read(void)
+{
+ static int first;
+
+ if (!first) {
+ pr_warn("Warning: Using dummy clocksource\n");
+ first = 1;
+ }
+
+ dummy_counter += CONFIG_CLOCKSOURCE_DUMMY_RATE;
+
+ return dummy_counter;
+}
+
+static struct clocksource dummy_cs = {
+ .read = dummy_cs_read,
+ .mask = CLOCKSOURCE_MASK(32),
+};
+
+static int clocksource_init(void)
+{
+ dummy_counter = 0;
+
+ clocks_calc_mult_shift(&dummy_cs.mult, &dummy_cs.shift,
+ 100000000, NSEC_PER_SEC, 10);
+
+ pr_debug("clocksource_init: mult=%08x, shift=%08x\n",
+ dummy_cs.mult, dummy_cs.shift);
+ init_clock(&dummy_cs);
+
+ return 0;
+}
+pure_initcall(clocksource_init);
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7c426563e7..7302955d87 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -20,6 +20,13 @@ config GPIO_CLPS711X
help
Say yes here to enable the GPIO driver for the CLPS711X CPUs
+config GPIO_DAVINCI
+ bool "TI Davinci/Keystone GPIO support"
+ default y if ARCH_DAVINCI
+ depends on ARM && ARCH_DAVINCI
+ help
+ Say yes here to enable GPIO support for TI Davinci/Keystone SoCs.
+
config GPIO_GENERIC_PLATFORM
bool "Generic memory-mapped GPIO controller support"
select GPIO_GENERIC
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index b7c536d21b..68a76a3745 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_GPIOLIB) += gpiolib.o
obj-$(CONFIG_GPIO_BCM2835) += gpio-bcm2835.o
+obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
obj-$(CONFIG_GPIO_IMX) += gpio-imx.o
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
new file mode 100644
index 0000000000..7d15b855f0
--- /dev/null
+++ b/drivers/gpio/gpio-davinci.c
@@ -0,0 +1,211 @@
+/*
+ * TI DaVinci GPIO Support
+ *
+ * Copyright (c) 2006-2007 David Brownell
+ * Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com>
+ * Copyright (c) 2014 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <common.h>
+#include <gpio.h>
+#include <init.h>
+#include <io.h>
+#include <linux/err.h>
+
+#define readl_relaxed readl
+#define writel_relaxed writel
+
+struct davinci_gpio_regs {
+ u32 dir;
+ u32 out_data;
+ u32 set_data;
+ u32 clr_data;
+ u32 in_data;
+ u32 set_rising;
+ u32 clr_rising;
+ u32 set_falling;
+ u32 clr_falling;
+ u32 intstat;
+};
+
+struct davinci_gpio_controller {
+ struct gpio_chip chip;
+ /* Serialize access to GPIO registers */
+ void __iomem *regs;
+ void __iomem *set_data;
+ void __iomem *clr_data;
+ void __iomem *in_data;
+};
+
+#define chip2controller(chip) \
+ container_of(chip, struct davinci_gpio_controller, chip)
+
+static struct davinci_gpio_regs __iomem *gpio2regs(void __iomem *gpio_base,
+ unsigned gpio)
+{
+ void __iomem *ptr;
+
+ if (gpio < 32 * 1)
+ ptr = gpio_base + 0x10;
+ else if (gpio < 32 * 2)
+ ptr = gpio_base + 0x38;
+ else if (gpio < 32 * 3)
+ ptr = gpio_base + 0x60;
+ else if (gpio < 32 * 4)
+ ptr = gpio_base + 0x88;
+ else if (gpio < 32 * 5)
+ ptr = gpio_base + 0xb0;
+ else
+ ptr = NULL;
+ return ptr;
+}
+
+static int davinci_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+ struct davinci_gpio_controller *d = chip2controller(chip);
+ struct davinci_gpio_regs __iomem *g = d->regs;
+
+ return ((readl_relaxed(&g->dir)) & (1 << offset)) ?
+ GPIOF_DIR_IN : GPIOF_DIR_OUT;
+}
+
+static inline int __davinci_direction(struct gpio_chip *chip,
+ unsigned offset, bool out, int value)
+{
+ struct davinci_gpio_controller *d = chip2controller(chip);
+ struct davinci_gpio_regs __iomem *g = d->regs;
+ u32 temp;
+ u32 mask = 1 << offset;
+
+ temp = readl_relaxed(&g->dir);
+ if (out) {
+ temp &= ~mask;
+ writel_relaxed(mask, value ? &g->set_data : &g->clr_data);
+ } else {
+ temp |= mask;
+ }
+ writel_relaxed(temp, &g->dir);
+
+ return 0;
+}
+
+static int davinci_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ return __davinci_direction(chip, offset, false, 0);
+}
+
+static int
+davinci_direction_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+ return __davinci_direction(chip, offset, true, value);
+}
+
+/*
+ * Read the pin's value (works even if it's set up as output);
+ * returns zero/nonzero.
+ *
+ * Note that changes are synched to the GPIO clock, so reading values back
+ * right after you've set them may give old values.
+ */
+static int davinci_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct davinci_gpio_controller *d = chip2controller(chip);
+ struct davinci_gpio_regs __iomem *g = d->regs;
+
+ return (1 << offset) & readl_relaxed(&g->in_data);
+}
+
+/*
+ * Assuming the pin is muxed as a gpio output, set its output value.
+ */
+static void
+davinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct davinci_gpio_controller *d = chip2controller(chip);
+ struct davinci_gpio_regs __iomem *g = d->regs;
+
+ writel_relaxed((1 << offset), value ? &g->set_data : &g->clr_data);
+}
+
+static struct gpio_ops davinci_gpio_ops = {
+ .direction_input = davinci_direction_in,
+ .direction_output = davinci_direction_out,
+ .get_direction = davinci_get_direction,
+ .get = davinci_gpio_get,
+ .set = davinci_gpio_set,
+};
+
+static int davinci_gpio_probe(struct device_d *dev)
+{
+ void __iomem *gpio_base;
+ int ret;
+ u32 val;
+ int i, base;
+ unsigned ngpio;
+ struct davinci_gpio_controller *chips;
+
+ ret = of_property_read_u32(dev->device_node, "ti,ngpio", &val);
+ if (ret) {
+ dev_err(dev, "could not read 'ti,ngpio' property\n");
+ return -EINVAL;
+ }
+
+ ngpio = val;
+
+ if (WARN_ON(ARCH_NR_GPIOS < ngpio))
+ ngpio = ARCH_NR_GPIOS;
+
+ chips = xzalloc((ngpio / 32 + 1) * sizeof(*chips));
+
+ gpio_base = dev_request_mem_region(dev, 0);
+ if (!gpio_base) {
+ dev_err(dev, "could not get memory region\n");
+ return -ENODEV;
+ }
+
+ for (i = 0, base = 0; base < ngpio; i++, base += 32) {
+ struct davinci_gpio_regs __iomem *regs;
+ struct gpio_chip *gc;
+
+ gc = &chips[i].chip;
+ gc->ops = &davinci_gpio_ops;
+
+ gc->dev = dev;
+ gc->base = base;
+ gc->ngpio = ngpio - base;
+ if (gc->ngpio > 32)
+ gc->ngpio = 32;
+
+ regs = gpio2regs(gpio_base, base);
+ chips[i].regs = regs;
+ chips[i].set_data = &regs->set_data;
+ chips[i].clr_data = &regs->clr_data;
+ chips[i].in_data = &regs->in_data;
+
+ gpiochip_add(gc);
+ }
+
+ return 0;
+}
+
+static struct of_device_id davinci_gpio_ids[] = {
+ { .compatible = "ti,dm6441-gpio", },
+ { /* sentinel */ },
+};
+
+static struct driver_d davinci_gpio_driver = {
+ .name = "davinci_gpio",
+ .probe = davinci_gpio_probe,
+ .of_compatible = DRV_OF_COMPAT(davinci_gpio_ids),
+};
+
+static int davinci_gpio_drv_reg(void)
+{
+ return platform_driver_register(&davinci_gpio_driver);
+}
+coredevice_initcall(davinci_gpio_drv_reg);
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index f657c287f7..6b390478fa 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -587,4 +587,4 @@ static struct driver_d i2c_fsl_driver = {
.name = DRIVER_NAME,
.of_compatible = DRV_OF_COMPAT(imx_i2c_dt_ids),
};
-device_platform_driver(i2c_fsl_driver);
+coredevice_platform_driver(i2c_fsl_driver);
diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c
index e289a3569b..9873957015 100644
--- a/drivers/i2c/i2c.c
+++ b/drivers/i2c/i2c.c
@@ -405,6 +405,17 @@ struct i2c_adapter *i2c_get_adapter(int busnum)
return NULL;
}
+struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
+{
+ struct i2c_adapter *adap;
+
+ list_for_each_entry(adap, &adapter_list, list)
+ if (adap->dev.device_node == node)
+ return adap;
+
+ return NULL;
+}
+
/**
* i2c_add_numbered_adapter - declare i2c adapter, use static bus number
* @adapter: the adapter to register (with adap->nr initialized)
diff --git a/drivers/input/gpio_keys.c b/drivers/input/gpio_keys.c
index 9486b9a786..d017594662 100644
--- a/drivers/input/gpio_keys.c
+++ b/drivers/input/gpio_keys.c
@@ -121,7 +121,7 @@ static int gpio_keys_probe_dt(struct gpio_keys *gk, struct device_d *dev)
struct device_node *npkey, *np = dev->device_node;
int i = 0, ret;
- if (!IS_ENABLED(CONFIG_OFDEVICE))
+ if (!IS_ENABLED(CONFIG_OFDEVICE) || !IS_ENABLED(CONFIG_OF_GPIO))
return -ENODEV;
gk->nbuttons = of_get_child_count(np);
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index a18b555f27..1dca0c0137 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1813,7 +1813,7 @@ void mci_of_parse(struct mci_host *host)
break;
default:
dev_err(host->hw_dev,
- "Invalid \"bus-width\" value %ud!\n", bus_width);
+ "Invalid \"bus-width\" value %u!\n", bus_width);
}
/* f_max is obtained from the optional "max-frequency" property */
diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index 3053a8e891..59712b8119 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/nand_omap_gpmc.c
@@ -940,7 +940,7 @@ static int gpmc_nand_probe(struct device_d *pdev)
switch (pdata->device_width) {
case 0:
- printk("probe buswidth\n");
+ dev_dbg(pdev, "probing buswidth\n");
nand->options |= NAND_BUSWIDTH_AUTO;
break;
case 8:
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index da21f69e7e..b908fcda0b 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -104,6 +104,8 @@ struct ubi_device *ubi_get_device(int ubi_num)
struct ubi_device *ubi;
ubi = ubi_devices[ubi_num];
+ if (!ubi)
+ return NULL;
ubi->ref_count++;
diff --git a/drivers/net/ar231x.c b/drivers/net/ar231x.c
index 5c091140ac..515de17b10 100644
--- a/drivers/net/ar231x.c
+++ b/drivers/net/ar231x.c
@@ -419,15 +419,9 @@ static int ar231x_eth_probe(struct device_d *dev)
return 0;
}
-static void ar231x_eth_remove(struct device_d *dev)
-{
-
-}
-
static struct driver_d ar231x_eth_driver = {
.name = "ar231x_eth",
.probe = ar231x_eth_probe,
- .remove = ar231x_eth_remove,
};
static int ar231x_eth_driver_init(void)
diff --git a/drivers/net/at91_ether.c b/drivers/net/at91_ether.c
index 25924cf7e2..bf2f957a36 100644
--- a/drivers/net/at91_ether.c
+++ b/drivers/net/at91_ether.c
@@ -357,13 +357,8 @@ static int at91_ether_probe(struct device_d *dev)
return 0;
}
-static void at91_ether_remove(struct device_d *dev)
-{
-}
-
static struct driver_d at91_ether_driver = {
.name = "at91_ether",
.probe = at91_ether_probe,
- .remove = at91_ether_remove,
};
device_platform_driver(at91_ether_driver);
diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index ecb4656e9d..e706f54b31 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -477,10 +477,6 @@ static int dwc_ether_probe(struct device_d *dev)
return 0;
}
-static void dwc_ether_remove(struct device_d *dev)
-{
-}
-
static __maybe_unused struct of_device_id dwc_ether_compatible[] = {
{
.compatible = "snps,dwmac-3.70a",
@@ -493,7 +489,6 @@ static __maybe_unused struct of_device_id dwc_ether_compatible[] = {
static struct driver_d dwc_ether_driver = {
.name = "designware_eth",
.probe = dwc_ether_probe,
- .remove = dwc_ether_remove,
.of_compatible = DRV_OF_COMPAT(dwc_ether_compatible),
};
device_platform_driver(dwc_ether_driver);
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 104b6daeaf..c440a69e6c 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1841,8 +1841,11 @@ int of_device_is_stdout_path(struct device_d *dev)
struct device_node *dn;
const char *name;
- name = of_get_property(of_chosen, "linux,stdout-path", NULL);
- if (name == NULL)
+ name = of_get_property(of_chosen, "stdout-path", NULL);
+ if (!name)
+ name = of_get_property(of_chosen, "linux,stdout-path", NULL);
+
+ if (!name)
return 0;
dn = of_find_node_by_path(name);
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 11fc155074..f51c6e6b02 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -14,6 +14,13 @@ config SERIAL_AMBA_PL011
If unsure, say N.
+config DRIVER_SERIAL_AR933X
+ bool "AR933X serial driver"
+ depends on MACH_MIPS_ATH79
+ help
+ If you have an Atheros AR933X SOC based board and want to use the
+ built-in UART of the SoC, say Y to this option.
+
config DRIVER_SERIAL_IMX
depends on ARCH_IMX
default y
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 93790b5349..e1865f725a 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_DRIVER_SERIAL_ARM_DCC) += arm_dcc.o
obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
+obj-$(CONFIG_DRIVER_SERIAL_AR933X) += serial_ar933x.o
obj-$(CONFIG_DRIVER_SERIAL_IMX) += serial_imx.o
obj-$(CONFIG_DRIVER_SERIAL_STM378X) += stm-serial.o
obj-$(CONFIG_DRIVER_SERIAL_ATMEL) += atmel.o
diff --git a/drivers/serial/serial_ar933x.c b/drivers/serial/serial_ar933x.c
new file mode 100644
index 0000000000..27cccba119
--- /dev/null
+++ b/drivers/serial/serial_ar933x.c
@@ -0,0 +1,204 @@
+/*
+ * based on linux.git/drivers/tty/serial/serial_ar933x.c
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 <common.h>
+#include <driver.h>
+#include <init.h>
+#include <malloc.h>
+#include <io.h>
+#include <asm-generic/div64.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include "serial_ar933x.h"
+
+#define AR933X_UART_MAX_SCALE 0xff
+#define AR933X_UART_MAX_STEP 0xffff
+
+struct ar933x_uart_priv {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static inline void ar933x_serial_writel(struct console_device *cdev,
+ u32 b, int offset)
+{
+ struct ar933x_uart_priv *priv = cdev->dev->priv;
+
+ cpu_writel(b, priv->base + offset);
+}
+
+static inline u32 ar933x_serial_readl(struct console_device *cdev,
+ int offset)
+{
+ struct ar933x_uart_priv *priv = cdev->dev->priv;
+
+ return cpu_readl(priv->base + offset);
+}
+
+/*
+ * baudrate = (clk / (scale + 1)) * (step * (1 / 2^17))
+ * take from linux.
+ */
+static unsigned long ar933x_uart_get_baud(unsigned int clk,
+ unsigned int scale,
+ unsigned int step)
+{
+ u64 t;
+ u32 div;
+
+ div = (2 << 16) * (scale + 1);
+ t = clk;
+ t *= step;
+ t += (div / 2);
+ do_div(t, div);
+
+ return t;
+}
+
+static void ar933x_uart_get_scale_step(unsigned int clk,
+ unsigned int baud,
+ unsigned int *scale,
+ unsigned int *step)
+{
+ unsigned int tscale;
+ long min_diff;
+
+ *scale = 0;
+ *step = 0;
+
+ min_diff = baud;
+ for (tscale = 0; tscale < AR933X_UART_MAX_SCALE; tscale++) {
+ u64 tstep;
+ int diff;
+
+ tstep = baud * (tscale + 1);
+ tstep *= (2 << 16);
+ do_div(tstep, clk);
+
+ if (tstep > AR933X_UART_MAX_STEP)
+ break;
+
+ diff = abs(ar933x_uart_get_baud(clk, tscale, tstep) - baud);
+ if (diff < min_diff) {
+ min_diff = diff;
+ *scale = tscale;
+ *step = tstep;
+ }
+ }
+}
+
+static int ar933x_serial_setbaudrate(struct console_device *cdev, int baudrate)
+{
+ struct ar933x_uart_priv *priv = cdev->dev->priv;
+ unsigned int scale, step;
+
+ ar933x_uart_get_scale_step(clk_get_rate(priv->clk), baudrate, &scale,
+ &step);
+ ar933x_serial_writel(cdev, (scale << AR933X_UART_CLOCK_SCALE_S) | step,
+ AR933X_UART_CLOCK_REG);
+
+ return 0;
+}
+
+static void ar933x_serial_putc(struct console_device *cdev, char ch)
+{
+ u32 data;
+
+ /* wait transmitter ready */
+ data = ar933x_serial_readl(cdev, AR933X_UART_DATA_REG);
+ while (!(data & AR933X_UART_DATA_TX_CSR))
+ data = ar933x_serial_readl(cdev, AR933X_UART_DATA_REG);
+
+ data = (ch & AR933X_UART_DATA_TX_RX_MASK) | AR933X_UART_DATA_TX_CSR;
+ ar933x_serial_writel(cdev, data, AR933X_UART_DATA_REG);
+}
+
+static int ar933x_serial_tstc(struct console_device *cdev)
+{
+ u32 rdata;
+
+ rdata = ar933x_serial_readl(cdev, AR933X_UART_DATA_REG);
+
+ return rdata & AR933X_UART_DATA_RX_CSR;
+}
+
+static int ar933x_serial_getc(struct console_device *cdev)
+{
+ u32 rdata;
+
+ while (!ar933x_serial_tstc(cdev))
+ ;
+
+ rdata = ar933x_serial_readl(cdev, AR933X_UART_DATA_REG);
+
+ /* remove the character from the FIFO */
+ ar933x_serial_writel(cdev, AR933X_UART_DATA_RX_CSR,
+ AR933X_UART_DATA_REG);
+
+ return rdata & AR933X_UART_DATA_TX_RX_MASK;
+}
+
+static int ar933x_serial_probe(struct device_d *dev)
+{
+ struct console_device *cdev;
+ struct ar933x_uart_priv *priv;
+ u32 uart_cs;
+
+ cdev = xzalloc(sizeof(struct console_device));
+ priv = xzalloc(sizeof(struct ar933x_uart_priv));
+ priv->base = dev_request_mem_region(dev, 0);
+ dev->priv = priv;
+
+ cdev->dev = dev;
+ cdev->tstc = ar933x_serial_tstc;
+ cdev->putc = ar933x_serial_putc;
+ cdev->getc = ar933x_serial_getc;
+ cdev->setbrg = ar933x_serial_setbaudrate;
+
+ priv->clk = clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "unable to get UART clock\n");
+ return PTR_ERR(priv->clk);
+ }
+
+ uart_cs = (AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S)
+ | AR933X_UART_CS_TX_READY_ORIDE
+ | AR933X_UART_CS_RX_READY_ORIDE;
+ ar933x_serial_writel(cdev, uart_cs, AR933X_UART_CS_REG);
+ /* FIXME: need ar933x_serial_init_port(cdev); */
+
+ console_register(cdev);
+
+ return 0;
+}
+
+static struct of_device_id ar933x_serial_dt_ids[] = {
+ {
+ .compatible = "qca,ar9330-uart",
+ }, {
+ /* sentinel */
+ },
+};
+
+static struct driver_d ar933x_serial_driver = {
+ .name = "ar933x_serial",
+ .probe = ar933x_serial_probe,
+ .of_compatible = DRV_OF_COMPAT(ar933x_serial_dt_ids),
+};
+console_platform_driver(ar933x_serial_driver);
diff --git a/drivers/serial/serial_ar933x.h b/drivers/serial/serial_ar933x.h
new file mode 100644
index 0000000000..f55f0fa3d7
--- /dev/null
+++ b/drivers/serial/serial_ar933x.h
@@ -0,0 +1,69 @@
+/*
+ * Atheros AR933X UART defines
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef __AR933X_UART_H
+#define __AR933X_UART_H
+
+#include <linux/bitops.h>
+
+#define AR933X_UART_REGS_SIZE 20
+#define AR933X_UART_FIFO_SIZE 16
+
+#define AR933X_UART_DATA_REG 0x00
+#define AR933X_UART_CS_REG 0x04
+#define AR933X_UART_CLOCK_REG 0x08
+#define AR933X_UART_INT_REG 0x0c
+#define AR933X_UART_INT_EN_REG 0x10
+
+#define AR933X_UART_DATA_TX_RX_MASK 0xff
+#define AR933X_UART_DATA_RX_CSR BIT(8)
+#define AR933X_UART_DATA_TX_CSR BIT(9)
+
+#define AR933X_UART_CS_PARITY_S 0
+#define AR933X_UART_CS_PARITY_M 0x3
+#define AR933X_UART_CS_PARITY_NONE 0
+#define AR933X_UART_CS_PARITY_ODD 1
+#define AR933X_UART_CS_PARITY_EVEN 2
+#define AR933X_UART_CS_IF_MODE_S 2
+#define AR933X_UART_CS_IF_MODE_M 0x3
+#define AR933X_UART_CS_IF_MODE_NONE 0
+#define AR933X_UART_CS_IF_MODE_DTE 1
+#define AR933X_UART_CS_IF_MODE_DCE 2
+#define AR933X_UART_CS_FLOW_CTRL_S 4
+#define AR933X_UART_CS_FLOW_CTRL_M 0x3
+#define AR933X_UART_CS_DMA_EN BIT(6)
+#define AR933X_UART_CS_TX_READY_ORIDE BIT(7)
+#define AR933X_UART_CS_RX_READY_ORIDE BIT(8)
+#define AR933X_UART_CS_TX_READY BIT(9)
+#define AR933X_UART_CS_RX_BREAK BIT(10)
+#define AR933X_UART_CS_TX_BREAK BIT(11)
+#define AR933X_UART_CS_HOST_INT BIT(12)
+#define AR933X_UART_CS_HOST_INT_EN BIT(13)
+#define AR933X_UART_CS_TX_BUSY BIT(14)
+#define AR933X_UART_CS_RX_BUSY BIT(15)
+
+#define AR933X_UART_CLOCK_STEP_M 0xffff
+#define AR933X_UART_CLOCK_SCALE_M 0xfff
+#define AR933X_UART_CLOCK_SCALE_S 16
+#define AR933X_UART_CLOCK_STEP_M 0xffff
+
+#define AR933X_UART_INT_RX_VALID BIT(0)
+#define AR933X_UART_INT_TX_READY BIT(1)
+#define AR933X_UART_INT_RX_FRAMING_ERR BIT(2)
+#define AR933X_UART_INT_RX_OFLOW_ERR BIT(3)
+#define AR933X_UART_INT_TX_OFLOW_ERR BIT(4)
+#define AR933X_UART_INT_RX_PARITY_ERR BIT(5)
+#define AR933X_UART_INT_RX_BREAK_ON BIT(6)
+#define AR933X_UART_INT_RX_BREAK_OFF BIT(7)
+#define AR933X_UART_INT_RX_FULL BIT(8)
+#define AR933X_UART_INT_TX_EMPTY BIT(9)
+#define AR933X_UART_INT_ALLINTS 0x3ff
+
+#endif /* __AR933X_UART_H */
diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c
index f1da44b7f7..0c00eb182c 100644
--- a/drivers/serial/serial_ns16550.c
+++ b/drivers/serial/serial_ns16550.c
@@ -48,6 +48,7 @@ struct ns16550_priv {
struct NS16550_plat plat;
int access_width;
struct clk *clk;
+ uint32_t fcrval;
};
static inline struct ns16550_priv *to_ns16550_priv(struct console_device *cdev)
@@ -157,18 +158,13 @@ static int ns16550_setbaudrate(struct console_device *cdev, int baud_rate)
{
unsigned int baud_divisor = ns16550_calc_divisor(cdev, baud_rate);
struct ns16550_priv *priv = to_ns16550_priv(cdev);
- struct NS16550_plat *plat = &priv->plat;
ns16550_write(cdev, LCR_BKSE, lcr);
ns16550_write(cdev, baud_divisor & 0xff, dll);
ns16550_write(cdev, (baud_divisor >> 8) & 0xff, dlm);
ns16550_write(cdev, LCRVAL, lcr);
ns16550_write(cdev, MCRVAL, mcr);
-
- if (plat->flags & NS16650_FLAG_DISABLE_FIFO)
- ns16550_write(cdev, FCRVAL & ~FCR_FIFO_EN, fcr);
- else
- ns16550_write(cdev, FCRVAL, fcr);
+ ns16550_write(cdev, priv->fcrval, fcr);
return 0;
}
@@ -185,6 +181,15 @@ static void ns16550_serial_init_port(struct console_device *cdev)
ns16550_write(cdev, 0x00, ier);
}
+static void ns16450_serial_init_port(struct console_device *cdev)
+{
+ struct ns16550_priv *priv = to_ns16550_priv(cdev);
+
+ priv->fcrval &= ~FCR_FIFO_EN;
+
+ ns16550_serial_init_port(cdev);
+}
+
#define omap_mdr1 8
static void ns16550_omap_init_port(struct console_device *cdev)
@@ -192,7 +197,17 @@ static void ns16550_omap_init_port(struct console_device *cdev)
ns16550_serial_init_port(cdev);
ns16550_write(cdev, 0x07, omap_mdr1); /* Disable */
- ns16550_write(cdev, 0x00, omap_mdr1);
+ ns16550_write(cdev, 0x00, omap_mdr1);
+}
+
+#define JZ_FCR_UME 0x10 /* Uart Module Enable */
+
+static void ns16550_jz_init_port(struct console_device *cdev)
+{
+ struct ns16550_priv *priv = to_ns16550_priv(cdev);
+
+ priv->fcrval |= JZ_FCR_UME;
+ ns16550_serial_init_port(cdev);
}
/*********** Exposed Functions **********************************/
@@ -246,6 +261,10 @@ static void ns16550_probe_dt(struct device_d *dev, struct ns16550_priv *priv)
of_property_read_u32(np, "reg-shift", &priv->plat.shift);
}
+static struct ns16550_drvdata ns16450_drvdata = {
+ .init_port = ns16450_serial_init_port,
+};
+
static struct ns16550_drvdata ns16550_drvdata = {
.init_port = ns16550_serial_init_port,
};
@@ -255,6 +274,10 @@ static __maybe_unused struct ns16550_drvdata omap_drvdata = {
.linux_console_name = "ttyO",
};
+static __maybe_unused struct ns16550_drvdata jz_drvdata = {
+ .init_port = ns16550_jz_init_port,
+};
+
/**
* @brief Probe entry point -called on the first match for device
*
@@ -316,6 +339,11 @@ static int ns16550_probe(struct device_d *dev)
cdev->setbrg = ns16550_setbaudrate;
cdev->linux_console_name = devtype->linux_console_name;
+ if (plat && (plat->flags & NS16650_FLAG_DISABLE_FIFO))
+ priv->fcrval = FCRVAL & ~FCR_FIFO_EN;
+ else
+ priv->fcrval = FCRVAL;
+
devtype->init_port(cdev);
return console_register(cdev);
@@ -328,6 +356,9 @@ err:
static struct of_device_id ns16550_serial_dt_ids[] = {
{
+ .compatible = "ns16450",
+ .data = (unsigned long)&ns16450_drvdata,
+ }, {
.compatible = "ns16550a",
.data = (unsigned long)&ns16550_drvdata,
}, {
@@ -346,6 +377,12 @@ static struct of_device_id ns16550_serial_dt_ids[] = {
.data = (unsigned long)&omap_drvdata,
},
#endif
+#if IS_ENABLED(CONFIG_MACH_MIPS_XBURST)
+ {
+ .compatible = "ingenic,jz4740-uart",
+ .data = (unsigned long)&jz_drvdata,
+ },
+#endif
{
/* sentinel */
},
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 11b6c186e3..71d0ecf39c 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -994,6 +994,8 @@ static struct usb_gadget_driver composite_driver = {
*/
int usb_composite_register(struct usb_composite_driver *driver)
{
+ int ret;
+
if (!driver || !driver->dev || !driver->bind || composite)
return -EINVAL;
@@ -1002,7 +1004,12 @@ int usb_composite_register(struct usb_composite_driver *driver)
composite_driver.function = (char *) driver->name;
composite = driver;
- return usb_gadget_register_driver(&composite_driver);
+ ret = usb_gadget_register_driver(&composite_driver);
+
+ if (ret)
+ composite = NULL;
+
+ return ret;
}
/**
diff --git a/drivers/usb/gadget/dfu.c b/drivers/usb/gadget/dfu.c
index 76b5def33c..d0f2155e49 100644
--- a/drivers/usb/gadget/dfu.c
+++ b/drivers/usb/gadget/dfu.c
@@ -703,7 +703,9 @@ int usb_dfu_register(struct usb_dfu_pdata *pdata)
strings_dev[STRING_MANUFACTURER_IDX].s = pdata->manufacturer;
strings_dev[STRING_PRODUCT_IDX].s = pdata->productname;
- usb_composite_register(&dfu_driver);
+ ret = usb_composite_register(&dfu_driver);
+ if (ret)
+ return ret;
while (1) {
ret = usb_gadget_poll();
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 5539266226..34177b3f08 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -77,6 +77,8 @@ config DRIVER_VIDEO_BCM2835
help
Add support for the BCM2835/VideoCore frame buffer device.
+source drivers/video/imx-ipu-v3/Kconfig
+
config DRIVER_VIDEO_SIMPLEFB
bool "Simple framebuffer support"
depends on OFTREE
@@ -84,4 +86,10 @@ config DRIVER_VIDEO_SIMPLEFB
Add support for setting up the kernel's simple framebuffer driver
based on the active barebox framebuffer.
+config DRIVER_VIDEO_EDID
+ bool "Add EDID support"
+ help
+ This enabled support for reading and parsing EDID data from an attached
+ monitor.
+
endif
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 31edfca2b0..ae9f6e545e 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -1,4 +1,6 @@
obj-$(CONFIG_VIDEO) += fb.o
+obj-$(CONFIG_DRIVER_VIDEO_EDID) += edid.o
+obj-$(CONFIG_OFDEVICE) += of_display_timing.o
obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o
obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o
@@ -11,3 +13,4 @@ obj-$(CONFIG_DRIVER_VIDEO_SDL) += sdl.o
obj-$(CONFIG_DRIVER_VIDEO_OMAP) += omap.o
obj-$(CONFIG_DRIVER_VIDEO_BCM2835) += bcm2835.o
obj-$(CONFIG_DRIVER_VIDEO_SIMPLEFB) += simplefb.o
+obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3) += imx-ipu-v3/
diff --git a/drivers/video/atmel_lcdfb_core.c b/drivers/video/atmel_lcdfb_core.c
index bed540da9f..d03922b2b6 100644
--- a/drivers/video/atmel_lcdfb_core.c
+++ b/drivers/video/atmel_lcdfb_core.c
@@ -269,9 +269,9 @@ int atmel_lcdc_register(struct device_d *dev, struct atmel_lcdfb_devdata *data)
info = &sinfo->info;
info->priv = sinfo;
info->fbops = &atmel_lcdc_ops;
- info->mode_list = pdata->mode_list;
- info->num_modes = pdata->num_modes;
- info->mode = &info->mode_list[0];
+ info->modes.modes = pdata->mode_list;
+ info->modes.num_modes = pdata->num_modes;
+ info->mode = &info->modes.modes[0];
info->xres = info->mode->xres;
info->yres = info->mode->yres;
info->bits_per_pixel = pdata->default_bpp;
diff --git a/drivers/video/edid.c b/drivers/video/edid.c
new file mode 100644
index 0000000000..828c3610ac
--- /dev/null
+++ b/drivers/video/edid.c
@@ -0,0 +1,909 @@
+/*
+ * drivers/video/edid.c
+ *
+ * Copyright (C) 2002 James Simmons <jsimmons@users.sf.net>
+ *
+ * Credits:
+ *
+ * The EDID Parser is a conglomeration from the following sources:
+ *
+ * 1. SciTech SNAP Graphics Architecture
+ * Copyright (C) 1991-2002 SciTech Software, Inc. All rights reserved.
+ *
+ * 2. XFree86 4.3.0, interpret_edid.c
+ * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE>
+ *
+ * 3. John Fremlin <vii@users.sourceforge.net> and
+ * Ani Joshi <ajoshi@unixbox.com>
+ *
+ * Generalized Timing Formula is derived from:
+ *
+ * GTF Spreadsheet by Andy Morrish (1/5/97)
+ * available at http://www.vesa.org
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "EDID: " fmt
+
+#include <common.h>
+#include <fb.h>
+#include <malloc.h>
+#include <i2c/i2c.h>
+
+#include "edid.h"
+
+#define FBMON_FIX_HEADER 1
+#define FBMON_FIX_INPUT 2
+#define FBMON_FIX_TIMINGS 3
+
+struct broken_edid {
+ u8 manufacturer[4];
+ u32 model;
+ u32 fix;
+};
+
+static const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x00
+};
+
+static int edid_is_serial_block(unsigned char *block)
+{
+ if ((block[0] == 0x00) && (block[1] == 0x00) &&
+ (block[2] == 0x00) && (block[3] == 0xff) &&
+ (block[4] == 0x00))
+ return 1;
+ else
+ return 0;
+}
+
+static int edid_is_ascii_block(unsigned char *block)
+{
+ if ((block[0] == 0x00) && (block[1] == 0x00) &&
+ (block[2] == 0x00) && (block[3] == 0xfe) &&
+ (block[4] == 0x00))
+ return 1;
+ else
+ return 0;
+}
+
+static int edid_is_limits_block(unsigned char *block)
+{
+ if ((block[0] == 0x00) && (block[1] == 0x00) &&
+ (block[2] == 0x00) && (block[3] == 0xfd) &&
+ (block[4] == 0x00))
+ return 1;
+ else
+ return 0;
+}
+
+static int edid_is_monitor_block(unsigned char *block)
+{
+ if ((block[0] == 0x00) && (block[1] == 0x00) &&
+ (block[2] == 0x00) && (block[3] == 0xfc) &&
+ (block[4] == 0x00))
+ return 1;
+ else
+ return 0;
+}
+
+static int edid_is_timing_block(unsigned char *block)
+{
+ if ((block[0] != 0x00) || (block[1] != 0x00) ||
+ (block[2] != 0x00) || (block[4] != 0x00))
+ return 1;
+ else
+ return 0;
+}
+
+static int check_edid(unsigned char *edid)
+{
+ unsigned char *block = edid + ID_MANUFACTURER_NAME, manufacturer[4];
+ unsigned char *b;
+ u32 model;
+ int i, fix = 0, ret = 0;
+
+ manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@';
+ manufacturer[1] = ((block[0] & 0x03) << 3) +
+ ((block[1] & 0xe0) >> 5) + '@';
+ manufacturer[2] = (block[1] & 0x1f) + '@';
+ manufacturer[3] = 0;
+ model = block[2] + (block[3] << 8);
+
+ switch (fix) {
+ case FBMON_FIX_HEADER:
+ for (i = 0; i < 8; i++) {
+ if (edid[i] != edid_v1_header[i]) {
+ ret = fix;
+ break;
+ }
+ }
+ break;
+ case FBMON_FIX_INPUT:
+ b = edid + EDID_STRUCT_DISPLAY;
+ /* Only if display is GTF capable will
+ the input type be reset to analog */
+ if (b[4] & 0x01 && b[0] & 0x80)
+ ret = fix;
+ break;
+ case FBMON_FIX_TIMINGS:
+ b = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+ ret = fix;
+
+ for (i = 0; i < 4; i++) {
+ if (edid_is_limits_block(b)) {
+ ret = 0;
+ break;
+ }
+
+ b += DETAILED_TIMING_DESCRIPTION_SIZE;
+ }
+
+ break;
+ }
+
+ if (ret)
+ printk("fbmon: The EDID Block of "
+ "Manufacturer: %s Model: 0x%x is known to "
+ "be broken,\n", manufacturer, model);
+
+ return ret;
+}
+
+static void fix_edid(unsigned char *edid, int fix)
+{
+ int i;
+ unsigned char *b, csum = 0;
+
+ switch (fix) {
+ case FBMON_FIX_HEADER:
+ printk("fbmon: trying a header reconstruct\n");
+ memcpy(edid, edid_v1_header, 8);
+ break;
+ case FBMON_FIX_INPUT:
+ printk("fbmon: trying to fix input type\n");
+ b = edid + EDID_STRUCT_DISPLAY;
+ b[0] &= ~0x80;
+ edid[127] += 0x80;
+ break;
+ case FBMON_FIX_TIMINGS:
+ printk("fbmon: trying to fix monitor timings\n");
+ b = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+ for (i = 0; i < 4; i++) {
+ if (!(edid_is_serial_block(b) ||
+ edid_is_ascii_block(b) ||
+ edid_is_monitor_block(b) ||
+ edid_is_timing_block(b))) {
+ b[0] = 0x00;
+ b[1] = 0x00;
+ b[2] = 0x00;
+ b[3] = 0xfd;
+ b[4] = 0x00;
+ b[5] = 60; /* vfmin */
+ b[6] = 60; /* vfmax */
+ b[7] = 30; /* hfmin */
+ b[8] = 75; /* hfmax */
+ b[9] = 17; /* pixclock - 170 MHz*/
+ b[10] = 0; /* GTF */
+ break;
+ }
+
+ b += DETAILED_TIMING_DESCRIPTION_SIZE;
+ }
+
+ for (i = 0; i < EDID_LENGTH - 1; i++)
+ csum += edid[i];
+
+ edid[127] = 256 - csum;
+ break;
+ }
+}
+
+static int edid_checksum(unsigned char *edid)
+{
+ unsigned char csum = 0, all_null = 0;
+ int i, err = 0, fix = check_edid(edid);
+
+ if (fix)
+ fix_edid(edid, fix);
+
+ for (i = 0; i < EDID_LENGTH; i++) {
+ csum += edid[i];
+ all_null |= edid[i];
+ }
+
+ if (csum == 0x00 && all_null) {
+ /* checksum passed, everything's good */
+ err = 1;
+ }
+
+ return err;
+}
+
+static int edid_check_header(unsigned char *edid)
+{
+ int i, err = 1, fix = check_edid(edid);
+
+ if (fix)
+ fix_edid(edid, fix);
+
+ for (i = 0; i < 8; i++) {
+ if (edid[i] != edid_v1_header[i])
+ err = 0;
+ }
+
+ return err;
+}
+
+/*
+ * VESA Generalized Timing Formula (GTF)
+ */
+
+#define FLYBACK 550
+#define V_FRONTPORCH 1
+#define H_OFFSET 40
+#define H_SCALEFACTOR 20
+#define H_BLANKSCALE 128
+#define H_GRADIENT 600
+#define C_VAL 30
+#define M_VAL 300
+
+struct __fb_timings {
+ u32 dclk;
+ u32 hfreq;
+ u32 vfreq;
+ u32 hactive;
+ u32 vactive;
+ u32 hblank;
+ u32 vblank;
+ u32 htotal;
+ u32 vtotal;
+};
+
+/**
+ * fb_get_vblank - get vertical blank time
+ * @hfreq: horizontal freq
+ *
+ * DESCRIPTION:
+ * vblank = right_margin + vsync_len + left_margin
+ *
+ * given: right_margin = 1 (V_FRONTPORCH)
+ * vsync_len = 3
+ * flyback = 550
+ *
+ * flyback * hfreq
+ * left_margin = --------------- - vsync_len
+ * 1000000
+ */
+static u32 fb_get_vblank(u32 hfreq)
+{
+ u32 vblank;
+
+ vblank = (hfreq * FLYBACK)/1000;
+ vblank = (vblank + 500)/1000;
+ return (vblank + V_FRONTPORCH);
+}
+
+/**
+ * fb_get_hblank_by_freq - get horizontal blank time given hfreq
+ * @hfreq: horizontal freq
+ * @xres: horizontal resolution in pixels
+ *
+ * DESCRIPTION:
+ *
+ * xres * duty_cycle
+ * hblank = ------------------
+ * 100 - duty_cycle
+ *
+ * duty cycle = percent of htotal assigned to inactive display
+ * duty cycle = C - (M/Hfreq)
+ *
+ * where: C = ((offset - scale factor) * blank_scale)
+ * -------------------------------------- + scale factor
+ * 256
+ * M = blank_scale * gradient
+ *
+ */
+static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres)
+{
+ u32 c_val, m_val, duty_cycle, hblank;
+
+ c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 +
+ H_SCALEFACTOR) * 1000;
+ m_val = (H_BLANKSCALE * H_GRADIENT)/256;
+ m_val = (m_val * 1000000)/hfreq;
+ duty_cycle = c_val - m_val;
+ hblank = (xres * duty_cycle)/(100000 - duty_cycle);
+ return (hblank);
+}
+
+/**
+ * int_sqrt - rough approximation to sqrt
+ * @x: integer of which to calculate the sqrt
+ *
+ * A very rough approximation to the sqrt() function.
+ */
+unsigned long int_sqrt(unsigned long x)
+{
+ unsigned long b, m, y = 0;
+
+ if (x <= 1)
+ return x;
+
+ m = 1UL << (BITS_PER_LONG - 2);
+ while (m != 0) {
+ b = y + m;
+ y >>= 1;
+
+ if (x >= b) {
+ x -= b;
+ y += m;
+ }
+ m >>= 2;
+ }
+
+ return y;
+}
+EXPORT_SYMBOL(int_sqrt);
+
+/**
+ * fb_get_hfreq - estimate hsync
+ * @vfreq: vertical refresh rate
+ * @yres: vertical resolution
+ *
+ * DESCRIPTION:
+ *
+ * (yres + front_port) * vfreq * 1000000
+ * hfreq = -------------------------------------
+ * (1000000 - (vfreq * FLYBACK)
+ *
+ */
+
+static u32 fb_get_hfreq(u32 vfreq, u32 yres)
+{
+ u32 divisor, hfreq;
+
+ divisor = (1000000 - (vfreq * FLYBACK))/1000;
+ hfreq = (yres + V_FRONTPORCH) * vfreq * 1000;
+ return (hfreq/divisor);
+}
+
+static void fb_timings_vfreq(struct __fb_timings *timings)
+{
+ timings->hfreq = fb_get_hfreq(timings->vfreq, timings->vactive);
+ timings->vblank = fb_get_vblank(timings->hfreq);
+ timings->vtotal = timings->vactive + timings->vblank;
+ timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq,
+ timings->hactive);
+ timings->htotal = timings->hactive + timings->hblank;
+ timings->dclk = timings->htotal * timings->hfreq;
+}
+
+/*
+ * fb_get_mode - calculates video mode using VESA GTF
+ * @flags: if: 0 - maximize vertical refresh rate
+ * 1 - vrefresh-driven calculation;
+ * 2 - hscan-driven calculation;
+ * 3 - pixelclock-driven calculation;
+ * @val: depending on @flags, ignored, vrefresh, hsync or pixelclock
+ * @var: pointer to fb_var_screeninfo
+ * @info: pointer to fb_info
+ *
+ * DESCRIPTION:
+ * Calculates video mode based on monitor specs using VESA GTF.
+ * The GTF is best for VESA GTF compliant monitors but is
+ * specifically formulated to work for older monitors as well.
+ *
+ * If @flag==0, the function will attempt to maximize the
+ * refresh rate. Otherwise, it will calculate timings based on
+ * the flag and accompanying value.
+ *
+ * If FB_IGNOREMON bit is set in @flags, monitor specs will be
+ * ignored and @var will be filled with the calculated timings.
+ *
+ * All calculations are based on the VESA GTF Spreadsheet
+ * available at VESA's public ftp (http://www.vesa.org).
+ *
+ * NOTES:
+ * The timings generated by the GTF will be different from VESA
+ * DMT. It might be a good idea to keep a table of standard
+ * VESA modes as well. The GTF may also not work for some displays,
+ * such as, and especially, analog TV.
+ *
+ * REQUIRES:
+ * A valid info->monspecs, otherwise 'safe numbers' will be used.
+ */
+int fb_get_mode(int flags, u32 val, struct fb_videomode *var)
+{
+ struct __fb_timings *timings;
+ u32 interlace = 1, dscan = 1;
+ u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax, err = 0;
+
+ timings = xzalloc(sizeof(struct __fb_timings));
+
+ /*
+ * If monspecs are invalid, use values that are enough
+ * for 640x480@60
+ */
+ hfmin = 29000; hfmax = 30000;
+ vfmin = 60; vfmax = 60;
+ dclkmin = 0; dclkmax = 25000000;
+
+ timings->hactive = var->xres;
+ timings->vactive = var->yres;
+ if (var->vmode & FB_VMODE_INTERLACED) {
+ timings->vactive /= 2;
+ interlace = 2;
+ }
+ if (var->vmode & FB_VMODE_DOUBLE) {
+ timings->vactive *= 2;
+ dscan = 2;
+ }
+
+ /* vrefresh driven */
+ timings->vfreq = val;
+ fb_timings_vfreq(timings);
+
+ if (timings->dclk)
+ var->pixclock = KHZ2PICOS(timings->dclk / 1000);
+ var->hsync_len = (timings->htotal * 8) / 100;
+ var->right_margin = (timings->hblank / 2) - var->hsync_len;
+ var->left_margin = timings->hblank - var->right_margin -
+ var->hsync_len;
+ var->vsync_len = (3 * interlace) / dscan;
+ var->lower_margin = (1 * interlace) / dscan;
+ var->upper_margin = (timings->vblank * interlace) / dscan -
+ (var->vsync_len + var->lower_margin);
+
+ free(timings);
+ return err;
+}
+
+static void calc_mode_timings(int xres, int yres, int refresh,
+ struct fb_videomode *mode)
+{
+ mode->xres = xres;
+ mode->yres = yres;
+ mode->refresh = refresh;
+ fb_get_mode(0, refresh, mode);
+ mode->name = asprintf("%dx%d@%d-calc", mode->xres, mode->yres, mode->refresh);
+ pr_debug(" %s\n", mode->name);
+}
+
+const struct fb_videomode vesa_modes[] = {
+ /* 0 640x350-85 VESA */
+ { NULL, 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3,
+ FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, 0},
+ /* 1 640x400-85 VESA */
+ { NULL, 85, 640, 400, 31746, 96, 32, 41, 01, 64, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
+ /* 2 720x400-85 VESA */
+ { NULL, 85, 721, 400, 28169, 108, 36, 42, 01, 72, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
+ /* 3 640x480-60 VESA */
+ { NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2,
+ 0, FB_VMODE_NONINTERLACED, 0 },
+ /* 4 640x480-72 VESA */
+ { NULL, 72, 640, 480, 31746, 128, 24, 29, 9, 40, 2,
+ 0, FB_VMODE_NONINTERLACED, 0 },
+ /* 5 640x480-75 VESA */
+ { NULL, 75, 640, 480, 31746, 120, 16, 16, 01, 64, 3,
+ 0, FB_VMODE_NONINTERLACED, 0 },
+ /* 6 640x480-85 VESA */
+ { NULL, 85, 640, 480, 27777, 80, 56, 25, 01, 56, 3,
+ 0, FB_VMODE_NONINTERLACED, 0 },
+ /* 7 800x600-56 VESA */
+ { NULL, 56, 800, 600, 27777, 128, 24, 22, 01, 72, 2,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 8 800x600-60 VESA */
+ { NULL, 60, 800, 600, 25000, 88, 40, 23, 01, 128, 4,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 9 800x600-72 VESA */
+ { NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 10 800x600-75 VESA */
+ { NULL, 75, 800, 600, 20202, 160, 16, 21, 01, 80, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 11 800x600-85 VESA */
+ { NULL, 85, 800, 600, 17761, 152, 32, 27, 01, 64, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 12 1024x768i-43 VESA */
+ { NULL, 43, 1024, 768, 22271, 56, 8, 41, 0, 176, 8,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_INTERLACED, 0 },
+ /* 13 1024x768-60 VESA */
+ { NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6,
+ 0, FB_VMODE_NONINTERLACED, 0 },
+ /* 14 1024x768-70 VESA */
+ { NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6,
+ 0, FB_VMODE_NONINTERLACED, 0 },
+ /* 15 1024x768-75 VESA */
+ { NULL, 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 16 1024x768-85 VESA */
+ { NULL, 85, 1024, 768, 10582, 208, 48, 36, 1, 96, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 17 1152x864-75 VESA */
+ { NULL, 75, 1152, 864, 9259, 256, 64, 32, 1, 128, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 18 1280x960-60 VESA */
+ { NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 19 1280x960-85 VESA */
+ { NULL, 85, 1280, 960, 6734, 224, 64, 47, 1, 160, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 20 1280x1024-60 VESA */
+ { NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 21 1280x1024-75 VESA */
+ { NULL, 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 22 1280x1024-85 VESA */
+ { NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 23 1600x1200-60 VESA */
+ { NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 24 1600x1200-65 VESA */
+ { NULL, 65, 1600, 1200, 5698, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 25 1600x1200-70 VESA */
+ { NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 26 1600x1200-75 VESA */
+ { NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 27 1600x1200-85 VESA */
+ { NULL, 85, 1600, 1200, 4357, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0 },
+ /* 28 1792x1344-60 VESA */
+ { NULL, 60, 1792, 1344, 4882, 328, 128, 46, 1, 200, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
+ /* 29 1792x1344-75 VESA */
+ { NULL, 75, 1792, 1344, 3831, 352, 96, 69, 1, 216, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
+ /* 30 1856x1392-60 VESA */
+ { NULL, 60, 1856, 1392, 4580, 352, 96, 43, 1, 224, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
+ /* 31 1856x1392-75 VESA */
+ { NULL, 75, 1856, 1392, 3472, 352, 128, 104, 1, 224, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
+ /* 32 1920x1440-60 VESA */
+ { NULL, 60, 1920, 1440, 4273, 344, 128, 56, 1, 200, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
+ /* 33 1920x1440-75 VESA */
+ { NULL, 75, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0 },
+};
+
+#define VESA_MODEDB_SIZE ARRAY_SIZE(vesa_modes)
+
+static void add_vesa_mode(struct fb_videomode *mode, int num)
+{
+ *mode = vesa_modes[num];
+ mode->name = asprintf("%dx%d@%d-vesa", mode->xres, mode->yres, mode->refresh);
+ pr_debug(" %s\n", mode->name);
+}
+
+static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
+{
+ int num = 0;
+ unsigned char c;
+
+ c = block[0];
+ if (c & 0x80)
+ calc_mode_timings(720, 400, 70, &mode[num++]);
+ if (c & 0x40)
+ calc_mode_timings(720, 400, 88, &mode[num++]);
+ if (c & 0x20)
+ add_vesa_mode(&mode[num++], 3);
+ if (c & 0x10)
+ calc_mode_timings(640, 480, 67, &mode[num++]);
+ if (c & 0x08)
+ add_vesa_mode(&mode[num++], 4);
+ if (c & 0x04)
+ add_vesa_mode(&mode[num++], 5);
+ if (c & 0x02)
+ add_vesa_mode(&mode[num++], 7);
+ if (c & 0x01)
+ add_vesa_mode(&mode[num++], 8);
+
+ c = block[1];
+ if (c & 0x80)
+ add_vesa_mode(&mode[num++], 9);
+ if (c & 0x40)
+ add_vesa_mode(&mode[num++], 10);
+ if (c & 0x20)
+ calc_mode_timings(832, 624, 75, &mode[num++]);
+ if (c & 0x10)
+ add_vesa_mode(&mode[num++], 12);
+ if (c & 0x08)
+ add_vesa_mode(&mode[num++], 13);
+ if (c & 0x04)
+ add_vesa_mode(&mode[num++], 14);
+ if (c & 0x02)
+ add_vesa_mode(&mode[num++], 15);
+ if (c & 0x01)
+ add_vesa_mode(&mode[num++], 21);
+ c = block[2];
+
+ if (c & 0x80)
+ add_vesa_mode(&mode[num++], 17);
+
+ pr_debug(" Manufacturer's mask: %x\n",c & 0x7F);
+ return num;
+}
+
+static int get_std_timing(unsigned char *block, struct fb_videomode *mode,
+ int ver, int rev)
+{
+ int xres, yres = 0, refresh, ratio, i;
+
+ xres = (block[0] + 31) * 8;
+ if (xres <= 256)
+ return 0;
+
+ ratio = (block[1] & 0xc0) >> 6;
+ switch (ratio) {
+ case 0:
+ /* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */
+ if (ver < 1 || (ver == 1 && rev < 3))
+ yres = xres;
+ else
+ yres = (xres * 10) / 16;
+ break;
+ case 1:
+ yres = (xres * 3) / 4;
+ break;
+ case 2:
+ yres = (xres * 4) / 5;
+ break;
+ case 3:
+ yres = (xres * 9) / 16;
+ break;
+ }
+ refresh = (block[1] & 0x3f) + 60;
+
+ for (i = 0; i < VESA_MODEDB_SIZE; i++) {
+ if (vesa_modes[i].xres == xres &&
+ vesa_modes[i].yres == yres &&
+ vesa_modes[i].refresh == refresh) {
+ add_vesa_mode(mode, i);
+ return 1;
+ }
+ }
+
+ calc_mode_timings(xres, yres, refresh, mode);
+
+ return 1;
+}
+
+static int get_dst_timing(unsigned char *block,
+ struct fb_videomode *mode, int ver, int rev)
+{
+ int j, num = 0;
+
+ for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE)
+ num += get_std_timing(block, &mode[num], ver, rev);
+
+ return num;
+}
+
+static void get_detailed_timing(unsigned char *block,
+ struct fb_videomode *mode)
+{
+ mode->xres = H_ACTIVE;
+ mode->yres = V_ACTIVE;
+ mode->pixclock = PIXEL_CLOCK;
+ mode->pixclock /= 1000;
+ mode->pixclock = KHZ2PICOS(mode->pixclock);
+ mode->right_margin = H_SYNC_OFFSET;
+ mode->left_margin = (H_ACTIVE + H_BLANKING) -
+ (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
+ mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
+ V_SYNC_WIDTH;
+ mode->lower_margin = V_SYNC_OFFSET;
+ mode->hsync_len = H_SYNC_WIDTH;
+ mode->vsync_len = V_SYNC_WIDTH;
+ if (HSYNC_POSITIVE)
+ mode->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (VSYNC_POSITIVE)
+ mode->sync |= FB_SYNC_VERT_HIGH_ACT;
+ mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
+ (V_ACTIVE + V_BLANKING));
+ if (INTERLACED) {
+ mode->yres *= 2;
+ mode->upper_margin *= 2;
+ mode->lower_margin *= 2;
+ mode->vsync_len *= 2;
+ mode->vmode |= FB_VMODE_INTERLACED;
+ }
+
+ pr_debug(" %d MHz ", PIXEL_CLOCK/1000000);
+ pr_debug("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
+ H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
+ pr_debug("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
+ V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
+ pr_debug("%sHSync %sVSync\n", (HSYNC_POSITIVE) ? "+" : "-",
+ (VSYNC_POSITIVE) ? "+" : "-");
+
+ mode->name = asprintf("%dx%d@%d", mode->xres, mode->yres, mode->refresh);
+}
+
+/**
+ * edid_to_display_timings - create video mode database
+ * @edid: EDID data
+ * @dbsize: database size
+ *
+ * RETURNS: struct fb_videomode, @dbsize contains length of database
+ *
+ * DESCRIPTION:
+ * This function builds a mode database using the contents of the EDID
+ * data
+ */
+int edid_to_display_timings(struct display_timings *timings, unsigned char *edid)
+{
+ struct fb_videomode *mode;
+ unsigned char *block;
+ int num = 0, i, first = 1;
+ int ver, rev, ret;
+
+ ver = edid[EDID_STRUCT_VERSION];
+ rev = edid[EDID_STRUCT_REVISION];
+
+ mode = xzalloc(50 * sizeof(struct fb_videomode));
+
+ if (!edid_checksum(edid) ||
+ !edid_check_header(edid)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ pr_debug(" Detailed Timings\n");
+ block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+ for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
+ if (!(block[0] == 0x00 && block[1] == 0x00)) {
+ get_detailed_timing(block, &mode[num]);
+ if (first) {
+ first = 0;
+ }
+ num++;
+ }
+ }
+
+ pr_debug(" Supported VESA Modes\n");
+ block = edid + ESTABLISHED_TIMING_1;
+ num += get_est_timing(block, &mode[num]);
+
+ pr_debug(" Standard Timings\n");
+ block = edid + STD_TIMING_DESCRIPTIONS_START;
+ for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE)
+ num += get_std_timing(block, &mode[num], ver, rev);
+
+ block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+ for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
+ if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa)
+ num += get_dst_timing(block + 5, &mode[num], ver, rev);
+ }
+
+ /* Yikes, EDID data is totally useless */
+ if (!num) {
+ free(mode);
+ return -EINVAL;
+ }
+
+ timings->num_modes = num;
+ timings->modes = mode;
+
+ return 0;
+out:
+ free(timings);
+ free(mode);
+ return ret;
+}
+
+#define DDC_ADDR 0x50
+#define DDC_SEGMENT_ADDR 0x30
+
+/**
+ * Get EDID information via I2C.
+ *
+ * \param adapter : i2c device adaptor
+ * \param buf : EDID data buffer to be filled
+ * \param len : EDID data buffer length
+ * \return 0 on success or -1 on failure.
+ *
+ * Try to fetch EDID information by calling i2c driver function.
+ */
+static int
+edid_do_read_i2c(struct i2c_adapter *adapter, unsigned char *buf,
+ int block, int len)
+{
+ unsigned char start = block * EDID_LENGTH;
+ unsigned char segment = block >> 1;
+ unsigned char xfers = segment ? 3 : 2;
+ int ret, retries = 5;
+
+ /* The core i2c driver will automatically retry the transfer if the
+ * adapter reports EAGAIN. However, we find that bit-banging transfers
+ * are susceptible to errors under a heavily loaded machine and
+ * generate spurious NAKs and timeouts. Retrying the transfer
+ * of the individual block a few times seems to overcome this.
+ */
+ do {
+ struct i2c_msg msgs[] = {
+ {
+ .addr = DDC_SEGMENT_ADDR,
+ .flags = 0,
+ .len = 1,
+ .buf = &segment,
+ }, {
+ .addr = DDC_ADDR,
+ .flags = 0,
+ .len = 1,
+ .buf = &start,
+ }, {
+ .addr = DDC_ADDR,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ }
+ };
+
+ /*
+ * Avoid sending the segment addr to not upset non-compliant ddc
+ * monitors.
+ */
+ ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);
+ } while (ret != xfers && --retries);
+
+ return ret == xfers ? 0 : -1;
+}
+
+void *edid_read_i2c(struct i2c_adapter *adapter)
+{
+ u8 *block;
+
+ block = xmalloc(EDID_LENGTH);
+
+ if (edid_do_read_i2c(adapter, block, 0, EDID_LENGTH))
+ goto out;
+
+ return block;
+out:
+ free(block);
+
+ return NULL;
+}
+
+void fb_edid_add_modes(struct fb_info *info)
+{
+ if (info->edid_i2c_adapter)
+ info->edid_data = edid_read_i2c(info->edid_i2c_adapter);
+
+ if (!info->edid_data)
+ return;
+
+ edid_to_display_timings(&info->edid_modes, info->edid_data);
+}
diff --git a/drivers/video/edid.h b/drivers/video/edid.h
new file mode 100644
index 0000000000..006d9f2834
--- /dev/null
+++ b/drivers/video/edid.h
@@ -0,0 +1,138 @@
+/*
+ * drivers/video/edid.h - EDID/DDC Header
+ *
+ * Based on:
+ * 1. XFree86 4.3.0, edid.h
+ * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE>
+ *
+ * 2. John Fremlin <vii@users.sourceforge.net> and
+ * Ani Joshi <ajoshi@unixbox.com>
+ *
+ * DDC is a Trademark of VESA (Video Electronics Standard Association).
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+*/
+
+#ifndef __EDID_H__
+#define __EDID_H__
+
+#define EDID_LENGTH 0x80
+#define EDID_HEADER 0x00
+#define EDID_HEADER_END 0x07
+
+#define ID_MANUFACTURER_NAME 0x08
+#define ID_MANUFACTURER_NAME_END 0x09
+#define ID_MODEL 0x0a
+
+#define ID_SERIAL_NUMBER 0x0c
+
+#define MANUFACTURE_WEEK 0x10
+#define MANUFACTURE_YEAR 0x11
+
+#define EDID_STRUCT_VERSION 0x12
+#define EDID_STRUCT_REVISION 0x13
+
+#define EDID_STRUCT_DISPLAY 0x14
+
+#define DPMS_FLAGS 0x18
+#define ESTABLISHED_TIMING_1 0x23
+#define ESTABLISHED_TIMING_2 0x24
+#define MANUFACTURERS_TIMINGS 0x25
+
+/* standard timings supported */
+#define STD_TIMING 8
+#define STD_TIMING_DESCRIPTION_SIZE 2
+#define STD_TIMING_DESCRIPTIONS_START 0x26
+
+#define DETAILED_TIMING_DESCRIPTIONS_START 0x36
+#define DETAILED_TIMING_DESCRIPTION_SIZE 18
+#define NO_DETAILED_TIMING_DESCRIPTIONS 4
+
+#define DETAILED_TIMING_DESCRIPTION_1 0x36
+#define DETAILED_TIMING_DESCRIPTION_2 0x48
+#define DETAILED_TIMING_DESCRIPTION_3 0x5a
+#define DETAILED_TIMING_DESCRIPTION_4 0x6c
+
+#define DESCRIPTOR_DATA 5
+
+#define UPPER_NIBBLE( x ) \
+ (((128|64|32|16) & (x)) >> 4)
+
+#define LOWER_NIBBLE( x ) \
+ ((1|2|4|8) & (x))
+
+#define COMBINE_HI_8LO( hi, lo ) \
+ ( (((unsigned)hi) << 8) | (unsigned)lo )
+
+#define COMBINE_HI_4LO( hi, lo ) \
+ ( (((unsigned)hi) << 4) | (unsigned)lo )
+
+#define PIXEL_CLOCK_LO (unsigned)block[ 0 ]
+#define PIXEL_CLOCK_HI (unsigned)block[ 1 ]
+#define PIXEL_CLOCK (COMBINE_HI_8LO( PIXEL_CLOCK_HI,PIXEL_CLOCK_LO )*10000)
+#define H_ACTIVE_LO (unsigned)block[ 2 ]
+#define H_BLANKING_LO (unsigned)block[ 3 ]
+#define H_ACTIVE_HI UPPER_NIBBLE( (unsigned)block[ 4 ] )
+#define H_ACTIVE COMBINE_HI_8LO( H_ACTIVE_HI, H_ACTIVE_LO )
+#define H_BLANKING_HI LOWER_NIBBLE( (unsigned)block[ 4 ] )
+#define H_BLANKING COMBINE_HI_8LO( H_BLANKING_HI, H_BLANKING_LO )
+
+#define V_ACTIVE_LO (unsigned)block[ 5 ]
+#define V_BLANKING_LO (unsigned)block[ 6 ]
+#define V_ACTIVE_HI UPPER_NIBBLE( (unsigned)block[ 7 ] )
+#define V_ACTIVE COMBINE_HI_8LO( V_ACTIVE_HI, V_ACTIVE_LO )
+#define V_BLANKING_HI LOWER_NIBBLE( (unsigned)block[ 7 ] )
+#define V_BLANKING COMBINE_HI_8LO( V_BLANKING_HI, V_BLANKING_LO )
+
+#define H_SYNC_OFFSET_LO (unsigned)block[ 8 ]
+#define H_SYNC_WIDTH_LO (unsigned)block[ 9 ]
+
+#define V_SYNC_OFFSET_LO UPPER_NIBBLE( (unsigned)block[ 10 ] )
+#define V_SYNC_WIDTH_LO LOWER_NIBBLE( (unsigned)block[ 10 ] )
+
+#define V_SYNC_WIDTH_HI ((unsigned)block[ 11 ] & (1|2))
+#define V_SYNC_OFFSET_HI (((unsigned)block[ 11 ] & (4|8)) >> 2)
+
+#define H_SYNC_WIDTH_HI (((unsigned)block[ 11 ] & (16|32)) >> 4)
+#define H_SYNC_OFFSET_HI (((unsigned)block[ 11 ] & (64|128)) >> 6)
+
+#define V_SYNC_WIDTH COMBINE_HI_4LO( V_SYNC_WIDTH_HI, V_SYNC_WIDTH_LO )
+#define V_SYNC_OFFSET COMBINE_HI_4LO( V_SYNC_OFFSET_HI, V_SYNC_OFFSET_LO )
+
+#define H_SYNC_WIDTH COMBINE_HI_8LO( H_SYNC_WIDTH_HI, H_SYNC_WIDTH_LO )
+#define H_SYNC_OFFSET COMBINE_HI_8LO( H_SYNC_OFFSET_HI, H_SYNC_OFFSET_LO )
+
+#define H_SIZE_LO (unsigned)block[ 12 ]
+#define V_SIZE_LO (unsigned)block[ 13 ]
+
+#define H_SIZE_HI UPPER_NIBBLE( (unsigned)block[ 14 ] )
+#define V_SIZE_HI LOWER_NIBBLE( (unsigned)block[ 14 ] )
+
+#define H_SIZE COMBINE_HI_8LO( H_SIZE_HI, H_SIZE_LO )
+#define V_SIZE COMBINE_HI_8LO( V_SIZE_HI, V_SIZE_LO )
+
+#define H_BORDER (unsigned)block[ 15 ]
+#define V_BORDER (unsigned)block[ 16 ]
+
+#define FLAGS (unsigned)block[ 17 ]
+
+#define INTERLACED (FLAGS&128)
+#define SYNC_TYPE (FLAGS&3<<3) /* bits 4,3 */
+#define SYNC_SEPARATE (3<<3)
+#define HSYNC_POSITIVE (FLAGS & 4)
+#define VSYNC_POSITIVE (FLAGS & 2)
+
+#define V_MIN_RATE block[ 5 ]
+#define V_MAX_RATE block[ 6 ]
+#define H_MIN_RATE block[ 7 ]
+#define H_MAX_RATE block[ 8 ]
+#define MAX_PIXEL_CLOCK (((int)block[ 9 ]) * 10)
+#define GTF_SUPPORT block[10]
+
+#define DPMS_ACTIVE_OFF (1 << 5)
+#define DPMS_SUSPEND (1 << 6)
+#define DPMS_STANDBY (1 << 7)
+
+#endif /* __EDID_H__ */
diff --git a/drivers/video/fb.c b/drivers/video/fb.c
index c36b9adcea..2c8b8eb25e 100644
--- a/drivers/video/fb.c
+++ b/drivers/video/fb.c
@@ -49,32 +49,45 @@ static int fb_enable_set(struct param_d *param, void *priv)
return 0;
}
-static int fb_setup_mode(struct device_d *dev, struct param_d *param,
- const char *val)
+static struct fb_videomode *fb_num_to_mode(struct fb_info *info, int num)
{
- struct fb_info *info = dev->priv;
- int mode, ret;
+ int num_modes;
+
+ num_modes = info->modes.num_modes + info->edid_modes.num_modes;
+
+ if (num >= num_modes)
+ return NULL;
+
+ if (num >= info->modes.num_modes)
+ return &info->edid_modes.modes[num - info->modes.num_modes];
+
+ return &info->modes.modes[num];
+}
+
+static int fb_setup_mode(struct fb_info *info)
+{
+ struct device_d *dev = &info->dev;
+ int ret;
+ struct fb_videomode *mode;
if (info->enabled != 0)
return -EPERM;
- if (!val)
- return dev_param_set_generic(dev, param, NULL);
-
- for (mode = 0; mode < info->num_modes; mode++) {
- if (!strcmp(info->mode_list[mode].name, val))
- break;
- }
- if (mode >= info->num_modes)
+ mode = fb_num_to_mode(info, info->current_mode);
+ if (!mode)
return -EINVAL;
- info->mode = &info->mode_list[mode];
+ info->mode = mode;
info->xres = info->mode->xres;
info->yres = info->mode->yres;
info->line_length = 0;
- ret = info->fbops->fb_activate_var(info);
+ if (info->fbops->fb_activate_var) {
+ ret = info->fbops->fb_activate_var(info);
+ if (ret)
+ return ret;
+ }
if (!info->line_length)
info->line_length = info->xres * (info->bits_per_pixel >> 3);
@@ -85,13 +98,24 @@ static int fb_setup_mode(struct device_d *dev, struct param_d *param,
dev->resource[0].start = (resource_size_t)info->screen_base;
info->cdev.size = info->line_length * info->yres;
dev->resource[0].end = dev->resource[0].start + info->cdev.size - 1;
- dev_param_set_generic(dev, param, val);
} else
info->cdev.size = 0;
return ret;
}
+static int fb_set_modename(struct param_d *param, void *priv)
+{
+ struct fb_info *info = priv;
+ int ret;
+
+ ret = fb_setup_mode(info);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static struct file_operations fb_ops = {
.read = mem_read,
.write = mem_write,
@@ -100,22 +124,28 @@ static struct file_operations fb_ops = {
.ioctl = fb_ioctl,
};
-static void fb_info(struct device_d *dev)
+static void fb_print_mode(struct fb_videomode *mode)
+{
+ printf("%-20s %dx%d@%d\n", mode->name,
+ mode->xres, mode->yres, mode->refresh);
+}
+
+static void fb_print_modes(struct display_timings *modes)
{
- struct fb_info *info = dev->priv;
int i;
- if (!info->num_modes)
- return;
+ for (i = 0; i < modes->num_modes; i++)
+ fb_print_mode(&modes->modes[i]);
+}
+
+static void fb_info(struct device_d *dev)
+{
+ struct fb_info *info = dev->priv;
printf("available modes:\n");
- for (i = 0; i < info->num_modes; i++) {
- struct fb_videomode *mode = &info->mode_list[i];
-
- printf("%-10s %dx%d@%d\n", mode->name,
- mode->xres, mode->yres, mode->refresh);
- }
+ fb_print_modes(&info->modes);
+ fb_print_modes(&info->edid_modes);
printf("\n");
}
@@ -124,10 +154,20 @@ int register_framebuffer(struct fb_info *info)
{
int id = get_free_deviceid("fb");
struct device_d *dev;
- int ret;
+ int ret, num_modes, i;
+ const char **names;
dev = &info->dev;
+ /*
+ * If info->mode is set at this point it's the only mode
+ * the fb supports. move it over to the modes list.
+ */
+ if (info->mode) {
+ info->modes.modes = info->mode;
+ info->modes.num_modes = 1;
+ }
+
if (!info->line_length)
info->line_length = info->xres * (info->bits_per_pixel >> 3);
@@ -155,11 +195,22 @@ int register_framebuffer(struct fb_info *info)
dev_add_param_bool(dev, "enable", fb_enable_set, NULL,
&info->p_enable, info);
- if (info->num_modes && (info->mode_list != NULL) &&
- (info->fbops->fb_activate_var != NULL)) {
- dev_add_param(dev, "mode_name", fb_setup_mode, NULL, 0);
- dev_set_param(dev, "mode_name", info->mode_list[0].name);
- }
+ if (IS_ENABLED(CONFIG_DRIVER_VIDEO_EDID))
+ fb_edid_add_modes(info);
+
+ num_modes = info->modes.num_modes + info->edid_modes.num_modes;
+
+ names = xzalloc(sizeof(char *) * num_modes);
+
+ for (i = 0; i < info->modes.num_modes; i++)
+ names[i] = info->modes.modes[i].name;
+ for (i = 0; i < info->edid_modes.num_modes; i++)
+ names[i + info->modes.num_modes] = info->edid_modes.modes[i].name;
+ dev_add_param_enum(dev, "mode_name", fb_set_modename, NULL, &info->current_mode, names, num_modes, info);
+
+ info->mode = fb_num_to_mode(info, 0);
+
+ fb_setup_mode(info);
ret = devfs_create(&info->cdev);
if (ret)
diff --git a/drivers/video/imx-ipu-fb.c b/drivers/video/imx-ipu-fb.c
index 7276647382..abac812cb6 100644
--- a/drivers/video/imx-ipu-fb.c
+++ b/drivers/video/imx-ipu-fb.c
@@ -1007,8 +1007,8 @@ static int imxfb_probe(struct device_d *dev)
fbi->disable_fractional_divider = pdata->disable_fractional_divider;
info->priv = fbi;
info->fbops = &imxfb_ops;
- info->num_modes = pdata->num_modes;
- info->mode_list = pdata->mode;
+ info->modes.modes = pdata->mode;
+ info->modes.num_modes = pdata->num_modes;
imxfb_init_info(info, pdata->mode, pdata->bpp);
@@ -1047,14 +1047,9 @@ static int imxfb_probe(struct device_d *dev)
return ret;
}
-static void imxfb_remove(struct device_d *dev)
-{
-}
-
static struct driver_d imx3fb_driver = {
.name = "imx-ipu-fb",
.probe = imxfb_probe,
- .remove = imxfb_remove,
};
device_platform_driver(imx3fb_driver);
diff --git a/drivers/video/imx-ipu-v3/Kconfig b/drivers/video/imx-ipu-v3/Kconfig
new file mode 100644
index 0000000000..3d656e5193
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/Kconfig
@@ -0,0 +1,14 @@
+config DRIVER_VIDEO_IMX_IPUV3
+ bool "i.MX IPUv3 driver"
+ help
+ Support the IPUv3 found on Freescale i.MX51/53/6 SoCs
+
+if DRIVER_VIDEO_IMX_IPUV3
+
+config DRIVER_VIDEO_IMX_IPUV3_LVDS
+ bool "IPUv3 LVDS support"
+
+config DRIVER_VIDEO_IMX_IPUV3_HDMI
+ bool "IPUv3 HDMI support"
+
+endif
diff --git a/drivers/video/imx-ipu-v3/Makefile b/drivers/video/imx-ipu-v3/Makefile
new file mode 100644
index 0000000000..2bc0aec5ae
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3) += ipu-common.o ipu-dmfc.o ipu-di.o
+obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3) += ipu-dp.o ipuv3-plane.o ipufb.o
+obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3) += ipu-dc.o
+obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3_LVDS) += imx-ldb.o
+obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3_HDMI) += imx-hdmi.o
diff --git a/drivers/video/imx-ipu-v3/imx-hdmi.c b/drivers/video/imx-ipu-v3/imx-hdmi.c
new file mode 100644
index 0000000000..4f462889a8
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/imx-hdmi.c
@@ -0,0 +1,1297 @@
+/*
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * SH-Mobile High-Definition Multimedia Interface (HDMI) driver
+ * for SLISHDMI13T and SLIPHDMIT IP cores
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ */
+#include <common.h>
+#include <fb.h>
+#include <io.h>
+#include <driver.h>
+#include <malloc.h>
+#include <errno.h>
+#include <init.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <asm-generic/div64.h>
+#include <linux/clk.h>
+#include <i2c/i2c.h>
+#include <mach/imx6-regs.h>
+#include <mach/imx53-regs.h>
+
+#include "imx-ipu-v3.h"
+#include "ipuv3-plane.h"
+#include "imx-hdmi.h"
+
+#define HDMI_EDID_LEN 512
+
+#define RGB 0
+#define YCBCR444 1
+#define YCBCR422_16BITS 2
+#define YCBCR422_8BITS 3
+#define XVYCC444 4
+
+enum hdmi_colorimetry {
+ HDMI_COLORIMETRY_NONE,
+ HDMI_COLORIMETRY_ITU_601,
+ HDMI_COLORIMETRY_ITU_709,
+ HDMI_COLORIMETRY_EXTENDED,
+};
+
+enum hdmi_datamap {
+ RGB444_8B = 0x01,
+ RGB444_10B = 0x03,
+ RGB444_12B = 0x05,
+ RGB444_16B = 0x07,
+ YCbCr444_8B = 0x09,
+ YCbCr444_10B = 0x0B,
+ YCbCr444_12B = 0x0D,
+ YCbCr444_16B = 0x0F,
+ YCbCr422_8B = 0x16,
+ YCbCr422_10B = 0x14,
+ YCbCr422_12B = 0x12,
+};
+
+enum imx_hdmi_devtype {
+ IMX6Q_HDMI,
+ IMX6DL_HDMI,
+};
+
+static const u16 csc_coeff_default[3][4] = {
+ { 0x2000, 0x0000, 0x0000, 0x0000 },
+ { 0x0000, 0x2000, 0x0000, 0x0000 },
+ { 0x0000, 0x0000, 0x2000, 0x0000 }
+};
+
+static const u16 csc_coeff_rgb_out_eitu601[3][4] = {
+ { 0x2000, 0x6926, 0x74fd, 0x010e },
+ { 0x2000, 0x2cdd, 0x0000, 0x7e9a },
+ { 0x2000, 0x0000, 0x38b4, 0x7e3b }
+};
+
+static const u16 csc_coeff_rgb_out_eitu709[3][4] = {
+ { 0x2000, 0x7106, 0x7a02, 0x00a7 },
+ { 0x2000, 0x3264, 0x0000, 0x7e6d },
+ { 0x2000, 0x0000, 0x3b61, 0x7e25 }
+};
+
+static const u16 csc_coeff_rgb_in_eitu601[3][4] = {
+ { 0x2591, 0x1322, 0x074b, 0x0000 },
+ { 0x6535, 0x2000, 0x7acc, 0x0200 },
+ { 0x6acd, 0x7534, 0x2000, 0x0200 }
+};
+
+static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
+ { 0x2dc5, 0x0d9b, 0x049e, 0x0000 },
+ { 0x62f0, 0x2000, 0x7d11, 0x0200 },
+ { 0x6756, 0x78ab, 0x2000, 0x0200 }
+};
+
+struct hdmi_vmode {
+ bool mdvi;
+ bool mhsyncpolarity;
+ bool mvsyncpolarity;
+ bool minterlaced;
+ bool mdataenablepolarity;
+
+ unsigned int mpixelclock;
+ unsigned int mpixelrepetitioninput;
+ unsigned int mpixelrepetitionoutput;
+};
+
+struct hdmi_data_info {
+ unsigned int enc_in_format;
+ unsigned int enc_out_format;
+ unsigned int enc_color_depth;
+ unsigned int colorimetry;
+ unsigned int pix_repet_factor;
+ unsigned int hdcp_enable;
+ struct hdmi_vmode video_mode;
+};
+
+struct imx_hdmi {
+ enum imx_hdmi_devtype dev_type;
+ struct device_d *dev;
+ struct clk *isfr_clk;
+ struct clk *iahb_clk;
+
+ bool connected;
+
+ struct hdmi_data_info hdmi_data;
+ int vic;
+
+ u8 edid[HDMI_EDID_LEN];
+ bool cable_plugin;
+
+ bool phy_enabled;
+
+ struct regmap *regmap;
+ struct i2c_adapter *ddc;
+ void __iomem *regs;
+
+ unsigned int sample_rate;
+ int ratio;
+
+ struct ipu_output output;
+};
+
+static void imx_hdmi_set_ipu_di_mux(struct imx_hdmi *hdmi, int ipu_di)
+{
+ void __iomem *gpr3 = (void *)MX6_IOMUXC_BASE_ADDR + 0xc;
+ uint32_t val;
+
+ dev_info(hdmi->dev, "setup hdmi mux to %d\n", ipu_di);
+
+ val = readl(gpr3);
+ val &= ~(3 << 2);
+ val |= ipu_di << 2;
+ writel(val, gpr3);
+}
+
+static inline void hdmi_writeb(struct imx_hdmi *hdmi, u8 val, int offset)
+{
+ writeb(val, hdmi->regs + offset);
+}
+
+static inline u8 hdmi_readb(struct imx_hdmi *hdmi, int offset)
+{
+ return readb(hdmi->regs + offset);
+}
+
+static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
+{
+ u8 val = hdmi_readb(hdmi, reg) & ~mask;
+ val |= data & mask;
+ hdmi_writeb(hdmi, val, reg);
+}
+
+static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u8 data, unsigned int reg,
+ u8 shift, u8 mask)
+{
+ hdmi_modb(hdmi, data << shift, mask, reg);
+}
+
+static void hdmi_set_clock_regenerator_n(struct imx_hdmi *hdmi,
+ unsigned int value)
+{
+ hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1);
+ hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2);
+ hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3);
+
+ /* nshift factor = 0 */
+ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
+}
+
+static void hdmi_regenerate_cts(struct imx_hdmi *hdmi, unsigned int cts)
+{
+ /* Must be set/cleared first */
+ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+
+ hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
+ hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
+ hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
+ HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+}
+
+static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
+ unsigned int ratio)
+{
+ unsigned int n = (128 * freq) / 1000;
+
+ switch (freq) {
+ case 32000:
+ if (pixel_clk == 25170000)
+ n = (ratio == 150) ? 9152 : 4576;
+ else if (pixel_clk == 27020000)
+ n = (ratio == 150) ? 8192 : 4096;
+ else if (pixel_clk == 74170000 || pixel_clk == 148350000)
+ n = 11648;
+ else
+ n = 4096;
+ break;
+
+ case 44100:
+ if (pixel_clk == 25170000)
+ n = 7007;
+ else if (pixel_clk == 74170000)
+ n = 17836;
+ else if (pixel_clk == 148350000)
+ n = (ratio == 150) ? 17836 : 8918;
+ else
+ n = 6272;
+ break;
+
+ case 48000:
+ if (pixel_clk == 25170000)
+ n = (ratio == 150) ? 9152 : 6864;
+ else if (pixel_clk == 27020000)
+ n = (ratio == 150) ? 8192 : 6144;
+ else if (pixel_clk == 74170000)
+ n = 11648;
+ else if (pixel_clk == 148350000)
+ n = (ratio == 150) ? 11648 : 5824;
+ else
+ n = 6144;
+ break;
+
+ case 88200:
+ n = hdmi_compute_n(44100, pixel_clk, ratio) * 2;
+ break;
+
+ case 96000:
+ n = hdmi_compute_n(48000, pixel_clk, ratio) * 2;
+ break;
+
+ case 176400:
+ n = hdmi_compute_n(44100, pixel_clk, ratio) * 4;
+ break;
+
+ case 192000:
+ n = hdmi_compute_n(48000, pixel_clk, ratio) * 4;
+ break;
+
+ default:
+ break;
+ }
+
+ return n;
+}
+
+static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
+ unsigned int ratio)
+{
+ unsigned int cts = 0;
+
+ pr_debug("%s: freq: %d pixel_clk: %ld ratio: %d\n", __func__, freq,
+ pixel_clk, ratio);
+
+ switch (freq) {
+ case 32000:
+ if (pixel_clk == 297000000) {
+ cts = 222750;
+ break;
+ }
+ case 48000:
+ case 96000:
+ case 192000:
+ switch (pixel_clk) {
+ case 25200000:
+ case 27000000:
+ case 54000000:
+ case 74250000:
+ case 148500000:
+ cts = pixel_clk / 1000;
+ break;
+ case 297000000:
+ cts = 247500;
+ break;
+ /*
+ * All other TMDS clocks are not supported by
+ * DWC_hdmi_tx. The TMDS clocks divided or
+ * multiplied by 1,001 coefficients are not
+ * supported.
+ */
+ default:
+ break;
+ }
+ break;
+ case 44100:
+ case 88200:
+ case 176400:
+ switch (pixel_clk) {
+ case 25200000:
+ cts = 28000;
+ break;
+ case 27000000:
+ cts = 30000;
+ break;
+ case 54000000:
+ cts = 60000;
+ break;
+ case 74250000:
+ cts = 82500;
+ break;
+ case 148500000:
+ cts = 165000;
+ break;
+ case 297000000:
+ cts = 247500;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ if (ratio == 100)
+ return cts;
+ else
+ return (cts * ratio) / 100;
+}
+
+static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi,
+ unsigned long pixel_clk)
+{
+ unsigned int clk_n, clk_cts;
+
+ clk_n = hdmi_compute_n(hdmi->sample_rate, pixel_clk,
+ hdmi->ratio);
+ clk_cts = hdmi_compute_cts(hdmi->sample_rate, pixel_clk,
+ hdmi->ratio);
+
+ if (!clk_cts) {
+ dev_dbg(hdmi->dev, "%s: pixel clock not supported: %lu\n",
+ __func__, pixel_clk);
+ return;
+ }
+
+ dev_dbg(hdmi->dev, "%s: samplerate=%d ratio=%d pixelclk=%lu N=%d cts=%d\n",
+ __func__, hdmi->sample_rate, hdmi->ratio,
+ pixel_clk, clk_n, clk_cts);
+
+ hdmi_set_clock_regenerator_n(hdmi, clk_n);
+ hdmi_regenerate_cts(hdmi, clk_cts);
+}
+
+static void hdmi_init_clk_regenerator(struct imx_hdmi *hdmi)
+{
+ hdmi_set_clk_regenerator(hdmi, 74250000);
+}
+
+/*
+ * this submodule is responsible for the video data synchronization.
+ * for example, for RGB 4:4:4 input, the data map is defined as
+ * pin{47~40} <==> R[7:0]
+ * pin{31~24} <==> G[7:0]
+ * pin{15~8} <==> B[7:0]
+ */
+static void hdmi_video_sample(struct imx_hdmi *hdmi)
+{
+ int color_format = 0;
+ u8 val;
+
+ if (hdmi->hdmi_data.enc_in_format == RGB) {
+ if (hdmi->hdmi_data.enc_color_depth == 8)
+ color_format = 0x01;
+ else if (hdmi->hdmi_data.enc_color_depth == 10)
+ color_format = 0x03;
+ else if (hdmi->hdmi_data.enc_color_depth == 12)
+ color_format = 0x05;
+ else if (hdmi->hdmi_data.enc_color_depth == 16)
+ color_format = 0x07;
+ else
+ return;
+ } else if (hdmi->hdmi_data.enc_in_format == YCBCR444) {
+ if (hdmi->hdmi_data.enc_color_depth == 8)
+ color_format = 0x09;
+ else if (hdmi->hdmi_data.enc_color_depth == 10)
+ color_format = 0x0B;
+ else if (hdmi->hdmi_data.enc_color_depth == 12)
+ color_format = 0x0D;
+ else if (hdmi->hdmi_data.enc_color_depth == 16)
+ color_format = 0x0F;
+ else
+ return;
+ } else if (hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) {
+ if (hdmi->hdmi_data.enc_color_depth == 8)
+ color_format = 0x16;
+ else if (hdmi->hdmi_data.enc_color_depth == 10)
+ color_format = 0x14;
+ else if (hdmi->hdmi_data.enc_color_depth == 12)
+ color_format = 0x12;
+ else
+ return;
+ }
+
+ val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
+ ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
+ HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
+ hdmi_writeb(hdmi, val, HDMI_TX_INVID0);
+
+ /* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
+ val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
+ HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
+ HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
+ hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING);
+ hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0);
+ hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1);
+ hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0);
+ hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1);
+ hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0);
+ hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1);
+}
+
+static int is_color_space_conversion(struct imx_hdmi *hdmi)
+{
+ return (hdmi->hdmi_data.enc_in_format !=
+ hdmi->hdmi_data.enc_out_format);
+}
+
+static int is_color_space_decimation(struct imx_hdmi *hdmi)
+{
+ return ((hdmi->hdmi_data.enc_out_format == YCBCR422_8BITS) &&
+ (hdmi->hdmi_data.enc_in_format == RGB ||
+ hdmi->hdmi_data.enc_in_format == YCBCR444));
+}
+
+static int is_color_space_interpolation(struct imx_hdmi *hdmi)
+{
+ return ((hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) &&
+ (hdmi->hdmi_data.enc_out_format == RGB ||
+ hdmi->hdmi_data.enc_out_format == YCBCR444));
+}
+
+static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi)
+{
+ const u16 (*csc_coeff)[3][4] = &csc_coeff_default;
+ unsigned i;
+ u32 csc_scale = 1;
+
+ if (is_color_space_conversion(hdmi)) {
+ if (hdmi->hdmi_data.enc_out_format == RGB) {
+ if (hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_601)
+ csc_coeff = &csc_coeff_rgb_out_eitu601;
+ else
+ csc_coeff = &csc_coeff_rgb_out_eitu709;
+ } else if (hdmi->hdmi_data.enc_in_format == RGB) {
+ if (hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_601)
+ csc_coeff = &csc_coeff_rgb_in_eitu601;
+ else
+ csc_coeff = &csc_coeff_rgb_in_eitu709;
+ csc_scale = 0;
+ }
+ }
+
+ /* The CSC registers are sequential, alternating MSB then LSB */
+ for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) {
+ u16 coeff_a = (*csc_coeff)[0][i];
+ u16 coeff_b = (*csc_coeff)[1][i];
+ u16 coeff_c = (*csc_coeff)[2][i];
+
+ hdmi_writeb(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2);
+ hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
+ hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
+ hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
+ hdmi_writeb(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2);
+ hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
+ }
+
+ hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
+ HDMI_CSC_SCALE);
+}
+
+static void hdmi_video_csc(struct imx_hdmi *hdmi)
+{
+ int color_depth = 0;
+ int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE;
+ int decimation = 0;
+
+ /* YCC422 interpolation to 444 mode */
+ if (is_color_space_interpolation(hdmi))
+ interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1;
+ else if (is_color_space_decimation(hdmi))
+ decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3;
+
+ if (hdmi->hdmi_data.enc_color_depth == 8)
+ color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP;
+ else if (hdmi->hdmi_data.enc_color_depth == 10)
+ color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP;
+ else if (hdmi->hdmi_data.enc_color_depth == 12)
+ color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP;
+ else if (hdmi->hdmi_data.enc_color_depth == 16)
+ color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP;
+ else
+ return;
+
+ /* Configure the CSC registers */
+ hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG);
+ hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
+ HDMI_CSC_SCALE);
+
+ imx_hdmi_update_csc_coeffs(hdmi);
+}
+
+/*
+ * HDMI video packetizer is used to packetize the data.
+ * for example, if input is YCC422 mode or repeater is used,
+ * data should be repacked this module can be bypassed.
+ */
+static void hdmi_video_packetize(struct imx_hdmi *hdmi)
+{
+ unsigned int color_depth = 0;
+ unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit;
+ unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
+ struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
+ u8 val, vp_conf;
+
+ if (hdmi_data->enc_out_format == RGB
+ || hdmi_data->enc_out_format == YCBCR444) {
+ if (!hdmi_data->enc_color_depth)
+ output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
+ else if (hdmi_data->enc_color_depth == 8) {
+ color_depth = 4;
+ output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
+ } else if (hdmi_data->enc_color_depth == 10)
+ color_depth = 5;
+ else if (hdmi_data->enc_color_depth == 12)
+ color_depth = 6;
+ else if (hdmi_data->enc_color_depth == 16)
+ color_depth = 7;
+ else
+ return;
+ } else if (hdmi_data->enc_out_format == YCBCR422_8BITS) {
+ if (!hdmi_data->enc_color_depth ||
+ hdmi_data->enc_color_depth == 8)
+ remap_size = HDMI_VP_REMAP_YCC422_16bit;
+ else if (hdmi_data->enc_color_depth == 10)
+ remap_size = HDMI_VP_REMAP_YCC422_20bit;
+ else if (hdmi_data->enc_color_depth == 12)
+ remap_size = HDMI_VP_REMAP_YCC422_24bit;
+ else
+ return;
+ output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422;
+ } else
+ return;
+
+ /* set the packetizer registers */
+ val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
+ HDMI_VP_PR_CD_COLOR_DEPTH_MASK) |
+ ((hdmi_data->pix_repet_factor <<
+ HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) &
+ HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
+ hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
+
+ hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
+ HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
+
+ /* Data from pixel repeater block */
+ if (hdmi_data->pix_repet_factor > 1) {
+ vp_conf = HDMI_VP_CONF_PR_EN_ENABLE |
+ HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER;
+ } else { /* data from packetizer block */
+ vp_conf = HDMI_VP_CONF_PR_EN_DISABLE |
+ HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
+ }
+
+ hdmi_modb(hdmi, vp_conf,
+ HDMI_VP_CONF_PR_EN_MASK |
+ HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF);
+
+ hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
+ HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
+
+ hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP);
+
+ if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
+ vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
+ HDMI_VP_CONF_PP_EN_ENABLE |
+ HDMI_VP_CONF_YCC422_EN_DISABLE;
+ } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) {
+ vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
+ HDMI_VP_CONF_PP_EN_DISABLE |
+ HDMI_VP_CONF_YCC422_EN_ENABLE;
+ } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) {
+ vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE |
+ HDMI_VP_CONF_PP_EN_DISABLE |
+ HDMI_VP_CONF_YCC422_EN_DISABLE;
+ } else {
+ return;
+ }
+
+ hdmi_modb(hdmi, vp_conf,
+ HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK |
+ HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF);
+
+ hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
+ HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE,
+ HDMI_VP_STUFF_PP_STUFFING_MASK |
+ HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF);
+
+ hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
+ HDMI_VP_CONF);
+}
+
+static inline void hdmi_phy_test_clear(struct imx_hdmi *hdmi,
+ unsigned char bit)
+{
+ hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
+ HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0);
+}
+
+static inline void hdmi_phy_test_enable(struct imx_hdmi *hdmi,
+ unsigned char bit)
+{
+ hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
+ HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0);
+}
+
+static inline void hdmi_phy_test_clock(struct imx_hdmi *hdmi,
+ unsigned char bit)
+{
+ hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
+ HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0);
+}
+
+static inline void hdmi_phy_test_din(struct imx_hdmi *hdmi,
+ unsigned char bit)
+{
+ hdmi_writeb(hdmi, bit, HDMI_PHY_TST1);
+}
+
+static inline void hdmi_phy_test_dout(struct imx_hdmi *hdmi,
+ unsigned char bit)
+{
+ hdmi_writeb(hdmi, bit, HDMI_PHY_TST2);
+}
+
+static bool hdmi_phy_wait_i2c_done(struct imx_hdmi *hdmi, int msec)
+{
+ unsigned char val = 0;
+ val = hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3;
+ while (!val) {
+ udelay(1000);
+ if (msec-- == 0)
+ return false;
+ val = hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3;
+ }
+ return true;
+}
+
+static void __hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data,
+ unsigned char addr)
+{
+ hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
+ hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
+ hdmi_writeb(hdmi, (unsigned char)(data >> 8),
+ HDMI_PHY_I2CM_DATAO_1_ADDR);
+ hdmi_writeb(hdmi, (unsigned char)(data >> 0),
+ HDMI_PHY_I2CM_DATAO_0_ADDR);
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
+ HDMI_PHY_I2CM_OPERATION_ADDR);
+ hdmi_phy_wait_i2c_done(hdmi, 1000);
+}
+
+static int hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data,
+ unsigned char addr)
+{
+ __hdmi_phy_i2c_write(hdmi, data, addr);
+ return 0;
+}
+
+static void imx_hdmi_phy_enable_power(struct imx_hdmi *hdmi, u8 enable)
+{
+ hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_PDZ_OFFSET,
+ HDMI_PHY_CONF0_PDZ_MASK);
+}
+
+static void imx_hdmi_phy_enable_tmds(struct imx_hdmi *hdmi, u8 enable)
+{
+ hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_ENTMDS_OFFSET,
+ HDMI_PHY_CONF0_ENTMDS_MASK);
+}
+
+static void imx_hdmi_phy_gen2_pddq(struct imx_hdmi *hdmi, u8 enable)
+{
+ hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
+ HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
+}
+
+static void imx_hdmi_phy_gen2_txpwron(struct imx_hdmi *hdmi, u8 enable)
+{
+ hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
+ HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
+}
+
+static void imx_hdmi_phy_sel_data_en_pol(struct imx_hdmi *hdmi, u8 enable)
+{
+ hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_SELDATAENPOL_OFFSET,
+ HDMI_PHY_CONF0_SELDATAENPOL_MASK);
+}
+
+static void imx_hdmi_phy_sel_interface_control(struct imx_hdmi *hdmi, u8 enable)
+{
+ hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_SELDIPIF_OFFSET,
+ HDMI_PHY_CONF0_SELDIPIF_MASK);
+}
+
+enum {
+ RES_8,
+ RES_10,
+ RES_12,
+ RES_MAX,
+};
+
+struct mpll_config {
+ unsigned long mpixelclock;
+ struct {
+ u16 cpce;
+ u16 gmp;
+ } res[RES_MAX];
+};
+
+static const struct mpll_config mpll_config[] = {
+ {
+ 45250000, {
+ { 0x01e0, 0x0000 },
+ { 0x21e1, 0x0000 },
+ { 0x41e2, 0x0000 }
+ },
+ }, {
+ 92500000, {
+ { 0x0140, 0x0005 },
+ { 0x2141, 0x0005 },
+ { 0x4142, 0x0005 },
+ },
+ }, {
+ 148500000, {
+ { 0x00a0, 0x000a },
+ { 0x20a1, 0x000a },
+ { 0x40a2, 0x000a },
+ },
+ }, {
+ ~0UL, {
+ { 0x00a0, 0x000a },
+ { 0x2001, 0x000f },
+ { 0x4002, 0x000f },
+ },
+ }
+};
+
+struct curr_ctrl {
+ unsigned long mpixelclock;
+ u16 curr[RES_MAX];
+};
+
+static const struct curr_ctrl curr_ctrl[] = {
+ /* pixelclk bpp8 bpp10 bpp12 */
+ {
+ 54000000, { 0x091c, 0x091c, 0x06dc },
+ }, {
+ 58400000, { 0x091c, 0x06dc, 0x06dc },
+ }, {
+ 72000000, { 0x06dc, 0x06dc, 0x091c },
+ }, {
+ 74250000, { 0x06dc, 0x0b5c, 0x091c },
+ }, {
+ 118800000, { 0x091c, 0x091c, 0x06dc },
+ }, {
+ 216000000, { 0x06dc, 0x0b5c, 0x091c },
+ }
+};
+
+static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
+ unsigned char res, int cscon)
+{
+ unsigned res_idx, i;
+ u8 val, msec;
+
+ if (prep)
+ return -EINVAL;
+
+ switch (res) {
+ case 0: /* color resolution 0 is 8 bit colour depth */
+ case 8:
+ res_idx = RES_8;
+ break;
+ case 10:
+ res_idx = RES_10;
+ break;
+ case 12:
+ res_idx = RES_12;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Enable csc path */
+ if (cscon)
+ val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH;
+ else
+ val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS;
+
+ hdmi_writeb(hdmi, val, HDMI_MC_FLOWCTRL);
+
+ /* gen2 tx power off */
+ imx_hdmi_phy_gen2_txpwron(hdmi, 0);
+
+ /* gen2 pddq */
+ imx_hdmi_phy_gen2_pddq(hdmi, 1);
+
+ /* PHY reset */
+ hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
+ hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
+
+ hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
+
+ hdmi_phy_test_clear(hdmi, 1);
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
+ HDMI_PHY_I2CM_SLAVE_ADDR);
+ hdmi_phy_test_clear(hdmi, 0);
+
+ /* PLL/MPLL Cfg - always match on final entry */
+ for (i = 0; i < ARRAY_SIZE(mpll_config) - 1; i++)
+ if (hdmi->hdmi_data.video_mode.mpixelclock <=
+ mpll_config[i].mpixelclock)
+ break;
+
+ hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].cpce, 0x06);
+ hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].gmp, 0x15);
+
+ for (i = 0; i < ARRAY_SIZE(curr_ctrl); i++)
+ if (hdmi->hdmi_data.video_mode.mpixelclock <=
+ curr_ctrl[i].mpixelclock)
+ break;
+
+ if (i >= ARRAY_SIZE(curr_ctrl)) {
+ dev_err(hdmi->dev,
+ "Pixel clock %d - unsupported by HDMI\n",
+ hdmi->hdmi_data.video_mode.mpixelclock);
+ return -EINVAL;
+ }
+
+ /* CURRCTRL */
+ hdmi_phy_i2c_write(hdmi, curr_ctrl[i].curr[res_idx], 0x10);
+
+ hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */
+ hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
+ /* RESISTANCE TERM 133Ohm Cfg */
+ hdmi_phy_i2c_write(hdmi, 0x0005, 0x19); /* TXTERM */
+ /* PREEMP Cgf 0.00 */
+ hdmi_phy_i2c_write(hdmi, 0x800d, 0x09); /* CKSYMTXCTRL */
+ /* TX/CK LVL 10 */
+ hdmi_phy_i2c_write(hdmi, 0x01ad, 0x0E); /* VLEVCTRL */
+ /* REMOVE CLK TERM */
+ hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */
+
+ imx_hdmi_phy_enable_power(hdmi, 1);
+
+ /* toggle TMDS enable */
+ imx_hdmi_phy_enable_tmds(hdmi, 0);
+ imx_hdmi_phy_enable_tmds(hdmi, 1);
+
+ /* gen2 tx power on */
+ imx_hdmi_phy_gen2_txpwron(hdmi, 1);
+ imx_hdmi_phy_gen2_pddq(hdmi, 0);
+
+ /*Wait for PHY PLL lock */
+ msec = 5;
+ do {
+ val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
+ if (!val)
+ break;
+
+ if (msec == 0) {
+ dev_err(hdmi->dev, "PHY PLL not locked\n");
+ return -ETIMEDOUT;
+ }
+
+ udelay(1000);
+ msec--;
+ } while (1);
+
+ return 0;
+}
+
+static int imx_hdmi_phy_init(struct imx_hdmi *hdmi)
+{
+ int i, ret;
+ bool cscon = false;
+
+ /*check csc whether needed activated in HDMI mode */
+ cscon = (is_color_space_conversion(hdmi) &&
+ !hdmi->hdmi_data.video_mode.mdvi);
+
+ /* HDMI Phy spec says to do the phy initialization sequence twice */
+ for (i = 0; i < 2; i++) {
+ imx_hdmi_phy_sel_data_en_pol(hdmi, 1);
+ imx_hdmi_phy_sel_interface_control(hdmi, 0);
+ imx_hdmi_phy_enable_tmds(hdmi, 0);
+ imx_hdmi_phy_enable_power(hdmi, 0);
+
+ /* Enable CSC */
+ ret = hdmi_phy_configure(hdmi, 0, 8, cscon);
+ if (ret)
+ return ret;
+ }
+
+ hdmi->phy_enabled = true;
+ return 0;
+}
+
+static void hdmi_tx_hdcp_config(struct imx_hdmi *hdmi)
+{
+ u8 de;
+
+ if (hdmi->hdmi_data.video_mode.mdataenablepolarity)
+ de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH;
+ else
+ de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
+
+ /* disable rx detect */
+ hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
+ HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
+
+ hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
+
+ hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
+}
+
+static void imx_hdmi_phy_disable(struct imx_hdmi *hdmi)
+{
+ if (!hdmi->phy_enabled)
+ return;
+
+ imx_hdmi_phy_enable_tmds(hdmi, 0);
+ imx_hdmi_phy_enable_power(hdmi, 0);
+
+ hdmi->phy_enabled = false;
+}
+
+/* HDMI Initialization Step B.4 */
+static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi)
+{
+ u8 clkdis;
+
+ /* control period minimum duration */
+ hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
+ hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
+ hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC);
+
+ /* Set to fill TMDS data channels */
+ hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM);
+ hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM);
+ hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
+
+ /* Enable pixel clock and tmds data path */
+ clkdis = 0x7F;
+ clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
+ hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+
+ clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+ hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+
+ /* Enable csc path */
+ if (is_color_space_conversion(hdmi)) {
+ clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
+ hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+ }
+}
+
+/* Workaround to clear the overflow condition */
+static void imx_hdmi_clear_overflow(struct imx_hdmi *hdmi)
+{
+ int count;
+ u8 val;
+
+ /* TMDS software reset */
+ hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
+
+ val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF);
+ if (hdmi->dev_type == IMX6DL_HDMI) {
+ hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
+ return;
+ }
+
+ for (count = 0; count < 4; count++)
+ hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
+}
+
+static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct fb_videomode *mode)
+{
+ int ret;
+
+ hdmi->vic = 0;
+
+ dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n");
+ hdmi->hdmi_data.video_mode.mdvi = true;
+
+ hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
+ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
+ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
+
+ /* TODO: Get input format from IPU (via FB driver interface) */
+ hdmi->hdmi_data.enc_in_format = RGB;
+
+ hdmi->hdmi_data.enc_out_format = RGB;
+
+ hdmi->hdmi_data.enc_color_depth = 8;
+ hdmi->hdmi_data.pix_repet_factor = 0;
+ hdmi->hdmi_data.hdcp_enable = 0;
+ hdmi->hdmi_data.video_mode.mdataenablepolarity = true;
+
+ /* HDMI Initializateion Step B.2 */
+ ret = imx_hdmi_phy_init(hdmi);
+ if (ret)
+ return ret;
+
+ /* HDMI Initialization Step B.3 */
+ imx_hdmi_enable_video_path(hdmi);
+
+ /* not for DVI mode */
+ dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
+
+ hdmi_video_packetize(hdmi);
+ hdmi_video_csc(hdmi);
+ hdmi_video_sample(hdmi);
+ hdmi_tx_hdcp_config(hdmi);
+
+ imx_hdmi_clear_overflow(hdmi);
+
+ return 0;
+}
+
+static void initialize_hdmi_ih_mutes(struct imx_hdmi *hdmi)
+{
+ u8 ih_mute;
+
+ /*
+ * Boot up defaults are:
+ * HDMI_IH_MUTE = 0x03 (disabled)
+ * HDMI_IH_MUTE_* = 0x00 (enabled)
+ *
+ * Disable top level interrupt bits in HDMI block
+ */
+ ih_mute = hdmi_readb(hdmi, HDMI_IH_MUTE) |
+ HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
+ HDMI_IH_MUTE_MUTE_ALL_INTERRUPT;
+
+ hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+
+ /* by default mask all interrupts */
+ hdmi_writeb(hdmi, 0xff, HDMI_VP_MASK);
+ hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0);
+ hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1);
+ hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2);
+ hdmi_writeb(hdmi, 0xff, HDMI_PHY_MASK0);
+ hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR);
+ hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
+ hdmi_writeb(hdmi, 0xff, HDMI_AUD_INT);
+ hdmi_writeb(hdmi, 0xff, HDMI_AUD_SPDIFINT);
+ hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK);
+ hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK);
+ hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
+ hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);
+ hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT);
+ hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT);
+
+ /* Disable interrupts in the IH_MUTE_* registers */
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
+ hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+ /* Enable top level interrupt bits in HDMI block */
+ ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
+ HDMI_IH_MUTE_MUTE_ALL_INTERRUPT);
+ hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+}
+
+struct imx_hdmi_data {
+ unsigned ipu_mask;
+ enum imx_hdmi_devtype devtype;
+};
+
+static struct imx_hdmi_data imx6q_hdmi_data = {
+ .ipu_mask = 0xf,
+ .devtype = IMX6Q_HDMI,
+};
+
+static struct imx_hdmi_data imx6dl_hdmi_data = {
+ .ipu_mask = 0x3,
+ .devtype = IMX6DL_HDMI,
+};
+
+static struct of_device_id imx_hdmi_dt_ids[] = {
+ {
+ .compatible = "fsl,imx6q-hdmi",
+ .data = (unsigned long)&imx6q_hdmi_data,
+ }, {
+ .compatible = "fsl,imx6dl-hdmi",
+ .data = (unsigned long)&imx6dl_hdmi_data,
+ }, {
+ /* sentinel */
+ }
+};
+
+static int imx_hdmi_prepare(struct ipu_output *output, struct fb_videomode *mode, int di)
+{
+ struct imx_hdmi *hdmi = container_of(output, struct imx_hdmi, output);
+
+ imx_hdmi_set_ipu_di_mux(hdmi, di);
+
+ return 0;
+}
+
+static int imx_hdmi_commit(struct ipu_output *output, struct fb_videomode *mode, int di)
+{
+ struct imx_hdmi *hdmi = container_of(output, struct imx_hdmi, output);
+
+ imx_hdmi_setup(hdmi, mode);
+
+ return 0;
+}
+
+static int imx_hdmi_disable(struct ipu_output *output)
+{
+ struct imx_hdmi *hdmi = container_of(output, struct imx_hdmi, output);
+
+ imx_hdmi_phy_disable(hdmi);
+
+ return 0;
+}
+
+static struct ipu_output_ops imx_hdmi_ops = {
+ .prepare = imx_hdmi_prepare,
+ .enable = imx_hdmi_commit,
+ .disable = imx_hdmi_disable,
+};
+
+static int imx_hdmi_probe(struct device_d *dev)
+{
+ struct device_node *np = dev->device_node;
+ struct device_node *ddc_node;
+ struct imx_hdmi *hdmi;
+ int ret;
+ const struct imx_hdmi_data *devtype;
+
+ ret = dev_get_drvdata(dev, (unsigned long *)&devtype);
+ if (ret)
+ return ret;
+
+ hdmi = xzalloc(sizeof(*hdmi));
+
+ hdmi->dev = dev;
+ hdmi->connected = 0;
+ hdmi->sample_rate = 48000;
+ hdmi->ratio = 100;
+
+ ret = dev_get_drvdata(dev, (unsigned long *)&hdmi->dev_type);
+ if (ret)
+ return ret;
+
+ ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
+ if (ddc_node) {
+ hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
+ if (!hdmi->ddc)
+ dev_dbg(hdmi->dev, "failed to read ddc node\n");
+ } else {
+ dev_dbg(hdmi->dev, "no ddc property found\n");
+ }
+
+ ddc_node = NULL;
+
+ hdmi->regs = dev_request_mem_region(dev, 0);
+ if (!hdmi->regs)
+ return -EBUSY;
+
+ hdmi->isfr_clk = clk_get(hdmi->dev, "isfr");
+ if (IS_ERR(hdmi->isfr_clk)) {
+ ret = PTR_ERR(hdmi->isfr_clk);
+ dev_err(dev,
+ "Unable to get HDMI isfr clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_enable(hdmi->isfr_clk);
+ if (ret)
+ return ret;
+
+ hdmi->iahb_clk = clk_get(hdmi->dev, "iahb");
+ if (IS_ERR(hdmi->iahb_clk)) {
+ ret = PTR_ERR(hdmi->iahb_clk);
+ dev_err(dev,
+ "Unable to get HDMI iahb clk: %d\n", ret);
+ goto err_isfr;
+ }
+
+ ret = clk_enable(hdmi->iahb_clk);
+ if (ret)
+ goto err_isfr;
+
+ /* Product and revision IDs */
+ dev_info(dev,
+ "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n",
+ hdmi_readb(hdmi, HDMI_DESIGN_ID),
+ hdmi_readb(hdmi, HDMI_REVISION_ID),
+ hdmi_readb(hdmi, HDMI_PRODUCT_ID0),
+ hdmi_readb(hdmi, HDMI_PRODUCT_ID1));
+
+ initialize_hdmi_ih_mutes(hdmi);
+
+ /*
+ * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
+ * N and cts values before enabling phy
+ */
+ hdmi_init_clk_regenerator(hdmi);
+
+ /*
+ * Configure registers related to HDMI interrupt
+ * generation before registering IRQ.
+ */
+ hdmi_writeb(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0);
+
+ /* Clear Hotplug interrupts */
+ hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+ HDMI_PHY_I2CM_INT_ADDR);
+
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+ HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
+ HDMI_PHY_I2CM_CTLINT_ADDR);
+
+ /* enable cable hot plug irq */
+ hdmi_writeb(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
+
+ /* Unmute interrupts */
+ hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+
+ hdmi->output.ops = &imx_hdmi_ops;
+ hdmi->output.di_clkflags = IPU_DI_CLKMODE_EXT | IPU_DI_CLKMODE_SYNC;
+ hdmi->output.out_pixel_fmt = V4L2_PIX_FMT_RGB24;
+ hdmi->output.name = asprintf("hdmi-0");
+ hdmi->output.ipu_mask = devtype->ipu_mask;
+ hdmi->output.edid_i2c_adapter = hdmi->ddc;
+ hdmi->output.modes = of_get_display_timings(np);
+
+ ipu_register_output(&hdmi->output);
+
+ return 0;
+
+err_isfr:
+ clk_disable(hdmi->isfr_clk);
+
+ return ret;
+}
+
+static struct driver_d imx_hdmi_driver = {
+ .probe = imx_hdmi_probe,
+ .of_compatible = imx_hdmi_dt_ids,
+ .name = "imx-hdmi",
+};
+device_platform_driver(imx_hdmi_driver);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("i.MX6 HDMI transmitter driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/imx-ipu-v3/imx-hdmi.h b/drivers/video/imx-ipu-v3/imx-hdmi.h
new file mode 100644
index 0000000000..39b677689d
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/imx-hdmi.h
@@ -0,0 +1,1032 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __IMX_HDMI_H__
+#define __IMX_HDMI_H__
+
+/* Identification Registers */
+#define HDMI_DESIGN_ID 0x0000
+#define HDMI_REVISION_ID 0x0001
+#define HDMI_PRODUCT_ID0 0x0002
+#define HDMI_PRODUCT_ID1 0x0003
+#define HDMI_CONFIG0_ID 0x0004
+#define HDMI_CONFIG1_ID 0x0005
+#define HDMI_CONFIG2_ID 0x0006
+#define HDMI_CONFIG3_ID 0x0007
+
+/* Interrupt Registers */
+#define HDMI_IH_FC_STAT0 0x0100
+#define HDMI_IH_FC_STAT1 0x0101
+#define HDMI_IH_FC_STAT2 0x0102
+#define HDMI_IH_AS_STAT0 0x0103
+#define HDMI_IH_PHY_STAT0 0x0104
+#define HDMI_IH_I2CM_STAT0 0x0105
+#define HDMI_IH_CEC_STAT0 0x0106
+#define HDMI_IH_VP_STAT0 0x0107
+#define HDMI_IH_I2CMPHY_STAT0 0x0108
+#define HDMI_IH_AHBDMAAUD_STAT0 0x0109
+
+#define HDMI_IH_MUTE_FC_STAT0 0x0180
+#define HDMI_IH_MUTE_FC_STAT1 0x0181
+#define HDMI_IH_MUTE_FC_STAT2 0x0182
+#define HDMI_IH_MUTE_AS_STAT0 0x0183
+#define HDMI_IH_MUTE_PHY_STAT0 0x0184
+#define HDMI_IH_MUTE_I2CM_STAT0 0x0185
+#define HDMI_IH_MUTE_CEC_STAT0 0x0186
+#define HDMI_IH_MUTE_VP_STAT0 0x0187
+#define HDMI_IH_MUTE_I2CMPHY_STAT0 0x0188
+#define HDMI_IH_MUTE_AHBDMAAUD_STAT0 0x0189
+#define HDMI_IH_MUTE 0x01FF
+
+/* Video Sample Registers */
+#define HDMI_TX_INVID0 0x0200
+#define HDMI_TX_INSTUFFING 0x0201
+#define HDMI_TX_GYDATA0 0x0202
+#define HDMI_TX_GYDATA1 0x0203
+#define HDMI_TX_RCRDATA0 0x0204
+#define HDMI_TX_RCRDATA1 0x0205
+#define HDMI_TX_BCBDATA0 0x0206
+#define HDMI_TX_BCBDATA1 0x0207
+
+/* Video Packetizer Registers */
+#define HDMI_VP_STATUS 0x0800
+#define HDMI_VP_PR_CD 0x0801
+#define HDMI_VP_STUFF 0x0802
+#define HDMI_VP_REMAP 0x0803
+#define HDMI_VP_CONF 0x0804
+#define HDMI_VP_STAT 0x0805
+#define HDMI_VP_INT 0x0806
+#define HDMI_VP_MASK 0x0807
+#define HDMI_VP_POL 0x0808
+
+/* Frame Composer Registers */
+#define HDMI_FC_INVIDCONF 0x1000
+#define HDMI_FC_INHACTV0 0x1001
+#define HDMI_FC_INHACTV1 0x1002
+#define HDMI_FC_INHBLANK0 0x1003
+#define HDMI_FC_INHBLANK1 0x1004
+#define HDMI_FC_INVACTV0 0x1005
+#define HDMI_FC_INVACTV1 0x1006
+#define HDMI_FC_INVBLANK 0x1007
+#define HDMI_FC_HSYNCINDELAY0 0x1008
+#define HDMI_FC_HSYNCINDELAY1 0x1009
+#define HDMI_FC_HSYNCINWIDTH0 0x100A
+#define HDMI_FC_HSYNCINWIDTH1 0x100B
+#define HDMI_FC_VSYNCINDELAY 0x100C
+#define HDMI_FC_VSYNCINWIDTH 0x100D
+#define HDMI_FC_INFREQ0 0x100E
+#define HDMI_FC_INFREQ1 0x100F
+#define HDMI_FC_INFREQ2 0x1010
+#define HDMI_FC_CTRLDUR 0x1011
+#define HDMI_FC_EXCTRLDUR 0x1012
+#define HDMI_FC_EXCTRLSPAC 0x1013
+#define HDMI_FC_CH0PREAM 0x1014
+#define HDMI_FC_CH1PREAM 0x1015
+#define HDMI_FC_CH2PREAM 0x1016
+#define HDMI_FC_AVICONF3 0x1017
+#define HDMI_FC_GCP 0x1018
+#define HDMI_FC_AVICONF0 0x1019
+#define HDMI_FC_AVICONF1 0x101A
+#define HDMI_FC_AVICONF2 0x101B
+#define HDMI_FC_AVIVID 0x101C
+#define HDMI_FC_AVIETB0 0x101D
+#define HDMI_FC_AVIETB1 0x101E
+#define HDMI_FC_AVISBB0 0x101F
+#define HDMI_FC_AVISBB1 0x1020
+#define HDMI_FC_AVIELB0 0x1021
+#define HDMI_FC_AVIELB1 0x1022
+#define HDMI_FC_AVISRB0 0x1023
+#define HDMI_FC_AVISRB1 0x1024
+#define HDMI_FC_AUDICONF0 0x1025
+#define HDMI_FC_AUDICONF1 0x1026
+#define HDMI_FC_AUDICONF2 0x1027
+#define HDMI_FC_AUDICONF3 0x1028
+#define HDMI_FC_VSDIEEEID0 0x1029
+#define HDMI_FC_VSDSIZE 0x102A
+#define HDMI_FC_VSDIEEEID1 0x1030
+#define HDMI_FC_VSDIEEEID2 0x1031
+#define HDMI_FC_VSDPAYLOAD0 0x1032
+#define HDMI_FC_VSDPAYLOAD1 0x1033
+#define HDMI_FC_VSDPAYLOAD2 0x1034
+#define HDMI_FC_VSDPAYLOAD3 0x1035
+#define HDMI_FC_VSDPAYLOAD4 0x1036
+#define HDMI_FC_VSDPAYLOAD5 0x1037
+#define HDMI_FC_VSDPAYLOAD6 0x1038
+#define HDMI_FC_VSDPAYLOAD7 0x1039
+#define HDMI_FC_VSDPAYLOAD8 0x103A
+#define HDMI_FC_VSDPAYLOAD9 0x103B
+#define HDMI_FC_VSDPAYLOAD10 0x103C
+#define HDMI_FC_VSDPAYLOAD11 0x103D
+#define HDMI_FC_VSDPAYLOAD12 0x103E
+#define HDMI_FC_VSDPAYLOAD13 0x103F
+#define HDMI_FC_VSDPAYLOAD14 0x1040
+#define HDMI_FC_VSDPAYLOAD15 0x1041
+#define HDMI_FC_VSDPAYLOAD16 0x1042
+#define HDMI_FC_VSDPAYLOAD17 0x1043
+#define HDMI_FC_VSDPAYLOAD18 0x1044
+#define HDMI_FC_VSDPAYLOAD19 0x1045
+#define HDMI_FC_VSDPAYLOAD20 0x1046
+#define HDMI_FC_VSDPAYLOAD21 0x1047
+#define HDMI_FC_VSDPAYLOAD22 0x1048
+#define HDMI_FC_VSDPAYLOAD23 0x1049
+#define HDMI_FC_SPDVENDORNAME0 0x104A
+#define HDMI_FC_SPDVENDORNAME1 0x104B
+#define HDMI_FC_SPDVENDORNAME2 0x104C
+#define HDMI_FC_SPDVENDORNAME3 0x104D
+#define HDMI_FC_SPDVENDORNAME4 0x104E
+#define HDMI_FC_SPDVENDORNAME5 0x104F
+#define HDMI_FC_SPDVENDORNAME6 0x1050
+#define HDMI_FC_SPDVENDORNAME7 0x1051
+#define HDMI_FC_SDPPRODUCTNAME0 0x1052
+#define HDMI_FC_SDPPRODUCTNAME1 0x1053
+#define HDMI_FC_SDPPRODUCTNAME2 0x1054
+#define HDMI_FC_SDPPRODUCTNAME3 0x1055
+#define HDMI_FC_SDPPRODUCTNAME4 0x1056
+#define HDMI_FC_SDPPRODUCTNAME5 0x1057
+#define HDMI_FC_SDPPRODUCTNAME6 0x1058
+#define HDMI_FC_SDPPRODUCTNAME7 0x1059
+#define HDMI_FC_SDPPRODUCTNAME8 0x105A
+#define HDMI_FC_SDPPRODUCTNAME9 0x105B
+#define HDMI_FC_SDPPRODUCTNAME10 0x105C
+#define HDMI_FC_SDPPRODUCTNAME11 0x105D
+#define HDMI_FC_SDPPRODUCTNAME12 0x105E
+#define HDMI_FC_SDPPRODUCTNAME13 0x105F
+#define HDMI_FC_SDPPRODUCTNAME14 0x1060
+#define HDMI_FC_SPDPRODUCTNAME15 0x1061
+#define HDMI_FC_SPDDEVICEINF 0x1062
+#define HDMI_FC_AUDSCONF 0x1063
+#define HDMI_FC_AUDSSTAT 0x1064
+#define HDMI_FC_DATACH0FILL 0x1070
+#define HDMI_FC_DATACH1FILL 0x1071
+#define HDMI_FC_DATACH2FILL 0x1072
+#define HDMI_FC_CTRLQHIGH 0x1073
+#define HDMI_FC_CTRLQLOW 0x1074
+#define HDMI_FC_ACP0 0x1075
+#define HDMI_FC_ACP28 0x1076
+#define HDMI_FC_ACP27 0x1077
+#define HDMI_FC_ACP26 0x1078
+#define HDMI_FC_ACP25 0x1079
+#define HDMI_FC_ACP24 0x107A
+#define HDMI_FC_ACP23 0x107B
+#define HDMI_FC_ACP22 0x107C
+#define HDMI_FC_ACP21 0x107D
+#define HDMI_FC_ACP20 0x107E
+#define HDMI_FC_ACP19 0x107F
+#define HDMI_FC_ACP18 0x1080
+#define HDMI_FC_ACP17 0x1081
+#define HDMI_FC_ACP16 0x1082
+#define HDMI_FC_ACP15 0x1083
+#define HDMI_FC_ACP14 0x1084
+#define HDMI_FC_ACP13 0x1085
+#define HDMI_FC_ACP12 0x1086
+#define HDMI_FC_ACP11 0x1087
+#define HDMI_FC_ACP10 0x1088
+#define HDMI_FC_ACP9 0x1089
+#define HDMI_FC_ACP8 0x108A
+#define HDMI_FC_ACP7 0x108B
+#define HDMI_FC_ACP6 0x108C
+#define HDMI_FC_ACP5 0x108D
+#define HDMI_FC_ACP4 0x108E
+#define HDMI_FC_ACP3 0x108F
+#define HDMI_FC_ACP2 0x1090
+#define HDMI_FC_ACP1 0x1091
+#define HDMI_FC_ISCR1_0 0x1092
+#define HDMI_FC_ISCR1_16 0x1093
+#define HDMI_FC_ISCR1_15 0x1094
+#define HDMI_FC_ISCR1_14 0x1095
+#define HDMI_FC_ISCR1_13 0x1096
+#define HDMI_FC_ISCR1_12 0x1097
+#define HDMI_FC_ISCR1_11 0x1098
+#define HDMI_FC_ISCR1_10 0x1099
+#define HDMI_FC_ISCR1_9 0x109A
+#define HDMI_FC_ISCR1_8 0x109B
+#define HDMI_FC_ISCR1_7 0x109C
+#define HDMI_FC_ISCR1_6 0x109D
+#define HDMI_FC_ISCR1_5 0x109E
+#define HDMI_FC_ISCR1_4 0x109F
+#define HDMI_FC_ISCR1_3 0x10A0
+#define HDMI_FC_ISCR1_2 0x10A1
+#define HDMI_FC_ISCR1_1 0x10A2
+#define HDMI_FC_ISCR2_15 0x10A3
+#define HDMI_FC_ISCR2_14 0x10A4
+#define HDMI_FC_ISCR2_13 0x10A5
+#define HDMI_FC_ISCR2_12 0x10A6
+#define HDMI_FC_ISCR2_11 0x10A7
+#define HDMI_FC_ISCR2_10 0x10A8
+#define HDMI_FC_ISCR2_9 0x10A9
+#define HDMI_FC_ISCR2_8 0x10AA
+#define HDMI_FC_ISCR2_7 0x10AB
+#define HDMI_FC_ISCR2_6 0x10AC
+#define HDMI_FC_ISCR2_5 0x10AD
+#define HDMI_FC_ISCR2_4 0x10AE
+#define HDMI_FC_ISCR2_3 0x10AF
+#define HDMI_FC_ISCR2_2 0x10B0
+#define HDMI_FC_ISCR2_1 0x10B1
+#define HDMI_FC_ISCR2_0 0x10B2
+#define HDMI_FC_DATAUTO0 0x10B3
+#define HDMI_FC_DATAUTO1 0x10B4
+#define HDMI_FC_DATAUTO2 0x10B5
+#define HDMI_FC_DATMAN 0x10B6
+#define HDMI_FC_DATAUTO3 0x10B7
+#define HDMI_FC_RDRB0 0x10B8
+#define HDMI_FC_RDRB1 0x10B9
+#define HDMI_FC_RDRB2 0x10BA
+#define HDMI_FC_RDRB3 0x10BB
+#define HDMI_FC_RDRB4 0x10BC
+#define HDMI_FC_RDRB5 0x10BD
+#define HDMI_FC_RDRB6 0x10BE
+#define HDMI_FC_RDRB7 0x10BF
+#define HDMI_FC_STAT0 0x10D0
+#define HDMI_FC_INT0 0x10D1
+#define HDMI_FC_MASK0 0x10D2
+#define HDMI_FC_POL0 0x10D3
+#define HDMI_FC_STAT1 0x10D4
+#define HDMI_FC_INT1 0x10D5
+#define HDMI_FC_MASK1 0x10D6
+#define HDMI_FC_POL1 0x10D7
+#define HDMI_FC_STAT2 0x10D8
+#define HDMI_FC_INT2 0x10D9
+#define HDMI_FC_MASK2 0x10DA
+#define HDMI_FC_POL2 0x10DB
+#define HDMI_FC_PRCONF 0x10E0
+
+#define HDMI_FC_GMD_STAT 0x1100
+#define HDMI_FC_GMD_EN 0x1101
+#define HDMI_FC_GMD_UP 0x1102
+#define HDMI_FC_GMD_CONF 0x1103
+#define HDMI_FC_GMD_HB 0x1104
+#define HDMI_FC_GMD_PB0 0x1105
+#define HDMI_FC_GMD_PB1 0x1106
+#define HDMI_FC_GMD_PB2 0x1107
+#define HDMI_FC_GMD_PB3 0x1108
+#define HDMI_FC_GMD_PB4 0x1109
+#define HDMI_FC_GMD_PB5 0x110A
+#define HDMI_FC_GMD_PB6 0x110B
+#define HDMI_FC_GMD_PB7 0x110C
+#define HDMI_FC_GMD_PB8 0x110D
+#define HDMI_FC_GMD_PB9 0x110E
+#define HDMI_FC_GMD_PB10 0x110F
+#define HDMI_FC_GMD_PB11 0x1110
+#define HDMI_FC_GMD_PB12 0x1111
+#define HDMI_FC_GMD_PB13 0x1112
+#define HDMI_FC_GMD_PB14 0x1113
+#define HDMI_FC_GMD_PB15 0x1114
+#define HDMI_FC_GMD_PB16 0x1115
+#define HDMI_FC_GMD_PB17 0x1116
+#define HDMI_FC_GMD_PB18 0x1117
+#define HDMI_FC_GMD_PB19 0x1118
+#define HDMI_FC_GMD_PB20 0x1119
+#define HDMI_FC_GMD_PB21 0x111A
+#define HDMI_FC_GMD_PB22 0x111B
+#define HDMI_FC_GMD_PB23 0x111C
+#define HDMI_FC_GMD_PB24 0x111D
+#define HDMI_FC_GMD_PB25 0x111E
+#define HDMI_FC_GMD_PB26 0x111F
+#define HDMI_FC_GMD_PB27 0x1120
+
+#define HDMI_FC_DBGFORCE 0x1200
+#define HDMI_FC_DBGAUD0CH0 0x1201
+#define HDMI_FC_DBGAUD1CH0 0x1202
+#define HDMI_FC_DBGAUD2CH0 0x1203
+#define HDMI_FC_DBGAUD0CH1 0x1204
+#define HDMI_FC_DBGAUD1CH1 0x1205
+#define HDMI_FC_DBGAUD2CH1 0x1206
+#define HDMI_FC_DBGAUD0CH2 0x1207
+#define HDMI_FC_DBGAUD1CH2 0x1208
+#define HDMI_FC_DBGAUD2CH2 0x1209
+#define HDMI_FC_DBGAUD0CH3 0x120A
+#define HDMI_FC_DBGAUD1CH3 0x120B
+#define HDMI_FC_DBGAUD2CH3 0x120C
+#define HDMI_FC_DBGAUD0CH4 0x120D
+#define HDMI_FC_DBGAUD1CH4 0x120E
+#define HDMI_FC_DBGAUD2CH4 0x120F
+#define HDMI_FC_DBGAUD0CH5 0x1210
+#define HDMI_FC_DBGAUD1CH5 0x1211
+#define HDMI_FC_DBGAUD2CH5 0x1212
+#define HDMI_FC_DBGAUD0CH6 0x1213
+#define HDMI_FC_DBGAUD1CH6 0x1214
+#define HDMI_FC_DBGAUD2CH6 0x1215
+#define HDMI_FC_DBGAUD0CH7 0x1216
+#define HDMI_FC_DBGAUD1CH7 0x1217
+#define HDMI_FC_DBGAUD2CH7 0x1218
+#define HDMI_FC_DBGTMDS0 0x1219
+#define HDMI_FC_DBGTMDS1 0x121A
+#define HDMI_FC_DBGTMDS2 0x121B
+
+/* HDMI Source PHY Registers */
+#define HDMI_PHY_CONF0 0x3000
+#define HDMI_PHY_TST0 0x3001
+#define HDMI_PHY_TST1 0x3002
+#define HDMI_PHY_TST2 0x3003
+#define HDMI_PHY_STAT0 0x3004
+#define HDMI_PHY_INT0 0x3005
+#define HDMI_PHY_MASK0 0x3006
+#define HDMI_PHY_POL0 0x3007
+
+/* HDMI Master PHY Registers */
+#define HDMI_PHY_I2CM_SLAVE_ADDR 0x3020
+#define HDMI_PHY_I2CM_ADDRESS_ADDR 0x3021
+#define HDMI_PHY_I2CM_DATAO_1_ADDR 0x3022
+#define HDMI_PHY_I2CM_DATAO_0_ADDR 0x3023
+#define HDMI_PHY_I2CM_DATAI_1_ADDR 0x3024
+#define HDMI_PHY_I2CM_DATAI_0_ADDR 0x3025
+#define HDMI_PHY_I2CM_OPERATION_ADDR 0x3026
+#define HDMI_PHY_I2CM_INT_ADDR 0x3027
+#define HDMI_PHY_I2CM_CTLINT_ADDR 0x3028
+#define HDMI_PHY_I2CM_DIV_ADDR 0x3029
+#define HDMI_PHY_I2CM_SOFTRSTZ_ADDR 0x302a
+#define HDMI_PHY_I2CM_SS_SCL_HCNT_1_ADDR 0x302b
+#define HDMI_PHY_I2CM_SS_SCL_HCNT_0_ADDR 0x302c
+#define HDMI_PHY_I2CM_SS_SCL_LCNT_1_ADDR 0x302d
+#define HDMI_PHY_I2CM_SS_SCL_LCNT_0_ADDR 0x302e
+#define HDMI_PHY_I2CM_FS_SCL_HCNT_1_ADDR 0x302f
+#define HDMI_PHY_I2CM_FS_SCL_HCNT_0_ADDR 0x3030
+#define HDMI_PHY_I2CM_FS_SCL_LCNT_1_ADDR 0x3031
+#define HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR 0x3032
+
+/* Audio Sampler Registers */
+#define HDMI_AUD_CONF0 0x3100
+#define HDMI_AUD_CONF1 0x3101
+#define HDMI_AUD_INT 0x3102
+#define HDMI_AUD_CONF2 0x3103
+#define HDMI_AUD_N1 0x3200
+#define HDMI_AUD_N2 0x3201
+#define HDMI_AUD_N3 0x3202
+#define HDMI_AUD_CTS1 0x3203
+#define HDMI_AUD_CTS2 0x3204
+#define HDMI_AUD_CTS3 0x3205
+#define HDMI_AUD_INPUTCLKFS 0x3206
+#define HDMI_AUD_SPDIFINT 0x3302
+#define HDMI_AUD_CONF0_HBR 0x3400
+#define HDMI_AUD_HBR_STATUS 0x3401
+#define HDMI_AUD_HBR_INT 0x3402
+#define HDMI_AUD_HBR_POL 0x3403
+#define HDMI_AUD_HBR_MASK 0x3404
+
+/*
+ * Generic Parallel Audio Interface Registers
+ * Not used as GPAUD interface is not enabled in hw
+ */
+#define HDMI_GP_CONF0 0x3500
+#define HDMI_GP_CONF1 0x3501
+#define HDMI_GP_CONF2 0x3502
+#define HDMI_GP_STAT 0x3503
+#define HDMI_GP_INT 0x3504
+#define HDMI_GP_MASK 0x3505
+#define HDMI_GP_POL 0x3506
+
+/* Audio DMA Registers */
+#define HDMI_AHB_DMA_CONF0 0x3600
+#define HDMI_AHB_DMA_START 0x3601
+#define HDMI_AHB_DMA_STOP 0x3602
+#define HDMI_AHB_DMA_THRSLD 0x3603
+#define HDMI_AHB_DMA_STRADDR0 0x3604
+#define HDMI_AHB_DMA_STRADDR1 0x3605
+#define HDMI_AHB_DMA_STRADDR2 0x3606
+#define HDMI_AHB_DMA_STRADDR3 0x3607
+#define HDMI_AHB_DMA_STPADDR0 0x3608
+#define HDMI_AHB_DMA_STPADDR1 0x3609
+#define HDMI_AHB_DMA_STPADDR2 0x360a
+#define HDMI_AHB_DMA_STPADDR3 0x360b
+#define HDMI_AHB_DMA_BSTADDR0 0x360c
+#define HDMI_AHB_DMA_BSTADDR1 0x360d
+#define HDMI_AHB_DMA_BSTADDR2 0x360e
+#define HDMI_AHB_DMA_BSTADDR3 0x360f
+#define HDMI_AHB_DMA_MBLENGTH0 0x3610
+#define HDMI_AHB_DMA_MBLENGTH1 0x3611
+#define HDMI_AHB_DMA_STAT 0x3612
+#define HDMI_AHB_DMA_INT 0x3613
+#define HDMI_AHB_DMA_MASK 0x3614
+#define HDMI_AHB_DMA_POL 0x3615
+#define HDMI_AHB_DMA_CONF1 0x3616
+#define HDMI_AHB_DMA_BUFFSTAT 0x3617
+#define HDMI_AHB_DMA_BUFFINT 0x3618
+#define HDMI_AHB_DMA_BUFFMASK 0x3619
+#define HDMI_AHB_DMA_BUFFPOL 0x361a
+
+/* Main Controller Registers */
+#define HDMI_MC_SFRDIV 0x4000
+#define HDMI_MC_CLKDIS 0x4001
+#define HDMI_MC_SWRSTZ 0x4002
+#define HDMI_MC_OPCTRL 0x4003
+#define HDMI_MC_FLOWCTRL 0x4004
+#define HDMI_MC_PHYRSTZ 0x4005
+#define HDMI_MC_LOCKONCLOCK 0x4006
+#define HDMI_MC_HEACPHY_RST 0x4007
+
+/* Color Space Converter Registers */
+#define HDMI_CSC_CFG 0x4100
+#define HDMI_CSC_SCALE 0x4101
+#define HDMI_CSC_COEF_A1_MSB 0x4102
+#define HDMI_CSC_COEF_A1_LSB 0x4103
+#define HDMI_CSC_COEF_A2_MSB 0x4104
+#define HDMI_CSC_COEF_A2_LSB 0x4105
+#define HDMI_CSC_COEF_A3_MSB 0x4106
+#define HDMI_CSC_COEF_A3_LSB 0x4107
+#define HDMI_CSC_COEF_A4_MSB 0x4108
+#define HDMI_CSC_COEF_A4_LSB 0x4109
+#define HDMI_CSC_COEF_B1_MSB 0x410A
+#define HDMI_CSC_COEF_B1_LSB 0x410B
+#define HDMI_CSC_COEF_B2_MSB 0x410C
+#define HDMI_CSC_COEF_B2_LSB 0x410D
+#define HDMI_CSC_COEF_B3_MSB 0x410E
+#define HDMI_CSC_COEF_B3_LSB 0x410F
+#define HDMI_CSC_COEF_B4_MSB 0x4110
+#define HDMI_CSC_COEF_B4_LSB 0x4111
+#define HDMI_CSC_COEF_C1_MSB 0x4112
+#define HDMI_CSC_COEF_C1_LSB 0x4113
+#define HDMI_CSC_COEF_C2_MSB 0x4114
+#define HDMI_CSC_COEF_C2_LSB 0x4115
+#define HDMI_CSC_COEF_C3_MSB 0x4116
+#define HDMI_CSC_COEF_C3_LSB 0x4117
+#define HDMI_CSC_COEF_C4_MSB 0x4118
+#define HDMI_CSC_COEF_C4_LSB 0x4119
+
+/* HDCP Encryption Engine Registers */
+#define HDMI_A_HDCPCFG0 0x5000
+#define HDMI_A_HDCPCFG1 0x5001
+#define HDMI_A_HDCPOBS0 0x5002
+#define HDMI_A_HDCPOBS1 0x5003
+#define HDMI_A_HDCPOBS2 0x5004
+#define HDMI_A_HDCPOBS3 0x5005
+#define HDMI_A_APIINTCLR 0x5006
+#define HDMI_A_APIINTSTAT 0x5007
+#define HDMI_A_APIINTMSK 0x5008
+#define HDMI_A_VIDPOLCFG 0x5009
+#define HDMI_A_OESSWCFG 0x500A
+#define HDMI_A_TIMER1SETUP0 0x500B
+#define HDMI_A_TIMER1SETUP1 0x500C
+#define HDMI_A_TIMER2SETUP0 0x500D
+#define HDMI_A_TIMER2SETUP1 0x500E
+#define HDMI_A_100MSCFG 0x500F
+#define HDMI_A_2SCFG0 0x5010
+#define HDMI_A_2SCFG1 0x5011
+#define HDMI_A_5SCFG0 0x5012
+#define HDMI_A_5SCFG1 0x5013
+#define HDMI_A_SRMVERLSB 0x5014
+#define HDMI_A_SRMVERMSB 0x5015
+#define HDMI_A_SRMCTRL 0x5016
+#define HDMI_A_SFRSETUP 0x5017
+#define HDMI_A_I2CHSETUP 0x5018
+#define HDMI_A_INTSETUP 0x5019
+#define HDMI_A_PRESETUP 0x501A
+#define HDMI_A_SRM_BASE 0x5020
+
+/* CEC Engine Registers */
+#define HDMI_CEC_CTRL 0x7D00
+#define HDMI_CEC_STAT 0x7D01
+#define HDMI_CEC_MASK 0x7D02
+#define HDMI_CEC_POLARITY 0x7D03
+#define HDMI_CEC_INT 0x7D04
+#define HDMI_CEC_ADDR_L 0x7D05
+#define HDMI_CEC_ADDR_H 0x7D06
+#define HDMI_CEC_TX_CNT 0x7D07
+#define HDMI_CEC_RX_CNT 0x7D08
+#define HDMI_CEC_TX_DATA0 0x7D10
+#define HDMI_CEC_TX_DATA1 0x7D11
+#define HDMI_CEC_TX_DATA2 0x7D12
+#define HDMI_CEC_TX_DATA3 0x7D13
+#define HDMI_CEC_TX_DATA4 0x7D14
+#define HDMI_CEC_TX_DATA5 0x7D15
+#define HDMI_CEC_TX_DATA6 0x7D16
+#define HDMI_CEC_TX_DATA7 0x7D17
+#define HDMI_CEC_TX_DATA8 0x7D18
+#define HDMI_CEC_TX_DATA9 0x7D19
+#define HDMI_CEC_TX_DATA10 0x7D1a
+#define HDMI_CEC_TX_DATA11 0x7D1b
+#define HDMI_CEC_TX_DATA12 0x7D1c
+#define HDMI_CEC_TX_DATA13 0x7D1d
+#define HDMI_CEC_TX_DATA14 0x7D1e
+#define HDMI_CEC_TX_DATA15 0x7D1f
+#define HDMI_CEC_RX_DATA0 0x7D20
+#define HDMI_CEC_RX_DATA1 0x7D21
+#define HDMI_CEC_RX_DATA2 0x7D22
+#define HDMI_CEC_RX_DATA3 0x7D23
+#define HDMI_CEC_RX_DATA4 0x7D24
+#define HDMI_CEC_RX_DATA5 0x7D25
+#define HDMI_CEC_RX_DATA6 0x7D26
+#define HDMI_CEC_RX_DATA7 0x7D27
+#define HDMI_CEC_RX_DATA8 0x7D28
+#define HDMI_CEC_RX_DATA9 0x7D29
+#define HDMI_CEC_RX_DATA10 0x7D2a
+#define HDMI_CEC_RX_DATA11 0x7D2b
+#define HDMI_CEC_RX_DATA12 0x7D2c
+#define HDMI_CEC_RX_DATA13 0x7D2d
+#define HDMI_CEC_RX_DATA14 0x7D2e
+#define HDMI_CEC_RX_DATA15 0x7D2f
+#define HDMI_CEC_LOCK 0x7D30
+#define HDMI_CEC_WKUPCTRL 0x7D31
+
+/* I2C Master Registers (E-DDC) */
+#define HDMI_I2CM_SLAVE 0x7E00
+#define HDMI_I2CMESS 0x7E01
+#define HDMI_I2CM_DATAO 0x7E02
+#define HDMI_I2CM_DATAI 0x7E03
+#define HDMI_I2CM_OPERATION 0x7E04
+#define HDMI_I2CM_INT 0x7E05
+#define HDMI_I2CM_CTLINT 0x7E06
+#define HDMI_I2CM_DIV 0x7E07
+#define HDMI_I2CM_SEGADDR 0x7E08
+#define HDMI_I2CM_SOFTRSTZ 0x7E09
+#define HDMI_I2CM_SEGPTR 0x7E0A
+#define HDMI_I2CM_SS_SCL_HCNT_1_ADDR 0x7E0B
+#define HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x7E0C
+#define HDMI_I2CM_SS_SCL_LCNT_1_ADDR 0x7E0D
+#define HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0x7E0E
+#define HDMI_I2CM_FS_SCL_HCNT_1_ADDR 0x7E0F
+#define HDMI_I2CM_FS_SCL_HCNT_0_ADDR 0x7E10
+#define HDMI_I2CM_FS_SCL_LCNT_1_ADDR 0x7E11
+#define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12
+
+enum {
+/* IH_FC_INT2 field values */
+ HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
+ HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_IH_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* IH_FC_STAT2 field values */
+ HDMI_IH_FC_STAT2_OVERFLOW_MASK = 0x03,
+ HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* IH_PHY_STAT0 field values */
+ HDMI_IH_PHY_STAT0_RX_SENSE3 = 0x20,
+ HDMI_IH_PHY_STAT0_RX_SENSE2 = 0x10,
+ HDMI_IH_PHY_STAT0_RX_SENSE1 = 0x8,
+ HDMI_IH_PHY_STAT0_RX_SENSE0 = 0x4,
+ HDMI_IH_PHY_STAT0_TX_PHY_LOCK = 0x2,
+ HDMI_IH_PHY_STAT0_HPD = 0x1,
+
+/* IH_MUTE_I2CMPHY_STAT0 field values */
+ HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYDONE = 0x2,
+ HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYERROR = 0x1,
+
+/* IH_AHBDMAAUD_STAT0 field values */
+ HDMI_IH_AHBDMAAUD_STAT0_ERROR = 0x20,
+ HDMI_IH_AHBDMAAUD_STAT0_LOST = 0x10,
+ HDMI_IH_AHBDMAAUD_STAT0_RETRY = 0x08,
+ HDMI_IH_AHBDMAAUD_STAT0_DONE = 0x04,
+ HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = 0x02,
+ HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01,
+
+/* IH_MUTE_FC_STAT2 field values */
+ HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK = 0x03,
+ HDMI_IH_MUTE_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_IH_MUTE_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* IH_MUTE_AHBDMAAUD_STAT0 field values */
+ HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = 0x20,
+ HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = 0x10,
+ HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = 0x08,
+ HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = 0x04,
+ HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = 0x02,
+ HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01,
+
+/* IH_MUTE field values */
+ HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT = 0x2,
+ HDMI_IH_MUTE_MUTE_ALL_INTERRUPT = 0x1,
+
+/* TX_INVID0 field values */
+ HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_MASK = 0x80,
+ HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_ENABLE = 0x80,
+ HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE = 0x00,
+ HDMI_TX_INVID0_VIDEO_MAPPING_MASK = 0x1F,
+ HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET = 0,
+
+/* TX_INSTUFFING field values */
+ HDMI_TX_INSTUFFING_BDBDATA_STUFFING_MASK = 0x4,
+ HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE = 0x4,
+ HDMI_TX_INSTUFFING_BDBDATA_STUFFING_DISABLE = 0x0,
+ HDMI_TX_INSTUFFING_RCRDATA_STUFFING_MASK = 0x2,
+ HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE = 0x2,
+ HDMI_TX_INSTUFFING_RCRDATA_STUFFING_DISABLE = 0x0,
+ HDMI_TX_INSTUFFING_GYDATA_STUFFING_MASK = 0x1,
+ HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE = 0x1,
+ HDMI_TX_INSTUFFING_GYDATA_STUFFING_DISABLE = 0x0,
+
+/* VP_PR_CD field values */
+ HDMI_VP_PR_CD_COLOR_DEPTH_MASK = 0xF0,
+ HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET = 4,
+ HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK = 0x0F,
+ HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET = 0,
+
+/* VP_STUFF field values */
+ HDMI_VP_STUFF_IDEFAULT_PHASE_MASK = 0x20,
+ HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET = 5,
+ HDMI_VP_STUFF_IFIX_PP_TO_LAST_MASK = 0x10,
+ HDMI_VP_STUFF_IFIX_PP_TO_LAST_OFFSET = 4,
+ HDMI_VP_STUFF_ICX_GOTO_P0_ST_MASK = 0x8,
+ HDMI_VP_STUFF_ICX_GOTO_P0_ST_OFFSET = 3,
+ HDMI_VP_STUFF_YCC422_STUFFING_MASK = 0x4,
+ HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE = 0x4,
+ HDMI_VP_STUFF_YCC422_STUFFING_DIRECT_MODE = 0x0,
+ HDMI_VP_STUFF_PP_STUFFING_MASK = 0x2,
+ HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE = 0x2,
+ HDMI_VP_STUFF_PP_STUFFING_DIRECT_MODE = 0x0,
+ HDMI_VP_STUFF_PR_STUFFING_MASK = 0x1,
+ HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE = 0x1,
+ HDMI_VP_STUFF_PR_STUFFING_DIRECT_MODE = 0x0,
+
+/* VP_CONF field values */
+ HDMI_VP_CONF_BYPASS_EN_MASK = 0x40,
+ HDMI_VP_CONF_BYPASS_EN_ENABLE = 0x40,
+ HDMI_VP_CONF_BYPASS_EN_DISABLE = 0x00,
+ HDMI_VP_CONF_PP_EN_ENMASK = 0x20,
+ HDMI_VP_CONF_PP_EN_ENABLE = 0x20,
+ HDMI_VP_CONF_PP_EN_DISABLE = 0x00,
+ HDMI_VP_CONF_PR_EN_MASK = 0x10,
+ HDMI_VP_CONF_PR_EN_ENABLE = 0x10,
+ HDMI_VP_CONF_PR_EN_DISABLE = 0x00,
+ HDMI_VP_CONF_YCC422_EN_MASK = 0x8,
+ HDMI_VP_CONF_YCC422_EN_ENABLE = 0x8,
+ HDMI_VP_CONF_YCC422_EN_DISABLE = 0x0,
+ HDMI_VP_CONF_BYPASS_SELECT_MASK = 0x4,
+ HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER = 0x4,
+ HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER = 0x0,
+ HDMI_VP_CONF_OUTPUT_SELECTOR_MASK = 0x3,
+ HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS = 0x3,
+ HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422 = 0x1,
+ HDMI_VP_CONF_OUTPUT_SELECTOR_PP = 0x0,
+
+/* VP_REMAP field values */
+ HDMI_VP_REMAP_MASK = 0x3,
+ HDMI_VP_REMAP_YCC422_24bit = 0x2,
+ HDMI_VP_REMAP_YCC422_20bit = 0x1,
+ HDMI_VP_REMAP_YCC422_16bit = 0x0,
+
+/* FC_INVIDCONF field values */
+ HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK = 0x80,
+ HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE = 0x80,
+ HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE = 0x00,
+ HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_MASK = 0x40,
+ HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH = 0x40,
+ HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW = 0x00,
+ HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_MASK = 0x20,
+ HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH = 0x20,
+ HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW = 0x00,
+ HDMI_FC_INVIDCONF_DE_IN_POLARITY_MASK = 0x10,
+ HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH = 0x10,
+ HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW = 0x00,
+ HDMI_FC_INVIDCONF_DVI_MODEZ_MASK = 0x8,
+ HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE = 0x8,
+ HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE = 0x0,
+ HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_MASK = 0x2,
+ HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH = 0x2,
+ HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW = 0x0,
+ HDMI_FC_INVIDCONF_IN_I_P_MASK = 0x1,
+ HDMI_FC_INVIDCONF_IN_I_P_INTERLACED = 0x1,
+ HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE = 0x0,
+
+/* FC_AUDICONF0 field values */
+ HDMI_FC_AUDICONF0_CC_OFFSET = 4,
+ HDMI_FC_AUDICONF0_CC_MASK = 0x70,
+ HDMI_FC_AUDICONF0_CT_OFFSET = 0,
+ HDMI_FC_AUDICONF0_CT_MASK = 0xF,
+
+/* FC_AUDICONF1 field values */
+ HDMI_FC_AUDICONF1_SS_OFFSET = 3,
+ HDMI_FC_AUDICONF1_SS_MASK = 0x18,
+ HDMI_FC_AUDICONF1_SF_OFFSET = 0,
+ HDMI_FC_AUDICONF1_SF_MASK = 0x7,
+
+/* FC_AUDICONF3 field values */
+ HDMI_FC_AUDICONF3_LFEPBL_OFFSET = 5,
+ HDMI_FC_AUDICONF3_LFEPBL_MASK = 0x60,
+ HDMI_FC_AUDICONF3_DM_INH_OFFSET = 4,
+ HDMI_FC_AUDICONF3_DM_INH_MASK = 0x10,
+ HDMI_FC_AUDICONF3_LSV_OFFSET = 0,
+ HDMI_FC_AUDICONF3_LSV_MASK = 0xF,
+
+/* FC_AUDSCHNLS0 field values */
+ HDMI_FC_AUDSCHNLS0_CGMSA_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS0_CGMSA_MASK = 0x30,
+ HDMI_FC_AUDSCHNLS0_COPYRIGHT_OFFSET = 0,
+ HDMI_FC_AUDSCHNLS0_COPYRIGHT_MASK = 0x01,
+
+/* FC_AUDSCHNLS3-6 field values */
+ HDMI_FC_AUDSCHNLS3_OIEC_CH0_OFFSET = 0,
+ HDMI_FC_AUDSCHNLS3_OIEC_CH0_MASK = 0x0f,
+ HDMI_FC_AUDSCHNLS3_OIEC_CH1_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS3_OIEC_CH1_MASK = 0xf0,
+ HDMI_FC_AUDSCHNLS4_OIEC_CH2_OFFSET = 0,
+ HDMI_FC_AUDSCHNLS4_OIEC_CH2_MASK = 0x0f,
+ HDMI_FC_AUDSCHNLS4_OIEC_CH3_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS4_OIEC_CH3_MASK = 0xf0,
+
+ HDMI_FC_AUDSCHNLS5_OIEC_CH0_OFFSET = 0,
+ HDMI_FC_AUDSCHNLS5_OIEC_CH0_MASK = 0x0f,
+ HDMI_FC_AUDSCHNLS5_OIEC_CH1_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS5_OIEC_CH1_MASK = 0xf0,
+ HDMI_FC_AUDSCHNLS6_OIEC_CH2_OFFSET = 0,
+ HDMI_FC_AUDSCHNLS6_OIEC_CH2_MASK = 0x0f,
+ HDMI_FC_AUDSCHNLS6_OIEC_CH3_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS6_OIEC_CH3_MASK = 0xf0,
+
+/* HDMI_FC_AUDSCHNLS7 field values */
+ HDMI_FC_AUDSCHNLS7_ACCURACY_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS7_ACCURACY_MASK = 0x30,
+
+/* HDMI_FC_AUDSCHNLS8 field values */
+ HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_MASK = 0xf0,
+ HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_OFFSET = 4,
+ HDMI_FC_AUDSCHNLS8_WORDLEGNTH_MASK = 0x0f,
+ HDMI_FC_AUDSCHNLS8_WORDLEGNTH_OFFSET = 0,
+
+/* FC_AUDSCONF field values */
+ HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_MASK = 0xF0,
+ HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_OFFSET = 4,
+ HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK = 0x1,
+ HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_OFFSET = 0,
+ HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1 = 0x1,
+ HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0 = 0x0,
+
+/* FC_STAT2 field values */
+ HDMI_FC_STAT2_OVERFLOW_MASK = 0x03,
+ HDMI_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* FC_INT2 field values */
+ HDMI_FC_INT2_OVERFLOW_MASK = 0x03,
+ HDMI_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* FC_MASK2 field values */
+ HDMI_FC_MASK2_OVERFLOW_MASK = 0x03,
+ HDMI_FC_MASK2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_FC_MASK2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* FC_PRCONF field values */
+ HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK = 0xF0,
+ HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET = 4,
+ HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK = 0x0F,
+ HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET = 0,
+
+/* FC_AVICONF0-FC_AVICONF3 field values */
+ HDMI_FC_AVICONF0_PIX_FMT_MASK = 0x03,
+ HDMI_FC_AVICONF0_PIX_FMT_RGB = 0x00,
+ HDMI_FC_AVICONF0_PIX_FMT_YCBCR422 = 0x01,
+ HDMI_FC_AVICONF0_PIX_FMT_YCBCR444 = 0x02,
+ HDMI_FC_AVICONF0_ACTIVE_FMT_MASK = 0x40,
+ HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT = 0x40,
+ HDMI_FC_AVICONF0_ACTIVE_FMT_NO_INFO = 0x00,
+ HDMI_FC_AVICONF0_BAR_DATA_MASK = 0x0C,
+ HDMI_FC_AVICONF0_BAR_DATA_NO_DATA = 0x00,
+ HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR = 0x04,
+ HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR = 0x08,
+ HDMI_FC_AVICONF0_BAR_DATA_VERT_HORIZ_BAR = 0x0C,
+ HDMI_FC_AVICONF0_SCAN_INFO_MASK = 0x30,
+ HDMI_FC_AVICONF0_SCAN_INFO_OVERSCAN = 0x10,
+ HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN = 0x20,
+ HDMI_FC_AVICONF0_SCAN_INFO_NODATA = 0x00,
+
+ HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_MASK = 0x0F,
+ HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_USE_CODED = 0x08,
+ HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3 = 0x09,
+ HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9 = 0x0A,
+ HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_14_9 = 0x0B,
+ HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_MASK = 0x30,
+ HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_NO_DATA = 0x00,
+ HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3 = 0x10,
+ HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9 = 0x20,
+ HDMI_FC_AVICONF1_COLORIMETRY_MASK = 0xC0,
+ HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA = 0x00,
+ HDMI_FC_AVICONF1_COLORIMETRY_SMPTE = 0x40,
+ HDMI_FC_AVICONF1_COLORIMETRY_ITUR = 0x80,
+ HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO = 0xC0,
+
+ HDMI_FC_AVICONF2_SCALING_MASK = 0x03,
+ HDMI_FC_AVICONF2_SCALING_NONE = 0x00,
+ HDMI_FC_AVICONF2_SCALING_HORIZ = 0x01,
+ HDMI_FC_AVICONF2_SCALING_VERT = 0x02,
+ HDMI_FC_AVICONF2_SCALING_HORIZ_VERT = 0x03,
+ HDMI_FC_AVICONF2_RGB_QUANT_MASK = 0x0C,
+ HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT = 0x00,
+ HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE = 0x04,
+ HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE = 0x08,
+ HDMI_FC_AVICONF2_EXT_COLORIMETRY_MASK = 0x70,
+ HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601 = 0x00,
+ HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709 = 0x10,
+ HDMI_FC_AVICONF2_EXT_COLORIMETRY_SYCC601 = 0x20,
+ HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_YCC601 = 0x30,
+ HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_RGB = 0x40,
+ HDMI_FC_AVICONF2_IT_CONTENT_MASK = 0x80,
+ HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA = 0x00,
+ HDMI_FC_AVICONF2_IT_CONTENT_VALID = 0x80,
+
+ HDMI_FC_AVICONF3_IT_CONTENT_TYPE_MASK = 0x03,
+ HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS = 0x00,
+ HDMI_FC_AVICONF3_IT_CONTENT_TYPE_PHOTO = 0x01,
+ HDMI_FC_AVICONF3_IT_CONTENT_TYPE_CINEMA = 0x02,
+ HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GAME = 0x03,
+ HDMI_FC_AVICONF3_QUANT_RANGE_MASK = 0x0C,
+ HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00,
+ HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04,
+
+/* FC_DBGFORCE field values */
+ HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10,
+ HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1,
+
+/* PHY_CONF0 field values */
+ HDMI_PHY_CONF0_PDZ_MASK = 0x80,
+ HDMI_PHY_CONF0_PDZ_OFFSET = 7,
+ HDMI_PHY_CONF0_ENTMDS_MASK = 0x40,
+ HDMI_PHY_CONF0_ENTMDS_OFFSET = 6,
+ HDMI_PHY_CONF0_SPARECTRL = 0x20,
+ HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10,
+ HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4,
+ HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8,
+ HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET = 3,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK = 0x4,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET = 2,
+ HDMI_PHY_CONF0_SELDATAENPOL_MASK = 0x2,
+ HDMI_PHY_CONF0_SELDATAENPOL_OFFSET = 1,
+ HDMI_PHY_CONF0_SELDIPIF_MASK = 0x1,
+ HDMI_PHY_CONF0_SELDIPIF_OFFSET = 0,
+
+/* PHY_TST0 field values */
+ HDMI_PHY_TST0_TSTCLR_MASK = 0x20,
+ HDMI_PHY_TST0_TSTCLR_OFFSET = 5,
+ HDMI_PHY_TST0_TSTEN_MASK = 0x10,
+ HDMI_PHY_TST0_TSTEN_OFFSET = 4,
+ HDMI_PHY_TST0_TSTCLK_MASK = 0x1,
+ HDMI_PHY_TST0_TSTCLK_OFFSET = 0,
+
+/* PHY_STAT0 field values */
+ HDMI_PHY_RX_SENSE3 = 0x80,
+ HDMI_PHY_RX_SENSE2 = 0x40,
+ HDMI_PHY_RX_SENSE1 = 0x20,
+ HDMI_PHY_RX_SENSE0 = 0x10,
+ HDMI_PHY_HPD = 0x02,
+ HDMI_PHY_TX_PHY_LOCK = 0x01,
+
+/* PHY_I2CM_SLAVE_ADDR field values */
+ HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 = 0x69,
+ HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY = 0x49,
+
+/* PHY_I2CM_OPERATION_ADDR field values */
+ HDMI_PHY_I2CM_OPERATION_ADDR_WRITE = 0x10,
+ HDMI_PHY_I2CM_OPERATION_ADDR_READ = 0x1,
+
+/* HDMI_PHY_I2CM_INT_ADDR */
+ HDMI_PHY_I2CM_INT_ADDR_DONE_POL = 0x08,
+ HDMI_PHY_I2CM_INT_ADDR_DONE_MASK = 0x04,
+
+/* HDMI_PHY_I2CM_CTLINT_ADDR */
+ HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL = 0x80,
+ HDMI_PHY_I2CM_CTLINT_ADDR_NAC_MASK = 0x40,
+ HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08,
+ HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04,
+
+/* AUD_CTS3 field values */
+ HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5,
+ HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0,
+ HDMI_AUD_CTS3_N_SHIFT_1 = 0,
+ HDMI_AUD_CTS3_N_SHIFT_16 = 0x20,
+ HDMI_AUD_CTS3_N_SHIFT_32 = 0x40,
+ HDMI_AUD_CTS3_N_SHIFT_64 = 0x60,
+ HDMI_AUD_CTS3_N_SHIFT_128 = 0x80,
+ HDMI_AUD_CTS3_N_SHIFT_256 = 0xa0,
+ /* note that the CTS3 MANUAL bit has been removed
+ from our part. Can't set it, will read as 0. */
+ HDMI_AUD_CTS3_CTS_MANUAL = 0x10,
+ HDMI_AUD_CTS3_AUDCTS19_16_MASK = 0x0f,
+
+/* AHB_DMA_CONF0 field values */
+ HDMI_AHB_DMA_CONF0_SW_FIFO_RST_OFFSET = 7,
+ HDMI_AHB_DMA_CONF0_SW_FIFO_RST_MASK = 0x80,
+ HDMI_AHB_DMA_CONF0_HBR = 0x10,
+ HDMI_AHB_DMA_CONF0_EN_HLOCK_OFFSET = 3,
+ HDMI_AHB_DMA_CONF0_EN_HLOCK_MASK = 0x08,
+ HDMI_AHB_DMA_CONF0_INCR_TYPE_OFFSET = 1,
+ HDMI_AHB_DMA_CONF0_INCR_TYPE_MASK = 0x06,
+ HDMI_AHB_DMA_CONF0_INCR4 = 0x0,
+ HDMI_AHB_DMA_CONF0_INCR8 = 0x2,
+ HDMI_AHB_DMA_CONF0_INCR16 = 0x4,
+ HDMI_AHB_DMA_CONF0_BURST_MODE = 0x1,
+
+/* HDMI_AHB_DMA_START field values */
+ HDMI_AHB_DMA_START_START_OFFSET = 0,
+ HDMI_AHB_DMA_START_START_MASK = 0x01,
+
+/* HDMI_AHB_DMA_STOP field values */
+ HDMI_AHB_DMA_STOP_STOP_OFFSET = 0,
+ HDMI_AHB_DMA_STOP_STOP_MASK = 0x01,
+
+/* AHB_DMA_STAT, AHB_DMA_INT, AHB_DMA_MASK, AHB_DMA_POL field values */
+ HDMI_AHB_DMA_DONE = 0x80,
+ HDMI_AHB_DMA_RETRY_SPLIT = 0x40,
+ HDMI_AHB_DMA_LOSTOWNERSHIP = 0x20,
+ HDMI_AHB_DMA_ERROR = 0x10,
+ HDMI_AHB_DMA_FIFO_THREMPTY = 0x04,
+ HDMI_AHB_DMA_FIFO_FULL = 0x02,
+ HDMI_AHB_DMA_FIFO_EMPTY = 0x01,
+
+/* AHB_DMA_BUFFSTAT, AHB_DMA_BUFFINT,AHB_DMA_BUFFMASK,AHB_DMA_BUFFPOL values */
+ HDMI_AHB_DMA_BUFFSTAT_FULL = 0x02,
+ HDMI_AHB_DMA_BUFFSTAT_EMPTY = 0x01,
+
+/* MC_CLKDIS field values */
+ HDMI_MC_CLKDIS_HDCPCLK_DISABLE = 0x40,
+ HDMI_MC_CLKDIS_CECCLK_DISABLE = 0x20,
+ HDMI_MC_CLKDIS_CSCCLK_DISABLE = 0x10,
+ HDMI_MC_CLKDIS_AUDCLK_DISABLE = 0x8,
+ HDMI_MC_CLKDIS_PREPCLK_DISABLE = 0x4,
+ HDMI_MC_CLKDIS_TMDSCLK_DISABLE = 0x2,
+ HDMI_MC_CLKDIS_PIXELCLK_DISABLE = 0x1,
+
+/* MC_SWRSTZ field values */
+ HDMI_MC_SWRSTZ_TMDSSWRST_REQ = 0x02,
+
+/* MC_FLOWCTRL field values */
+ HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK = 0x1,
+ HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH = 0x1,
+ HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0,
+
+/* MC_PHYRSTZ field values */
+ HDMI_MC_PHYRSTZ_ASSERT = 0x0,
+ HDMI_MC_PHYRSTZ_DEASSERT = 0x1,
+
+/* MC_HEACPHY_RST field values */
+ HDMI_MC_HEACPHY_RST_ASSERT = 0x1,
+ HDMI_MC_HEACPHY_RST_DEASSERT = 0x0,
+
+/* CSC_CFG field values */
+ HDMI_CSC_CFG_INTMODE_MASK = 0x30,
+ HDMI_CSC_CFG_INTMODE_OFFSET = 4,
+ HDMI_CSC_CFG_INTMODE_DISABLE = 0x00,
+ HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1 = 0x10,
+ HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA2 = 0x20,
+ HDMI_CSC_CFG_DECMODE_MASK = 0x3,
+ HDMI_CSC_CFG_DECMODE_OFFSET = 0,
+ HDMI_CSC_CFG_DECMODE_DISABLE = 0x0,
+ HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1 = 0x1,
+ HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA2 = 0x2,
+ HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3 = 0x3,
+
+/* CSC_SCALE field values */
+ HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK = 0xF0,
+ HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP = 0x00,
+ HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP = 0x50,
+ HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP = 0x60,
+ HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP = 0x70,
+ HDMI_CSC_SCALE_CSCSCALE_MASK = 0x03,
+
+/* A_HDCPCFG0 field values */
+ HDMI_A_HDCPCFG0_ELVENA_MASK = 0x80,
+ HDMI_A_HDCPCFG0_ELVENA_ENABLE = 0x80,
+ HDMI_A_HDCPCFG0_ELVENA_DISABLE = 0x00,
+ HDMI_A_HDCPCFG0_I2CFASTMODE_MASK = 0x40,
+ HDMI_A_HDCPCFG0_I2CFASTMODE_ENABLE = 0x40,
+ HDMI_A_HDCPCFG0_I2CFASTMODE_DISABLE = 0x00,
+ HDMI_A_HDCPCFG0_BYPENCRYPTION_MASK = 0x20,
+ HDMI_A_HDCPCFG0_BYPENCRYPTION_ENABLE = 0x20,
+ HDMI_A_HDCPCFG0_BYPENCRYPTION_DISABLE = 0x00,
+ HDMI_A_HDCPCFG0_SYNCRICHECK_MASK = 0x10,
+ HDMI_A_HDCPCFG0_SYNCRICHECK_ENABLE = 0x10,
+ HDMI_A_HDCPCFG0_SYNCRICHECK_DISABLE = 0x00,
+ HDMI_A_HDCPCFG0_AVMUTE_MASK = 0x8,
+ HDMI_A_HDCPCFG0_AVMUTE_ENABLE = 0x8,
+ HDMI_A_HDCPCFG0_AVMUTE_DISABLE = 0x0,
+ HDMI_A_HDCPCFG0_RXDETECT_MASK = 0x4,
+ HDMI_A_HDCPCFG0_RXDETECT_ENABLE = 0x4,
+ HDMI_A_HDCPCFG0_RXDETECT_DISABLE = 0x0,
+ HDMI_A_HDCPCFG0_EN11FEATURE_MASK = 0x2,
+ HDMI_A_HDCPCFG0_EN11FEATURE_ENABLE = 0x2,
+ HDMI_A_HDCPCFG0_EN11FEATURE_DISABLE = 0x0,
+ HDMI_A_HDCPCFG0_HDMIDVI_MASK = 0x1,
+ HDMI_A_HDCPCFG0_HDMIDVI_HDMI = 0x1,
+ HDMI_A_HDCPCFG0_HDMIDVI_DVI = 0x0,
+
+/* A_HDCPCFG1 field values */
+ HDMI_A_HDCPCFG1_DISSHA1CHECK_MASK = 0x8,
+ HDMI_A_HDCPCFG1_DISSHA1CHECK_DISABLE = 0x8,
+ HDMI_A_HDCPCFG1_DISSHA1CHECK_ENABLE = 0x0,
+ HDMI_A_HDCPCFG1_PH2UPSHFTENC_MASK = 0x4,
+ HDMI_A_HDCPCFG1_PH2UPSHFTENC_ENABLE = 0x4,
+ HDMI_A_HDCPCFG1_PH2UPSHFTENC_DISABLE = 0x0,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK = 0x2,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE = 0x2,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_ENABLE = 0x0,
+ HDMI_A_HDCPCFG1_SWRESET_MASK = 0x1,
+ HDMI_A_HDCPCFG1_SWRESET_ASSERT = 0x0,
+
+/* A_VIDPOLCFG field values */
+ HDMI_A_VIDPOLCFG_UNENCRYPTCONF_MASK = 0x60,
+ HDMI_A_VIDPOLCFG_UNENCRYPTCONF_OFFSET = 5,
+ HDMI_A_VIDPOLCFG_DATAENPOL_MASK = 0x10,
+ HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH = 0x10,
+ HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW = 0x0,
+ HDMI_A_VIDPOLCFG_VSYNCPOL_MASK = 0x8,
+ HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH = 0x8,
+ HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW = 0x0,
+ HDMI_A_VIDPOLCFG_HSYNCPOL_MASK = 0x2,
+ HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH = 0x2,
+ HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0,
+};
+#endif /* __IMX_HDMI_H__ */
diff --git a/drivers/video/imx-ipu-v3/imx-ipu-v3.h b/drivers/video/imx-ipu-v3/imx-ipu-v3.h
new file mode 100644
index 0000000000..7c48a7ce38
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/imx-ipu-v3.h
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef __DRM_IPU_H__
+#define __DRM_IPU_H__
+
+#include <io.h>
+#include <fb.h>
+#include <video/fourcc.h>
+
+struct ipu_soc;
+
+enum ipuv3_type {
+ IPUV3EX,
+ IPUV3M,
+ IPUV3H,
+};
+
+void ipuwritel(const char *unit, uint32_t value, void __iomem *reg);
+uint32_t ipureadl(void __iomem *reg);
+
+/*
+ * Bitfield of Display Interface signal polarities.
+ */
+struct ipu_di_signal_cfg {
+ unsigned datamask_en:1;
+ unsigned interlaced:1;
+ unsigned odd_field_first:1;
+ unsigned clksel_en:1;
+ unsigned clkidle_en:1;
+ unsigned data_pol:1; /* true = inverted */
+ unsigned clk_pol:1; /* true = rising edge */
+ unsigned enable_pol:1;
+ unsigned Hsync_pol:1; /* true = active high */
+ unsigned Vsync_pol:1;
+
+ u16 width;
+ u16 height;
+ u32 pixel_fmt;
+ u16 h_start_width;
+ u16 h_sync_width;
+ u16 h_end_width;
+ u16 v_start_width;
+ u16 v_sync_width;
+ u16 v_end_width;
+ u32 v_to_h_sync;
+ unsigned long pixelclock;
+#define IPU_DI_CLKMODE_SYNC (1 << 0)
+#define IPU_DI_CLKMODE_EXT (1 << 1)
+#define IPU_DI_CLKMODE_NON_FRACTIONAL (1 << 2)
+ unsigned long clkflags;
+
+ u8 hsync_pin;
+ u8 vsync_pin;
+};
+
+enum ipu_color_space {
+ IPUV3_COLORSPACE_RGB,
+ IPUV3_COLORSPACE_YUV,
+ IPUV3_COLORSPACE_UNKNOWN,
+};
+
+struct ipuv3_channel;
+
+enum ipu_channel_irq {
+ IPU_IRQ_EOF = 0,
+ IPU_IRQ_NFACK = 64,
+ IPU_IRQ_NFB4EOF = 128,
+ IPU_IRQ_EOS = 192,
+};
+
+int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
+ enum ipu_channel_irq irq);
+
+#define IPU_IRQ_DP_SF_START (448 + 2)
+#define IPU_IRQ_DP_SF_END (448 + 3)
+#define IPU_IRQ_BG_SF_END IPU_IRQ_DP_SF_END,
+#define IPU_IRQ_DC_FC_0 (448 + 8)
+#define IPU_IRQ_DC_FC_1 (448 + 9)
+#define IPU_IRQ_DC_FC_2 (448 + 10)
+#define IPU_IRQ_DC_FC_3 (448 + 11)
+#define IPU_IRQ_DC_FC_4 (448 + 12)
+#define IPU_IRQ_DC_FC_6 (448 + 13)
+#define IPU_IRQ_VSYNC_PRE_0 (448 + 14)
+#define IPU_IRQ_VSYNC_PRE_1 (448 + 15)
+
+/*
+ * IPU Image DMA Controller (idmac) functions
+ */
+struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned channel);
+void ipu_idmac_put(struct ipuv3_channel *);
+
+int ipu_idmac_enable_channel(struct ipuv3_channel *channel);
+int ipu_idmac_disable_channel(struct ipuv3_channel *channel);
+int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms);
+
+void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
+ bool doublebuffer);
+void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num);
+
+/*
+ * IPU Display Controller (dc) functions
+ */
+struct ipu_dc;
+struct ipu_di;
+struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel);
+void ipu_dc_put(struct ipu_dc *dc);
+int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
+ u32 pixel_fmt, u32 width);
+void ipu_dc_enable_channel(struct ipu_dc *dc);
+void ipu_dc_disable_channel(struct ipu_dc *dc);
+
+/*
+ * IPU Display Interface (di) functions
+ */
+struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp);
+void ipu_di_put(struct ipu_di *);
+int ipu_di_disable(struct ipu_di *);
+int ipu_di_enable(struct ipu_di *);
+int ipu_di_get_num(struct ipu_di *);
+int ipu_di_init_sync_panel(struct ipu_di *, struct ipu_di_signal_cfg *sig);
+
+/*
+ * IPU Display Multi FIFO Controller (dmfc) functions
+ */
+struct dmfc_channel;
+int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc);
+void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc);
+int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
+ unsigned long bandwidth_mbs, int burstsize);
+void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc);
+int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width);
+struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipuv3_channel);
+void ipu_dmfc_put(struct dmfc_channel *dmfc);
+
+/*
+ * IPU Display Processor (dp) functions
+ */
+#define IPU_DP_FLOW_SYNC_BG 0
+#define IPU_DP_FLOW_SYNC_FG 1
+#define IPU_DP_FLOW_ASYNC0_BG 2
+#define IPU_DP_FLOW_ASYNC0_FG 3
+#define IPU_DP_FLOW_ASYNC1_BG 4
+#define IPU_DP_FLOW_ASYNC1_FG 5
+
+struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow);
+void ipu_dp_put(struct ipu_dp *);
+int ipu_dp_enable_channel(struct ipu_dp *dp);
+void ipu_dp_disable_channel(struct ipu_dp *dp);
+int ipu_dp_setup_channel(struct ipu_dp *dp,
+ enum ipu_color_space in, enum ipu_color_space out);
+int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos);
+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha,
+ bool bg_chan);
+
+#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size))
+
+#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22)
+#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22)
+#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4)
+#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1)
+#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1)
+#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14)
+#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14)
+
+#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10)
+#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9)
+#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13)
+#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12)
+#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1)
+#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1)
+#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12)
+#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11)
+#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10)
+#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7)
+#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10)
+#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1)
+#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1)
+#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7)
+#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1)
+#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1)
+#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3)
+#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2)
+#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1)
+#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3)
+#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2)
+#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1)
+#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1)
+#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1)
+#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1)
+#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1)
+#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1)
+#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13)
+#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12)
+#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29)
+#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29)
+#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20)
+#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7)
+#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4)
+#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1)
+#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3)
+#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2)
+#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7)
+#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14)
+#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3)
+#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3)
+#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3)
+#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3)
+#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5)
+#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5)
+#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5)
+#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5)
+#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1)
+#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1)
+#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1)
+
+struct ipu_cpmem_word {
+ u32 data[5];
+ u32 res[3];
+};
+
+struct ipu_ch_param {
+ struct ipu_cpmem_word word[2];
+};
+
+void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v);
+u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs);
+struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel);
+void ipu_ch_param_dump(struct ipu_ch_param __iomem *p);
+
+static inline void ipu_ch_param_zero(struct ipu_ch_param __iomem *p)
+{
+ int i;
+ void __iomem *base = p;
+
+ for (i = 0; i < sizeof(*p) / sizeof(u32); i++)
+ ipuwritel("chp", 0, base + i * sizeof(u32));
+}
+
+static inline void ipu_cpmem_set_buffer(struct ipu_ch_param __iomem *p,
+ int bufnum, dma_addr_t buf)
+{
+ if (bufnum)
+ ipu_ch_param_write_field(p, IPU_FIELD_EBA1, buf >> 3);
+ else
+ ipu_ch_param_write_field(p, IPU_FIELD_EBA0, buf >> 3);
+}
+
+static inline void ipu_cpmem_set_resolution(struct ipu_ch_param __iomem *p,
+ int xres, int yres)
+{
+ ipu_ch_param_write_field(p, IPU_FIELD_FW, xres - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_FH, yres - 1);
+}
+
+static inline void ipu_cpmem_set_stride(struct ipu_ch_param __iomem *p,
+ int stride)
+{
+ ipu_ch_param_write_field(p, IPU_FIELD_SLY, stride - 1);
+}
+
+void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel);
+
+struct ipu_rgb {
+ struct fb_bitfield red;
+ struct fb_bitfield green;
+ struct fb_bitfield blue;
+ struct fb_bitfield transp;
+ int bits_per_pixel;
+};
+
+struct ipu_rgb *drm_fourcc_to_rgb(u32 drm_fourcc);
+
+int ipu_cpmem_set_format_passthrough(struct ipu_ch_param __iomem *p,
+ int width);
+
+int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *,
+ const struct ipu_rgb *rgb);
+
+static inline void ipu_cpmem_interlaced_scan(struct ipu_ch_param *p,
+ int stride)
+{
+ ipu_ch_param_write_field(p, IPU_FIELD_SO, 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_ILO, stride / 8);
+ ipu_ch_param_write_field(p, IPU_FIELD_SLY, (stride * 2) - 1);
+};
+
+void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format,
+ int stride, int height);
+void ipu_cpmem_set_yuv_interleaved(struct ipu_ch_param __iomem *p,
+ u32 pixel_format);
+void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p,
+ u32 pixel_format, int stride, int u_offset, int v_offset);
+int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 pixelformat);
+
+enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc);
+enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat);
+
+static inline void ipu_cpmem_set_burstsize(struct ipu_ch_param __iomem *p,
+ int burstsize)
+{
+ ipu_ch_param_write_field(p, IPU_FIELD_NPB, burstsize - 1);
+};
+
+struct ipu_client_platformdata {
+ int di;
+ int dc;
+ int dp;
+ int dmfc;
+ int dma[2];
+ struct device_node *device_node;
+};
+
+struct ipu_output;
+
+struct ipu_output_ops {
+ int (*prepare)(struct ipu_output *ipu_video_output, struct fb_videomode *mode, int di);
+ int (*enable)(struct ipu_output *ipu_video_output, struct fb_videomode *mode, int di);
+ int (*disable)(struct ipu_output *ipu_video_output);
+ int (*unprepare)(struct ipu_output *ipu_video_output);
+};
+
+struct ipu_output {
+ struct ipu_output_ops *ops;
+ struct list_head list;
+ unsigned int di_clkflags;
+ uint32_t out_pixel_fmt;
+ struct i2c_adapter *edid_i2c_adapter;
+ struct display_timings *modes;
+ char *name;
+ int ipu_mask;
+};
+
+int ipu_register_output(struct ipu_output *ouput);
+
+#endif /* __DRM_IPU_H__ */
diff --git a/drivers/video/imx-ipu-v3/imx-ldb.c b/drivers/video/imx-ipu-v3/imx-ldb.c
new file mode 100644
index 0000000000..7367fbea71
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/imx-ldb.c
@@ -0,0 +1,310 @@
+/*
+ * i.MX drm driver - parallel display implementation
+ *
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <common.h>
+#include <fb.h>
+#include <io.h>
+#include <driver.h>
+#include <malloc.h>
+#include <errno.h>
+#include <init.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <asm-generic/div64.h>
+#include <linux/clk.h>
+#include <mach/imx6-regs.h>
+#include <mach/imx53-regs.h>
+
+#include "imx-ipu-v3.h"
+#include "ipuv3-plane.h"
+
+#define LDB_CH0_MODE_EN_TO_DI0 (1 << 0)
+#define LDB_CH0_MODE_EN_TO_DI1 (3 << 0)
+#define LDB_CH0_MODE_EN_MASK (3 << 0)
+#define LDB_CH1_MODE_EN_TO_DI0 (1 << 2)
+#define LDB_CH1_MODE_EN_TO_DI1 (3 << 2)
+#define LDB_CH1_MODE_EN_MASK (3 << 2)
+#define LDB_SPLIT_MODE_EN (1 << 4)
+#define LDB_DATA_WIDTH_CH0_24 (1 << 5)
+#define LDB_BIT_MAP_CH0_JEIDA (1 << 6)
+#define LDB_DATA_WIDTH_CH1_24 (1 << 7)
+#define LDB_BIT_MAP_CH1_JEIDA (1 << 8)
+#define LDB_DI0_VS_POL_ACT_LOW (1 << 9)
+#define LDB_DI1_VS_POL_ACT_LOW (1 << 10)
+#define LDB_BGREF_RMODE_INT (1 << 15)
+
+struct imx_ldb;
+
+struct imx_ldb_channel {
+ struct imx_ldb *ldb;
+ int chno;
+ int mode_valid;
+ struct display_timings *modes;
+ struct ipu_output output;
+};
+
+struct imx_ldb_data {
+ void __iomem *base;
+ int (*prepare)(struct imx_ldb_channel *imx_ldb_ch, int di);
+ unsigned ipu_mask;
+};
+
+struct imx_ldb {
+ struct device_d *dev;
+ u32 interface_pix_fmt;
+ int mode_valid;
+ struct imx_ldb_channel channel[2];
+ u32 ldb_ctrl;
+ void __iomem *base;
+ const struct imx_ldb_data *soc_data;
+};
+
+enum {
+ LVDS_BIT_MAP_SPWG,
+ LVDS_BIT_MAP_JEIDA
+};
+
+static const char * const imx_ldb_bit_mappings[] = {
+ [LVDS_BIT_MAP_SPWG] = "spwg",
+ [LVDS_BIT_MAP_JEIDA] = "jeida",
+};
+
+static const int of_get_data_mapping(struct device_node *np)
+{
+ const char *bm;
+ int ret, i;
+
+ ret = of_property_read_string(np, "fsl,data-mapping", &bm);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(imx_ldb_bit_mappings); i++)
+ if (!strcasecmp(bm, imx_ldb_bit_mappings[i]))
+ return i;
+
+ return -EINVAL;
+}
+
+static int imx_ldb_prepare(struct ipu_output *output, struct fb_videomode *mode, int di)
+{
+ struct imx_ldb_channel *imx_ldb_ch = container_of(output, struct imx_ldb_channel, output);
+ struct imx_ldb *ldb = imx_ldb_ch->ldb;
+
+ if (PICOS2KHZ(mode->pixclock) > 85000) {
+ dev_warn(ldb->dev,
+ "%s: mode exceeds 85 MHz pixel clock\n", __func__);
+ }
+
+ ldb->soc_data->prepare(imx_ldb_ch, di);
+
+ /* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */
+ if (imx_ldb_ch == &ldb->channel[0]) {
+ if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
+ ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW;
+ else
+ ldb->ldb_ctrl &= ~LDB_DI0_VS_POL_ACT_LOW;
+ }
+
+ if (imx_ldb_ch == &ldb->channel[1]) {
+ if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
+ ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW;
+ else
+ ldb->ldb_ctrl &= ~LDB_DI1_VS_POL_ACT_LOW;
+ }
+
+ if (imx_ldb_ch == &ldb->channel[0]) {
+ ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
+ ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI0;
+ }
+
+ if (imx_ldb_ch == &ldb->channel[1]) {
+ ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;
+ ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI1;
+ }
+
+ writel(ldb->ldb_ctrl, ldb->base);
+
+ return 0;
+}
+
+static int imx6q_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di)
+{
+ struct clk *diclk, *ldbclk;
+ struct imx_ldb *ldb = imx_ldb_ch->ldb;
+ int ret, ipuno, dino;
+ char *clkname;
+ void __iomem *gpr3 = (void *)MX6_IOMUXC_BASE_ADDR + 0xc;
+ uint32_t val;
+ int shift;
+
+ ipuno = ((di >> 1) & 1) + 1;
+ dino = di & 0x1;
+
+ clkname = asprintf("ipu%d_di%d_sel", ipuno, dino);
+ diclk = clk_lookup(clkname);
+ free(clkname);
+ if (IS_ERR(diclk)) {
+ dev_err(ldb->dev, "failed to get di clk: %s\n", strerror(PTR_ERR(diclk)));
+ return PTR_ERR(diclk);
+ }
+
+ clkname = asprintf("ldb_di%d_podf", imx_ldb_ch->chno);
+ ldbclk = clk_lookup(clkname);
+ free(clkname);
+ if (IS_ERR(ldbclk)) {
+ dev_err(ldb->dev, "failed to get ldb clk: %s\n", strerror(PTR_ERR(ldbclk)));
+ return PTR_ERR(ldbclk);
+ }
+
+ ret = clk_set_parent(diclk, ldbclk);
+ if (ret) {
+ dev_err(ldb->dev, "failed to set display clock parent: %s\n", strerror(-ret));
+ return ret;
+ }
+printk("%s: %d\n", __func__, di);
+ val = readl(gpr3);
+ shift = (imx_ldb_ch->chno == 0) ? 6 : 8;
+ val &= ~(3 << shift);
+ val |= di << shift;
+ writel(val, gpr3);
+
+ return 0;
+}
+
+static int imx53_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di)
+{
+ return -ENOSYS;
+}
+
+static struct imx_ldb_data imx_ldb_data_imx6q = {
+ .base = (void *)MX6_IOMUXC_BASE_ADDR + 0x8,
+ .prepare = imx6q_ldb_prepare,
+ .ipu_mask = 0xf,
+};
+
+static struct imx_ldb_data imx_ldb_data_imx53 = {
+ .base = (void *)MX53_IOMUXC_BASE_ADDR + 0x8,
+ .prepare = imx53_ldb_prepare,
+ .ipu_mask = 0x3,
+};
+
+static struct ipu_output_ops imx_ldb_ops = {
+ .prepare = imx_ldb_prepare,
+};
+
+static int imx_ldb_probe(struct device_d *dev)
+{
+ struct device_node *np = dev->device_node;
+ struct device_node *child;
+ struct imx_ldb *imx_ldb;
+ int ret, i;
+ int dual = 0;
+ int datawidth;
+ int mapping;
+ const struct imx_ldb_data *devtype;
+
+ ret = dev_get_drvdata(dev, (unsigned long *)&devtype);
+ if (ret)
+ return ret;
+
+ imx_ldb = xzalloc(sizeof(*imx_ldb));
+ imx_ldb->base = devtype->base;
+ imx_ldb->soc_data = devtype;
+
+ for_each_child_of_node(np, child) {
+ struct imx_ldb_channel *channel;
+
+ ret = of_property_read_u32(child, "reg", &i);
+ if (ret || i < 0 || i > 1)
+ return -EINVAL;
+
+ if (dual && i > 0) {
+ dev_warn(dev, "dual-channel mode, ignoring second output\n");
+ continue;
+ }
+
+ if (!of_device_is_available(child))
+ continue;
+
+ channel = &imx_ldb->channel[i];
+ channel->ldb = imx_ldb;
+ channel->chno = i;
+
+ ret = of_property_read_u32(child, "fsl,data-width", &datawidth);
+ if (ret)
+ datawidth = 0;
+ else if (datawidth != 18 && datawidth != 24)
+ return -EINVAL;
+
+ mapping = of_get_data_mapping(child);
+ switch (mapping) {
+ case LVDS_BIT_MAP_SPWG:
+ if (datawidth == 24) {
+ if (i == 0 || dual)
+ imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;
+ if (i == 1 || dual)
+ imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24;
+ }
+ break;
+ case LVDS_BIT_MAP_JEIDA:
+ if (datawidth == 18) {
+ dev_err(dev, "JEIDA standard only supported in 24 bit\n");
+ return -EINVAL;
+ }
+ if (i == 0 || dual)
+ imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 | LDB_BIT_MAP_CH0_JEIDA;
+ if (i == 1 || dual)
+ imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 | LDB_BIT_MAP_CH1_JEIDA;
+ break;
+ default:
+ dev_err(dev, "data mapping not specified or invalid\n");
+ return -EINVAL;
+ }
+
+ channel->output.ops = &imx_ldb_ops;
+ channel->output.di_clkflags = IPU_DI_CLKMODE_EXT | IPU_DI_CLKMODE_SYNC;
+ channel->output.out_pixel_fmt = (datawidth == 24) ?
+ V4L2_PIX_FMT_RGB24 : V4L2_PIX_FMT_BGR666;
+ channel->output.modes = of_get_display_timings(child);
+ channel->output.name = asprintf("ldb-%d", i);
+ channel->output.ipu_mask = devtype->ipu_mask;
+
+ ipu_register_output(&channel->output);
+ }
+
+ return 0;
+}
+
+static struct of_device_id imx_ldb_dt_ids[] = {
+ { .compatible = "fsl,imx6q-ldb", (unsigned long)&imx_ldb_data_imx6q},
+ { .compatible = "fsl,imx53-ldb", (unsigned long)&imx_ldb_data_imx53},
+ { /* sentinel */ }
+};
+
+static struct driver_d imx_ldb_driver = {
+ .probe = imx_ldb_probe,
+ .of_compatible = imx_ldb_dt_ids,
+ .name = "imx-ldb",
+};
+device_platform_driver(imx_ldb_driver);
+
+MODULE_DESCRIPTION("i.MX LVDS display driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/imx-ipu-v3/ipu-common.c b/drivers/video/imx-ipu-v3/ipu-common.c
new file mode 100644
index 0000000000..ffd6ec18a9
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipu-common.c
@@ -0,0 +1,836 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 <common.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <clock.h>
+#include <driver.h>
+#include <init.h>
+#include <asm/mmu.h>
+#include <mach/generic.h>
+#include <mach/imx6-regs.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+void ipuwritel(const char *unit, uint32_t value, void __iomem *reg)
+{
+ pr_debug("w: %s 0x%08lx -> 0x%08x\n", unit, (unsigned long)reg & 0xfff, value);
+
+ writel(value, reg);
+}
+
+uint32_t ipureadl(void __iomem *reg)
+{
+ uint32_t val;
+
+ val = readl(reg);
+
+ pr_debug("r: 0x%p -> 0x%08x\n", reg - 0x02600000, val);
+
+ return val;
+}
+
+static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
+{
+ return ipureadl(ipu->cm_reg + offset);
+}
+
+static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
+{
+ ipuwritel("cm", value, ipu->cm_reg + offset);
+}
+
+static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset)
+{
+ return ipureadl(ipu->idmac_reg + offset);
+}
+
+static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value,
+ unsigned offset)
+{
+ ipuwritel("idmac", value, ipu->idmac_reg + offset);
+}
+
+void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
+{
+ u32 val;
+
+ val = ipu_cm_read(ipu, IPU_SRM_PRI2);
+ val |= 0x8;
+ ipu_cm_write(ipu, val, IPU_SRM_PRI2);
+}
+EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update);
+
+struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+
+ return ipu->cpmem_base + channel->num;
+}
+EXPORT_SYMBOL_GPL(ipu_get_cpmem);
+
+void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ struct ipu_ch_param __iomem *p = ipu_get_cpmem(channel);
+ u32 val;
+
+ if (ipu->ipu_type == IPUV3EX)
+ ipu_ch_param_write_field(p, IPU_FIELD_ID, 1);
+
+ val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(channel->num));
+ val |= 1 << (channel->num % 32);
+ ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(channel->num));
+};
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority);
+
+void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v)
+{
+ u32 bit = (wbs >> 8) % 160;
+ u32 size = wbs & 0xff;
+ u32 word = (wbs >> 8) / 160;
+ u32 i = bit / 32;
+ u32 ofs = bit % 32;
+ u32 mask = (1 << size) - 1;
+ u32 val;
+
+ pr_debug("%s %d %d %d 0x%08x\n", __func__, word, bit , size, v);
+
+ val = ipureadl(&base->word[word].data[i]);
+ val &= ~(mask << ofs);
+ val |= v << ofs;
+ ipuwritel("chp", val, &base->word[word].data[i]);
+
+ if ((bit + size - 1) / 32 > i) {
+ val = ipureadl(&base->word[word].data[i + 1]);
+ val &= ~(mask >> (ofs ? (32 - ofs) : 0));
+ val |= v >> (ofs ? (32 - ofs) : 0);
+ ipuwritel("chp", val, &base->word[word].data[i + 1]);
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_ch_param_write_field);
+
+u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs)
+{
+ u32 bit = (wbs >> 8) % 160;
+ u32 size = wbs & 0xff;
+ u32 word = (wbs >> 8) / 160;
+ u32 i = bit / 32;
+ u32 ofs = bit % 32;
+ u32 mask = (1 << size) - 1;
+ u32 val = 0;
+
+ pr_debug("%s %d %d %d\n", __func__, word, bit , size);
+
+ val = (ipureadl(&base->word[word].data[i]) >> ofs) & mask;
+
+ if ((bit + size - 1) / 32 > i) {
+ u32 tmp;
+ tmp = ipureadl(&base->word[word].data[i + 1]);
+ tmp &= mask >> (ofs ? (32 - ofs) : 0);
+ val |= tmp << (ofs ? (32 - ofs) : 0);
+ }
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(ipu_ch_param_read_field);
+
+int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *p,
+ const struct ipu_rgb *rgb)
+{
+ int bpp = 0, npb = 0, ro, go, bo, to;
+
+ ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset;
+ go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset;
+ bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset;
+ to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset;
+
+ ipu_ch_param_write_field(p, IPU_FIELD_WID0, rgb->red.length - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS0, ro);
+ ipu_ch_param_write_field(p, IPU_FIELD_WID1, rgb->green.length - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS1, go);
+ ipu_ch_param_write_field(p, IPU_FIELD_WID2, rgb->blue.length - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS2, bo);
+
+ if (rgb->transp.length) {
+ ipu_ch_param_write_field(p, IPU_FIELD_WID3,
+ rgb->transp.length - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS3, to);
+ } else {
+ ipu_ch_param_write_field(p, IPU_FIELD_WID3, 7);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS3,
+ rgb->bits_per_pixel);
+ }
+
+ switch (rgb->bits_per_pixel) {
+ case 32:
+ bpp = 0;
+ npb = 15;
+ break;
+ case 24:
+ bpp = 1;
+ npb = 19;
+ break;
+ case 16:
+ bpp = 3;
+ npb = 31;
+ break;
+ case 8:
+ bpp = 5;
+ npb = 63;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp);
+ ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb);
+ ipu_ch_param_write_field(p, IPU_FIELD_PFS, 7); /* rgb mode */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb);
+
+static struct ipu_rgb def_rgb_32 = {
+ .red = { .offset = 16, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 0, .length = 8, },
+ .transp = { .offset = 24, .length = 8, },
+ .bits_per_pixel = 32,
+};
+
+static struct ipu_rgb def_bgr_32 = {
+ .red = { .offset = 0, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 16, .length = 8, },
+ .transp = { .offset = 24, .length = 8, },
+ .bits_per_pixel = 32,
+};
+
+static struct ipu_rgb def_rgb_24 = {
+ .red = { .offset = 16, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 0, .length = 8, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 24,
+};
+
+static struct ipu_rgb def_bgr_24 = {
+ .red = { .offset = 0, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 16, .length = 8, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 24,
+};
+
+static struct ipu_rgb def_rgb_16 = {
+ .red = { .offset = 11, .length = 5, },
+ .green = { .offset = 5, .length = 6, },
+ .blue = { .offset = 0, .length = 5, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 16,
+};
+
+static struct ipu_rgb def_bgr_16 = {
+ .red = { .offset = 0, .length = 5, },
+ .green = { .offset = 5, .length = 6, },
+ .blue = { .offset = 11, .length = 5, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 16,
+};
+
+struct ipu_rgb *drm_fourcc_to_rgb(u32 drm_fourcc)
+{
+ switch (drm_fourcc) {
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_XBGR8888:
+ return &def_bgr_32;
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_XRGB8888:
+ return &def_rgb_32;
+ case DRM_FORMAT_BGR888:
+ return &def_bgr_24;
+ case DRM_FORMAT_RGB888:
+ return &def_rgb_24;
+ case DRM_FORMAT_RGB565:
+ return &def_rgb_16;
+ case DRM_FORMAT_BGR565:
+ return &def_bgr_16;
+ default:
+ return NULL;
+ }
+}
+
+#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y))
+#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \
+ (pix->width * (y) / 4) + (x) / 2)
+#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \
+ (pix->width * pix->height / 4) + \
+ (pix->width * (y) / 4) + (x) / 2)
+
+int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 drm_fourcc)
+{
+ switch (drm_fourcc) {
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_XBGR8888:
+ ipu_cpmem_set_format_rgb(cpmem, &def_bgr_32);
+ break;
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_XRGB8888:
+ ipu_cpmem_set_format_rgb(cpmem, &def_rgb_32);
+ break;
+ case DRM_FORMAT_BGR888:
+ ipu_cpmem_set_format_rgb(cpmem, &def_bgr_24);
+ break;
+ case DRM_FORMAT_RGB888:
+ ipu_cpmem_set_format_rgb(cpmem, &def_rgb_24);
+ break;
+ case DRM_FORMAT_RGB565:
+ ipu_cpmem_set_format_rgb(cpmem, &def_rgb_16);
+ break;
+ case DRM_FORMAT_BGR565:
+ ipu_cpmem_set_format_rgb(cpmem, &def_bgr_16);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt);
+
+struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
+{
+ struct ipuv3_channel *channel;
+
+ dev_dbg(ipu->dev, "%s %d\n", __func__, num);
+
+ if (num > 63)
+ return ERR_PTR(-ENODEV);
+
+ mutex_lock(&ipu->channel_lock);
+
+ channel = &ipu->channel[num];
+
+ if (channel->busy) {
+ channel = ERR_PTR(-EBUSY);
+ goto out;
+ }
+
+ channel->busy = true;
+ channel->num = num;
+
+out:
+ mutex_unlock(&ipu->channel_lock);
+
+ return channel;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_get);
+
+void ipu_idmac_put(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+
+ dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
+
+ mutex_lock(&ipu->channel_lock);
+
+ channel->busy = false;
+
+ mutex_unlock(&ipu->channel_lock);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_put);
+
+#define idma_mask(ch) (1 << (ch & 0x1f))
+
+void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
+ bool doublebuffer)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ u32 reg;
+
+ reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
+ if (doublebuffer)
+ reg |= idma_mask(channel->num);
+ else
+ reg &= ~idma_mask(channel->num);
+ ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
+
+int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
+{
+ u32 val;
+
+ val = ipu_cm_read(ipu, IPU_DISP_GEN);
+
+ if (mask & IPU_CONF_DI0_EN)
+ val |= IPU_DI0_COUNTER_RELEASE;
+ if (mask & IPU_CONF_DI1_EN)
+ val |= IPU_DI1_COUNTER_RELEASE;
+
+ ipu_cm_write(ipu, val, IPU_DISP_GEN);
+
+ val = ipu_cm_read(ipu, IPU_CONF);
+ val |= mask;
+ ipu_cm_write(ipu, val, IPU_CONF);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_module_enable);
+
+int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
+{
+ u32 val;
+
+ val = ipu_cm_read(ipu, IPU_CONF);
+ val &= ~mask;
+ ipu_cm_write(ipu, val, IPU_CONF);
+
+ val = ipu_cm_read(ipu, IPU_DISP_GEN);
+
+ if (mask & IPU_CONF_DI0_EN)
+ val &= ~IPU_DI0_COUNTER_RELEASE;
+ if (mask & IPU_CONF_DI1_EN)
+ val &= ~IPU_DI1_COUNTER_RELEASE;
+
+ ipu_cm_write(ipu, val, IPU_DISP_GEN);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_module_disable);
+
+void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ unsigned int chno = channel->num;
+
+ /* Mark buffer as ready. */
+ if (buf_num == 0)
+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
+ else
+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
+
+int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ u32 val;
+
+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
+ val |= idma_mask(channel->num);
+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
+
+int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ uint64_t start;
+
+ start = get_time_ns();
+
+ while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
+ idma_mask(channel->num)) {
+ if (is_timeout(start, ms * MSECOND))
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
+
+int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ u32 val;
+
+ /* Disable DMA channel(s) */
+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
+ val &= ~idma_mask(channel->num);
+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
+
+ /* Set channel buffers NOT to be ready */
+ ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
+
+ if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
+ idma_mask(channel->num)) {
+ ipu_cm_write(ipu, idma_mask(channel->num),
+ IPU_CHA_BUF0_RDY(channel->num));
+ }
+
+ if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
+ idma_mask(channel->num)) {
+ ipu_cm_write(ipu, idma_mask(channel->num),
+ IPU_CHA_BUF1_RDY(channel->num));
+ }
+
+ ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
+
+ /* Reset the double buffer */
+ val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
+ val &= ~idma_mask(channel->num);
+ ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
+
+static int ipu_memory_reset(struct ipu_soc *ipu)
+{
+ uint64_t start;
+
+ ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
+
+ start = get_time_ns();
+
+ while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
+ if (is_timeout(start, SECOND))
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int imx6_ipu_reset(struct ipu_soc *ipu)
+{
+ uint32_t val;
+ int ret;
+ void __iomem *reg;
+
+ reg = (void *)MX6_SRC_BASE_ADDR;
+ val = ipureadl(reg);
+ if (ipu->base == (void *)MX6_IPU1_BASE_ADDR)
+ val |= (1 << 3);
+ else
+ val |= (1 << 12);
+
+ ipuwritel("reset", val, reg);
+
+ ret = wait_on_timeout(100 * MSECOND, !(readl(reg) & (1 << 3)));
+
+ return ret;
+
+}
+
+struct ipu_devtype {
+ const char *name;
+ unsigned long cm_ofs;
+ unsigned long cpmem_ofs;
+ unsigned long srm_ofs;
+ unsigned long tpm_ofs;
+ unsigned long disp0_ofs;
+ unsigned long disp1_ofs;
+ unsigned long dc_tmpl_ofs;
+ unsigned long vdi_ofs;
+ enum ipuv3_type type;
+ int (*reset)(struct ipu_soc *ipu);
+};
+
+static struct ipu_devtype ipu_type_imx51 = {
+ .name = "IPUv3EX",
+ .cm_ofs = 0x1e000000,
+ .cpmem_ofs = 0x1f000000,
+ .srm_ofs = 0x1f040000,
+ .tpm_ofs = 0x1f060000,
+ .disp0_ofs = 0x1e040000,
+ .disp1_ofs = 0x1e048000,
+ .dc_tmpl_ofs = 0x1f080000,
+ .vdi_ofs = 0x1e068000,
+ .type = IPUV3EX,
+};
+
+static struct ipu_devtype ipu_type_imx53 = {
+ .name = "IPUv3M",
+ .cm_ofs = 0x06000000,
+ .cpmem_ofs = 0x07000000,
+ .srm_ofs = 0x07040000,
+ .tpm_ofs = 0x07060000,
+ .disp0_ofs = 0x06040000,
+ .disp1_ofs = 0x06048000,
+ .dc_tmpl_ofs = 0x07080000,
+ .vdi_ofs = 0x06068000,
+ .type = IPUV3M,
+};
+
+static struct ipu_devtype ipu_type_imx6q = {
+ .name = "IPUv3H",
+ .cm_ofs = 0x00200000,
+ .cpmem_ofs = 0x00300000,
+ .srm_ofs = 0x00340000,
+ .tpm_ofs = 0x00360000,
+ .disp0_ofs = 0x00240000,
+ .disp1_ofs = 0x00248000,
+ .dc_tmpl_ofs = 0x00380000,
+ .vdi_ofs = 0x00268000,
+ .type = IPUV3H,
+ .reset = imx6_ipu_reset,
+};
+
+static struct of_device_id imx_ipu_dt_ids[] = {
+ { .compatible = "fsl,imx51-ipu", .data = (unsigned long)&ipu_type_imx51, },
+ { .compatible = "fsl,imx53-ipu", .data = (unsigned long)&ipu_type_imx53, },
+ { .compatible = "fsl,imx6q-ipu", .data = (unsigned long)&ipu_type_imx6q, },
+ { /* sentinel */ }
+};
+
+static int ipu_submodules_init(struct ipu_soc *ipu,
+ struct device_d *dev, void __iomem *ipu_base,
+ struct clk *ipu_clk)
+{
+ char *unit;
+ int ret;
+ const struct ipu_devtype *devtype = ipu->devtype;
+
+ ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
+ IPU_CONF_DI0_EN, ipu_clk);
+ if (ret) {
+ unit = "di0";
+ goto err_di_0;
+ }
+
+ ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
+ IPU_CONF_DI1_EN, ipu_clk);
+ if (ret) {
+ unit = "di1";
+ goto err_di_1;
+ }
+
+ ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
+ IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
+ if (ret) {
+ unit = "dc_template";
+ goto err_dc;
+ }
+
+ ret = ipu_dmfc_init(ipu, dev, ipu_base +
+ devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
+ if (ret) {
+ unit = "dmfc";
+ goto err_dmfc;
+ }
+
+ ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
+ if (ret) {
+ unit = "dp";
+ goto err_dp;
+ }
+
+ return 0;
+
+err_dp:
+ ipu_dmfc_exit(ipu);
+err_dmfc:
+ ipu_dc_exit(ipu);
+err_dc:
+ ipu_di_exit(ipu, 1);
+err_di_1:
+ ipu_di_exit(ipu, 0);
+err_di_0:
+ dev_err(dev, "init %s failed with %d\n", unit, ret);
+ return ret;
+}
+
+static void ipu_submodules_exit(struct ipu_soc *ipu)
+{
+ ipu_dp_exit(ipu);
+ ipu_dmfc_exit(ipu);
+ ipu_dc_exit(ipu);
+ ipu_di_exit(ipu, 1);
+ ipu_di_exit(ipu, 0);
+}
+
+struct ipu_platform_reg {
+ struct ipu_client_platformdata pdata;
+ const char *name;
+};
+
+static struct ipu_platform_reg client_reg[] = {
+ {
+ .pdata = {
+ .di = 0,
+ .dc = 5,
+ .dp = IPU_DP_FLOW_SYNC_BG,
+ .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
+ .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
+ },
+ .name = "imx-ipuv3-crtc",
+ }, {
+ .pdata = {
+ .di = 1,
+ .dc = 1,
+ .dp = -EINVAL,
+ .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
+ .dma[1] = -EINVAL,
+ },
+ .name = "imx-ipuv3-crtc",
+ },
+};
+
+static int ipu_client_id;
+
+static int ipu_add_subdevice_pdata(struct device_d *ipu_dev,
+ struct ipu_platform_reg *reg)
+{
+ struct device_d *dev;
+ int ret;
+
+ dev = device_alloc(reg->name, ipu_client_id++);
+ dev->parent = ipu_dev;
+ device_add_data(dev, &reg->pdata, sizeof(reg->pdata));
+ ((struct ipu_client_platformdata *)dev->platform_data)->device_node = ipu_dev->device_node;
+
+ ret = platform_device_register(dev);
+
+ return ret;
+}
+
+static int ipu_add_client_devices(struct ipu_soc *ipu)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
+ struct ipu_platform_reg *reg = &client_reg[i];
+ ret = ipu_add_subdevice_pdata(ipu->dev, reg);
+ if (ret)
+ goto err_register;
+ }
+
+ return 0;
+
+err_register:
+
+ return ret;
+}
+
+static int ipu_probe(struct device_d *dev)
+{
+ struct ipu_soc *ipu;
+ void __iomem *ipu_base;
+ int i, ret;
+ const struct ipu_devtype *devtype;
+
+ ret = dev_get_drvdata(dev, (unsigned long *)&devtype);
+ if (ret)
+ return ret;
+
+ ipu_base = dev_request_mem_region(dev, 0);
+ if (!ipu_base)
+ return -EBUSY;
+
+ ipu = xzalloc(sizeof(*ipu));
+
+ ipu->base = ipu_base;
+
+ for (i = 0; i < 64; i++)
+ ipu->channel[i].ipu = ipu;
+ ipu->devtype = devtype;
+ ipu->ipu_type = devtype->type;
+
+ dev_dbg(dev, "cm_reg: 0x%p\n",
+ ipu_base + devtype->cm_ofs);
+ dev_dbg(dev, "idmac: 0x%p\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
+ dev_dbg(dev, "cpmem: 0x%p\n",
+ ipu_base + devtype->cpmem_ofs);
+ dev_dbg(dev, "disp0: 0x%p\n",
+ ipu_base + devtype->disp0_ofs);
+ dev_dbg(dev, "disp1: 0x%p\n",
+ ipu_base + devtype->disp1_ofs);
+ dev_dbg(dev, "srm: 0x%p\n",
+ ipu_base + devtype->srm_ofs);
+ dev_dbg(dev, "tpm: 0x%p\n",
+ ipu_base + devtype->tpm_ofs);
+ dev_dbg(dev, "dc: 0x%p\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
+ dev_dbg(dev, "ic: 0x%p\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
+ dev_dbg(dev, "dmfc: 0x%p\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
+ dev_dbg(dev, "vdi: 0x%p\n",
+ ipu_base + devtype->vdi_ofs);
+
+ ipu->cm_reg = ipu_base + devtype->cm_ofs, PAGE_SIZE;
+ ipu->idmac_reg = ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS;
+ ipu->cpmem_base = ipu_base + devtype->cpmem_ofs;
+
+ ipu->clk = clk_get(dev, "bus");
+ if (IS_ERR(ipu->clk)) {
+ ret = PTR_ERR(ipu->clk);
+ dev_err(dev, "clk_get failed with %d", ret);
+ return ret;
+ }
+
+ dev->priv = ipu;
+
+ ret = clk_enable(ipu->clk);
+ if (ret)
+ return ret;
+
+ ipu->dev = dev;
+
+ ret = devtype->reset(ipu);
+ if (ret) {
+ dev_err(dev, "failed to reset: %d\n", ret);
+ goto out_failed_reset;
+ }
+
+ ret = ipu_memory_reset(ipu);
+ if (ret)
+ goto out_failed_reset;
+
+ /* Set MCU_T to divide MCU access window into 2 */
+ ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
+ IPU_DISP_GEN);
+
+ ret = ipu_submodules_init(ipu, dev, ipu_base, ipu->clk);
+ if (ret)
+ goto failed_submodules_init;
+
+ ret = ipu_add_client_devices(ipu);
+ if (ret) {
+ dev_err(dev, "adding client devices failed with %d\n",
+ ret);
+ goto failed_add_clients;
+ }
+
+ dev_info(dev, "%s probed\n", devtype->name);
+
+ return 0;
+
+failed_add_clients:
+ ipu_submodules_exit(ipu);
+failed_submodules_init:
+out_failed_reset:
+ clk_disable(ipu->clk);
+ return ret;
+}
+
+static struct driver_d imx_ipu_driver = {
+ .name = "imx-ipuv3",
+ .of_compatible = imx_ipu_dt_ids,
+ .probe = ipu_probe,
+};
+
+device_platform_driver(imx_ipu_driver);
+
+MODULE_DESCRIPTION("i.MX IPU v3 driver");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/imx-ipu-v3/ipu-dc.c b/drivers/video/imx-ipu-v3/ipu-dc.c
new file mode 100644
index 0000000000..2deb2ae048
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipu-dc.c
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 <common.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <malloc.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+#define DC_MAP_CONF_PTR(n) (0x108 + ((n) & ~0x1) * 2)
+#define DC_MAP_CONF_VAL(n) (0x144 + ((n) & ~0x1) * 2)
+
+#define DC_EVT_NF 0
+#define DC_EVT_NL 1
+#define DC_EVT_EOF 2
+#define DC_EVT_NFIELD 3
+#define DC_EVT_EOL 4
+#define DC_EVT_EOFIELD 5
+#define DC_EVT_NEW_ADDR 6
+#define DC_EVT_NEW_CHAN 7
+#define DC_EVT_NEW_DATA 8
+
+#define DC_EVT_NEW_ADDR_W_0 0
+#define DC_EVT_NEW_ADDR_W_1 1
+#define DC_EVT_NEW_CHAN_W_0 2
+#define DC_EVT_NEW_CHAN_W_1 3
+#define DC_EVT_NEW_DATA_W_0 4
+#define DC_EVT_NEW_DATA_W_1 5
+#define DC_EVT_NEW_ADDR_R_0 6
+#define DC_EVT_NEW_ADDR_R_1 7
+#define DC_EVT_NEW_CHAN_R_0 8
+#define DC_EVT_NEW_CHAN_R_1 9
+#define DC_EVT_NEW_DATA_R_0 10
+#define DC_EVT_NEW_DATA_R_1 11
+
+#define DC_WR_CH_CONF 0x0
+#define DC_WR_CH_ADDR 0x4
+#define DC_RL_CH(evt) (8 + ((evt) & ~0x1) * 2)
+
+#define DC_GEN 0xd4
+#define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4)
+#define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4)
+#define DC_STAT 0x1c8
+
+#define WROD(lf) (0x18 | ((lf) << 1))
+#define WRG 0x01
+#define WCLK 0xc9
+
+#define SYNC_WAVE 0
+#define NULL_WAVE (-1)
+
+#define DC_GEN_SYNC_1_6_SYNC (2 << 1)
+#define DC_GEN_SYNC_PRIORITY_1 (1 << 7)
+
+#define DC_WR_CH_CONF_WORD_SIZE_8 (0 << 0)
+#define DC_WR_CH_CONF_WORD_SIZE_16 (1 << 0)
+#define DC_WR_CH_CONF_WORD_SIZE_24 (2 << 0)
+#define DC_WR_CH_CONF_WORD_SIZE_32 (3 << 0)
+#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i) (((i) & 0x1) << 3)
+#define DC_WR_CH_CONF_DISP_ID_SERIAL (2 << 3)
+#define DC_WR_CH_CONF_DISP_ID_ASYNC (3 << 4)
+#define DC_WR_CH_CONF_FIELD_MODE (1 << 9)
+#define DC_WR_CH_CONF_PROG_TYPE_NORMAL (4 << 5)
+#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5)
+#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2)
+#define DC_WR_CH_CONF_PROG_DISP_ID(i) (((i) & 0x1) << 3)
+
+#define IPU_DC_NUM_CHANNELS 10
+
+struct ipu_dc_priv;
+
+enum ipu_dc_map {
+ IPU_DC_MAP_RGB24,
+ IPU_DC_MAP_RGB565,
+ IPU_DC_MAP_GBR24, /* TVEv2 */
+ IPU_DC_MAP_BGR666,
+ IPU_DC_MAP_BGR24,
+};
+
+struct ipu_dc {
+ /* The display interface number assigned to this dc channel */
+ unsigned int di;
+ void __iomem *base;
+ struct ipu_dc_priv *priv;
+ int chno;
+ bool in_use;
+};
+
+struct ipu_dc_priv {
+ void __iomem *dc_reg;
+ void __iomem *dc_tmpl_reg;
+ struct ipu_soc *ipu;
+ struct device_d *dev;
+ struct ipu_dc channels[IPU_DC_NUM_CHANNELS];
+};
+
+static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority)
+{
+ u32 reg;
+
+ reg = ipureadl(dc->base + DC_RL_CH(event));
+ reg &= ~(0xffff << (16 * (event & 0x1)));
+ reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
+ ipuwritel("dc", reg, dc->base + DC_RL_CH(event));
+}
+
+static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand,
+ int map, int wave, int glue, int sync, int stop)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+ u32 reg1, reg2;
+
+ if (opcode == WCLK) {
+ reg1 = (operand << 20) & 0xfff00000;
+ reg2 = operand >> 12 | opcode << 1 | stop << 9;
+ } else if (opcode == WRG) {
+ reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000);
+ reg2 = operand >> 17 | opcode << 7 | stop << 9;
+ } else {
+ reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000);
+ reg2 = operand >> 12 | opcode << 4 | stop << 9;
+ }
+ ipuwritel("dc", reg1, priv->dc_tmpl_reg + word * 8);
+ ipuwritel("dc", reg2, priv->dc_tmpl_reg + word * 8 + 4);
+}
+
+static int ipu_pixfmt_to_map(u32 fmt)
+{
+ switch (fmt) {
+ case V4L2_PIX_FMT_RGB24:
+ return IPU_DC_MAP_RGB24;
+ case V4L2_PIX_FMT_RGB565:
+ return IPU_DC_MAP_RGB565;
+ case IPU_PIX_FMT_GBR24:
+ return IPU_DC_MAP_GBR24;
+ case V4L2_PIX_FMT_BGR666:
+ return IPU_DC_MAP_BGR666;
+ case V4L2_PIX_FMT_BGR24:
+ return IPU_DC_MAP_BGR24;
+ default:
+ return -EINVAL;
+ }
+}
+
+int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
+ u32 pixel_fmt, u32 width)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+ u32 reg = 0;
+ int map;
+
+ dc->di = ipu_di_get_num(di);
+
+ map = ipu_pixfmt_to_map(pixel_fmt);
+ if (map < 0) {
+ dev_dbg(priv->dev, "IPU_DISP: No MAP\n");
+ return map;
+ }
+
+ if (interlaced) {
+ dc_link_event(dc, DC_EVT_NL, 0, 3);
+ dc_link_event(dc, DC_EVT_EOL, 0, 2);
+ dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
+
+ /* Init template microcode */
+ dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
+ } else {
+ if (dc->di) {
+ dc_link_event(dc, DC_EVT_NL, 2, 3);
+ dc_link_event(dc, DC_EVT_EOL, 3, 2);
+ dc_link_event(dc, DC_EVT_NEW_DATA, 1, 1);
+ /* Init template microcode */
+ dc_write_tmpl(dc, 2, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1);
+ dc_write_tmpl(dc, 3, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0);
+ dc_write_tmpl(dc, 4, WRG, 0, map, NULL_WAVE, 0, 0, 1);
+ dc_write_tmpl(dc, 1, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
+ } else {
+ dc_link_event(dc, DC_EVT_NL, 5, 3);
+ dc_link_event(dc, DC_EVT_EOL, 6, 2);
+ dc_link_event(dc, DC_EVT_NEW_DATA, 8, 1);
+ /* Init template microcode */
+ dc_write_tmpl(dc, 5, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1);
+ dc_write_tmpl(dc, 6, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0);
+ dc_write_tmpl(dc, 7, WRG, 0, map, NULL_WAVE, 0, 0, 1);
+ dc_write_tmpl(dc, 8, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
+ }
+ }
+ dc_link_event(dc, DC_EVT_NF, 0, 0);
+ dc_link_event(dc, DC_EVT_NFIELD, 0, 0);
+ dc_link_event(dc, DC_EVT_EOF, 0, 0);
+ dc_link_event(dc, DC_EVT_EOFIELD, 0, 0);
+ dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0);
+ dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0);
+
+ reg = ipureadl(dc->base + DC_WR_CH_CONF);
+ if (interlaced)
+ reg |= DC_WR_CH_CONF_FIELD_MODE;
+ else
+ reg &= ~DC_WR_CH_CONF_FIELD_MODE;
+ ipuwritel("dc", reg, dc->base + DC_WR_CH_CONF);
+
+ ipuwritel("dc", 0x0, dc->base + DC_WR_CH_ADDR);
+ ipuwritel("dc", width, priv->dc_reg + DC_DISP_CONF2(dc->di));
+
+ ipu_module_enable(priv->ipu, IPU_CONF_DC_EN);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dc_init_sync);
+
+void ipu_dc_enable_channel(struct ipu_dc *dc)
+{
+ int di;
+ u32 reg;
+
+ di = dc->di;
+
+ reg = ipureadl(dc->base + DC_WR_CH_CONF);
+ reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL;
+ ipuwritel("dc", reg, dc->base + DC_WR_CH_CONF);
+}
+EXPORT_SYMBOL_GPL(ipu_dc_enable_channel);
+
+void ipu_dc_disable_channel(struct ipu_dc *dc)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+ u32 val;
+ int irq = 0, timeout = 50;
+
+ if (dc->chno == 1)
+ irq = IPU_IRQ_DC_FC_1;
+ else if (dc->chno == 5)
+ irq = IPU_IRQ_DP_SF_END;
+ else
+ return;
+
+ /* should wait for the interrupt here */
+ mdelay(50);
+
+ if (dc->di == 0)
+ val = 0x00000002;
+ else
+ val = 0x00000020;
+
+ /* Wait for DC triple buffer to empty */
+ while ((ipureadl(priv->dc_reg + DC_STAT) & val) != val) {
+ mdelay(2);
+ timeout -= 2;
+ if (timeout <= 0)
+ break;
+ }
+
+ val = ipureadl(dc->base + DC_WR_CH_CONF);
+ val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+ ipuwritel("dc", val, dc->base + DC_WR_CH_CONF);
+}
+EXPORT_SYMBOL_GPL(ipu_dc_disable_channel);
+
+static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map,
+ int byte_num, int offset, int mask)
+{
+ int ptr = map * 3 + byte_num;
+ u32 reg;
+
+ reg = ipureadl(priv->dc_reg + DC_MAP_CONF_VAL(ptr));
+ reg &= ~(0xffff << (16 * (ptr & 0x1)));
+ reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
+ ipuwritel("dc", reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr));
+
+ reg = ipureadl(priv->dc_reg + DC_MAP_CONF_PTR(map));
+ reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num)));
+ reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
+ ipuwritel("dc", reg, priv->dc_reg + DC_MAP_CONF_PTR(map));
+}
+
+static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map)
+{
+ u32 reg = ipureadl(priv->dc_reg + DC_MAP_CONF_PTR(map));
+
+ ipuwritel("dc", reg & ~(0xffff << (16 * (map & 0x1))),
+ priv->dc_reg + DC_MAP_CONF_PTR(map));
+}
+
+struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel)
+{
+ struct ipu_dc_priv *priv = ipu->dc_priv;
+ struct ipu_dc *dc;
+
+ if (channel >= IPU_DC_NUM_CHANNELS)
+ return ERR_PTR(-ENODEV);
+
+ dc = &priv->channels[channel];
+
+ if (dc->in_use)
+ return ERR_PTR(-EBUSY);
+
+ dc->in_use = true;
+
+ return dc;
+}
+EXPORT_SYMBOL_GPL(ipu_dc_get);
+
+void ipu_dc_put(struct ipu_dc *dc)
+{
+ dc->in_use = false;
+}
+EXPORT_SYMBOL_GPL(ipu_dc_put);
+
+int ipu_dc_init(struct ipu_soc *ipu, struct device_d *dev,
+ void __iomem *base, void __iomem *template_base)
+{
+ struct ipu_dc_priv *priv;
+ static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c,
+ 0x78, 0, 0x94, 0xb4};
+ int i;
+
+ priv = xzalloc(sizeof(*priv));
+
+ priv->dev = dev;
+ priv->ipu = ipu;
+ priv->dc_reg = base;
+ priv->dc_tmpl_reg = template_base;
+
+ for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) {
+ priv->channels[i].chno = i;
+ priv->channels[i].priv = priv;
+ priv->channels[i].base = priv->dc_reg + channel_offsets[i];
+ }
+
+ ipuwritel("dc", DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) |
+ DC_WR_CH_CONF_PROG_DI_ID,
+ priv->channels[1].base + DC_WR_CH_CONF);
+ ipuwritel("dc", DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0),
+ priv->channels[5].base + DC_WR_CH_CONF);
+
+ ipuwritel("dc", DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, priv->dc_reg + DC_GEN);
+
+ ipu->dc_priv = priv;
+
+ dev_dbg(dev, "DC base: 0x%p template base: 0x%p\n",
+ base, template_base);
+
+ /* rgb24 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24);
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */
+
+ /* rgb565 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565);
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */
+
+ /* gbr24 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24);
+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */
+
+ /* bgr666 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666);
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */
+
+ /* bgr24 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24);
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */
+
+ return 0;
+}
+
+void ipu_dc_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/video/imx-ipu-v3/ipu-di.c b/drivers/video/imx-ipu-v3/ipu-di.c
new file mode 100644
index 0000000000..e3338d09ba
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipu-di.c
@@ -0,0 +1,762 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 <common.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <asm-generic/div64.h>
+#include <malloc.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+struct ipu_di {
+ void __iomem *base;
+ int id;
+ u32 module;
+ struct clk *clk_di; /* display input clock */
+ struct clk *clk_ipu; /* IPU bus clock */
+ struct clk clk_di_pixel; /* resulting pixel clock */
+ char *clk_name;
+ const char *di_parent_names[2];
+ bool inuse;
+ unsigned long clkflags;
+ struct ipu_soc *ipu;
+};
+
+struct di_sync_config {
+ int run_count;
+ int run_src;
+ int offset_count;
+ int offset_src;
+ int repeat_count;
+ int cnt_clr_src;
+ int cnt_polarity_gen_en;
+ int cnt_polarity_clr_src;
+ int cnt_polarity_trigger_src;
+ int cnt_up;
+ int cnt_down;
+};
+
+enum di_pins {
+ DI_PIN11 = 0,
+ DI_PIN12 = 1,
+ DI_PIN13 = 2,
+ DI_PIN14 = 3,
+ DI_PIN15 = 4,
+ DI_PIN16 = 5,
+ DI_PIN17 = 6,
+ DI_PIN_CS = 7,
+
+ DI_PIN_SER_CLK = 0,
+ DI_PIN_SER_RS = 1,
+};
+
+enum di_sync_wave {
+ DI_SYNC_NONE = 0,
+ DI_SYNC_CLK = 1,
+ DI_SYNC_INT_HSYNC = 2,
+ DI_SYNC_HSYNC = 3,
+ DI_SYNC_VSYNC = 4,
+ DI_SYNC_DE = 6,
+};
+
+#define SYNC_WAVE 0
+
+#define DI_GENERAL 0x0000
+#define DI_BS_CLKGEN0 0x0004
+#define DI_BS_CLKGEN1 0x0008
+#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1))
+#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1))
+#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2))
+#define DI_SYNC_AS_GEN 0x0054
+#define DI_DW_GEN(gen) (0x0058 + 4 * (gen))
+#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set)))
+#define DI_SER_CONF 0x015c
+#define DI_SSC 0x0160
+#define DI_POL 0x0164
+#define DI_AW0 0x0168
+#define DI_AW1 0x016c
+#define DI_SCR_CONF 0x0170
+#define DI_STAT 0x0174
+
+#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19)
+#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16)
+#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3)
+#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0)
+
+#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29)
+#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25)
+#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12)
+#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9)
+#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16)
+#define DI_SW_GEN1_CNT_UP(x) (x)
+#define DI_SW_GEN1_AUTO_RELOAD (0x10000000)
+
+#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24
+#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16
+
+#define DI_GEN_POLARITY_1 (1 << 0)
+#define DI_GEN_POLARITY_2 (1 << 1)
+#define DI_GEN_POLARITY_3 (1 << 2)
+#define DI_GEN_POLARITY_4 (1 << 3)
+#define DI_GEN_POLARITY_5 (1 << 4)
+#define DI_GEN_POLARITY_6 (1 << 5)
+#define DI_GEN_POLARITY_7 (1 << 6)
+#define DI_GEN_POLARITY_8 (1 << 7)
+#define DI_GEN_POLARITY_DISP_CLK (1 << 17)
+#define DI_GEN_DI_CLK_EXT (1 << 20)
+#define DI_GEN_DI_VSYNC_EXT (1 << 21)
+
+#define DI_POL_DRDY_DATA_POLARITY (1 << 7)
+#define DI_POL_DRDY_POLARITY_15 (1 << 4)
+
+#define DI_VSYNC_SEL_OFFSET 13
+
+static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset)
+{
+ return readl(di->base + offset);
+}
+
+static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset)
+{
+ ipuwritel("di", value, di->base + offset);
+}
+
+static int ipu_di_clk_calc_div(unsigned long inrate, unsigned long outrate)
+{
+ u64 tmp = inrate;
+ int div;
+
+ tmp *= 16;
+
+ do_div(tmp, outrate);
+
+ div = tmp;
+
+ if (div < 0x10)
+ div = 0x10;
+
+#ifdef WTF_IS_THIS
+ /*
+ * Freescale has this in their Kernel. It is neither clear what
+ * it does nor why it does it
+ */
+ if (div & 0x10)
+ div &= ~0x7;
+ else {
+ /* Round up divider if it gets us closer to desired pix clk */
+ if ((div & 0xC) == 0xC) {
+ div += 0x10;
+ div &= ~0xF;
+ }
+ }
+#endif
+ return div;
+}
+
+static unsigned long clk_di_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel);
+ unsigned long outrate;
+ u32 div = ipu_di_read(di, DI_BS_CLKGEN0);
+
+ if (div < 0x10)
+ div = 0x10;
+
+ outrate = (parent_rate / div) * 16;
+
+ return outrate;
+}
+
+static long clk_di_round_rate(struct clk *clk, unsigned long rate,
+ unsigned long *prate)
+{
+ struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel);
+ unsigned long outrate;
+ int div;
+ u32 val;
+
+ div = ipu_di_clk_calc_div(*prate, rate);
+
+ outrate = (*prate / div) * 16;
+
+ val = ipu_di_read(di, DI_GENERAL);
+
+ if (!(val & DI_GEN_DI_CLK_EXT) && outrate > *prate / 2)
+ outrate = *prate / 2;
+
+ dev_dbg(di->ipu->dev,
+ "%s: inrate: %ld div: 0x%08x outrate: %ld wanted: %ld\n",
+ __func__, *prate, div, outrate, rate);
+
+ return outrate;
+}
+
+static int clk_di_set_rate(struct clk *clk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel);
+ int div;
+ u32 clkgen0;
+
+ clkgen0 = ipu_di_read(di, DI_BS_CLKGEN0) & ~0xfff;
+
+ div = ipu_di_clk_calc_div(parent_rate, rate);
+
+ ipu_di_write(di, clkgen0 | div, DI_BS_CLKGEN0);
+
+ dev_dbg(di->ipu->dev, "%s: inrate: %ld desired: %ld div: 0x%08x\n",
+ __func__, parent_rate, rate, div);
+ return 0;
+}
+
+static int clk_di_get_parent(struct clk *clk)
+{
+ struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel);
+ u32 val;
+
+ val = ipu_di_read(di, DI_GENERAL);
+
+ return val & DI_GEN_DI_CLK_EXT ? 1 : 0;
+}
+
+static int clk_di_set_parent(struct clk *clk, u8 index)
+{
+ struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel);
+ u32 val;
+
+ val = ipu_di_read(di, DI_GENERAL);
+
+ if (index)
+ val |= DI_GEN_DI_CLK_EXT;
+ else
+ val &= ~DI_GEN_DI_CLK_EXT;
+
+ ipu_di_write(di, val, DI_GENERAL);
+
+ return 0;
+}
+
+static struct clk_ops clk_di_ops = {
+ .round_rate = clk_di_round_rate,
+ .set_rate = clk_di_set_rate,
+ .recalc_rate = clk_di_recalc_rate,
+ .set_parent = clk_di_set_parent,
+ .get_parent = clk_di_get_parent,
+};
+
+static void ipu_di_data_wave_config(struct ipu_di *di,
+ int wave_gen,
+ int access_size, int component_size)
+{
+ u32 reg;
+ reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) |
+ (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET);
+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen));
+}
+
+static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin,
+ int set, int up, int down)
+{
+ u32 reg;
+
+ reg = ipu_di_read(di, DI_DW_GEN(wave_gen));
+ reg &= ~(0x3 << (di_pin * 2));
+ reg |= set << (di_pin * 2);
+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen));
+
+ ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set));
+}
+
+static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config,
+ int start, int count)
+{
+ u32 reg;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ struct di_sync_config *c = &config[i];
+ int wave_gen = start + i + 1;
+
+ if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) ||
+ (c->repeat_count >= 0x1000) ||
+ (c->cnt_up >= 0x400) ||
+ (c->cnt_down >= 0x400)) {
+ dev_err(di->ipu->dev, "DI%d counters out of range.\n",
+ di->id);
+ return;
+ }
+
+ reg = DI_SW_GEN0_RUN_COUNT(c->run_count) |
+ DI_SW_GEN0_RUN_SRC(c->run_src) |
+ DI_SW_GEN0_OFFSET_COUNT(c->offset_count) |
+ DI_SW_GEN0_OFFSET_SRC(c->offset_src);
+ ipu_di_write(di, reg, DI_SW_GEN0(wave_gen));
+
+ reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) |
+ DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) |
+ DI_SW_GEN1_CNT_POL_TRIGGER_SRC(
+ c->cnt_polarity_trigger_src) |
+ DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) |
+ DI_SW_GEN1_CNT_DOWN(c->cnt_down) |
+ DI_SW_GEN1_CNT_UP(c->cnt_up);
+
+ /* Enable auto reload */
+ if (c->repeat_count == 0)
+ reg |= DI_SW_GEN1_AUTO_RELOAD;
+
+ ipu_di_write(di, reg, DI_SW_GEN1(wave_gen));
+
+ reg = ipu_di_read(di, DI_STP_REP(wave_gen));
+ reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1)));
+ reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1));
+ ipu_di_write(di, reg, DI_STP_REP(wave_gen));
+ }
+}
+
+static void ipu_di_sync_config_interlaced(struct ipu_di *di,
+ struct ipu_di_signal_cfg *sig)
+{
+ u32 h_total = sig->width + sig->h_sync_width +
+ sig->h_start_width + sig->h_end_width;
+ u32 v_total = sig->height + sig->v_sync_width +
+ sig->v_start_width + sig->v_end_width;
+ u32 reg;
+ struct di_sync_config cfg[] = {
+ {
+ .run_count = h_total / 2 - 1,
+ .run_src = DI_SYNC_CLK,
+ }, {
+ .run_count = h_total - 11,
+ .run_src = DI_SYNC_CLK,
+ .cnt_down = 4,
+ }, {
+ .run_count = v_total * 2 - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = 1,
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = 4,
+ }, {
+ .run_count = v_total / 2 - 1,
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = sig->v_start_width,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = 2,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ }, {
+ .run_src = DI_SYNC_HSYNC,
+ .repeat_count = sig->height / 2,
+ .cnt_clr_src = 4,
+ }, {
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_HSYNC,
+ }, {
+ .run_count = v_total / 2 - 1,
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = 9,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = 2,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ }, {
+ .run_src = DI_SYNC_CLK,
+ .offset_count = sig->h_start_width,
+ .offset_src = DI_SYNC_CLK,
+ .repeat_count = sig->width,
+ .cnt_clr_src = 5,
+ }, {
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = v_total / 2,
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_clr_src = DI_SYNC_HSYNC,
+ .cnt_down = 4,
+ }
+ };
+
+ ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
+
+ /* set gentime select and tag sel */
+ reg = ipu_di_read(di, DI_SW_GEN1(9));
+ reg &= 0x1FFFFFFF;
+ reg |= (3 - 1) << 29 | 0x00008000;
+ ipu_di_write(di, reg, DI_SW_GEN1(9));
+
+ ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);
+}
+
+static void ipu_di_sync_config_noninterlaced(struct ipu_di *di,
+ struct ipu_di_signal_cfg *sig, int div)
+{
+ u32 h_total = sig->width + sig->h_sync_width + sig->h_start_width +
+ sig->h_end_width;
+ u32 v_total = sig->height + sig->v_sync_width + sig->v_start_width +
+ sig->v_end_width;
+ struct di_sync_config cfg[] = {
+ {
+ /* 1: INT_HSYNC */
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ } , {
+ /* PIN2: HSYNC */
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ .offset_count = div * sig->v_to_h_sync,
+ .offset_src = DI_SYNC_CLK,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_CLK,
+ .cnt_down = sig->h_sync_width * 2,
+ } , {
+ /* PIN3: VSYNC */
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = sig->v_sync_width * 2,
+ } , {
+ /* 4: Line Active */
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = sig->v_sync_width + sig->v_start_width,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = sig->height,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ } , {
+ /* 5: Pixel Active, referenced by DC */
+ .run_src = DI_SYNC_CLK,
+ .offset_count = sig->h_sync_width + sig->h_start_width,
+ .offset_src = DI_SYNC_CLK,
+ .repeat_count = sig->width,
+ .cnt_clr_src = 5, /* Line Active */
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ },
+ };
+ /* can't use #7 and #8 for line active and pixel active counters */
+ struct di_sync_config cfg_vga[] = {
+ {
+ /* 1: INT_HSYNC */
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ } , {
+ /* 2: VSYNC */
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ } , {
+ /* 3: Line Active */
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = sig->v_sync_width + sig->v_start_width,
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .repeat_count = sig->height,
+ .cnt_clr_src = 3 /* VSYNC */,
+ } , {
+ /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */
+ .offset_src = DI_SYNC_CLK,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_CLK,
+ .cnt_down = sig->h_sync_width * 2,
+ } , {
+ /* 5: Pixel Active signal to DC */
+ .run_src = DI_SYNC_CLK,
+ .offset_count = sig->h_sync_width + sig->h_start_width,
+ .offset_src = DI_SYNC_CLK,
+ .repeat_count = sig->width,
+ .cnt_clr_src = 4, /* Line Active */
+ } , {
+ /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = 1, /* magic value from Freescale TVE driver */
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = sig->v_sync_width * 2,
+ } , {
+ /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */
+ .offset_src = DI_SYNC_CLK,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_CLK,
+ .cnt_down = sig->h_sync_width * 2,
+ } , {
+ /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = 1, /* magic value from Freescale TVE driver */
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = sig->v_sync_width * 2,
+ } , {
+ /* unused */
+ },
+ };
+
+ ipu_di_write(di, v_total - 1, DI_SCR_CONF);
+ if (sig->hsync_pin == 2 && sig->vsync_pin == 3)
+ ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
+ else
+ ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga));
+}
+
+int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
+{
+ u32 reg;
+ u32 di_gen, vsync_cnt;
+ u32 div;
+ u32 h_total, v_total;
+ int ret;
+ unsigned long round;
+ struct clk *parent;
+
+ dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d, pixelclock = %ld\n",
+ di->id, sig->width, sig->height, sig->pixelclock);
+
+ if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0))
+ return -EINVAL;
+
+ if (sig->clkflags & IPU_DI_CLKMODE_EXT)
+ parent = di->clk_di;
+ else
+ parent = di->clk_ipu;
+
+ ret = clk_set_parent(&di->clk_di_pixel, parent);
+ if (ret) {
+ dev_err(di->ipu->dev,
+ "setting pixel clock to parent %s failed with %d\n",
+ parent->name, ret);
+ return ret;
+ }
+
+ if (sig->clkflags & IPU_DI_CLKMODE_SYNC) {
+ ret = clk_set_rate(clk_get_parent(parent), sig->pixelclock);
+ round = sig->pixelclock;
+ } else if (sig->clkflags & IPU_DI_CLKMODE_NON_FRACTIONAL) {
+ unsigned div;
+
+ round = clk_get_rate(parent);
+ div = DIV_ROUND_CLOSEST(round, sig->pixelclock);
+ round = round / div;
+ } else {
+ round = sig->pixelclock;
+ }
+
+ ret = clk_set_rate(&di->clk_di_pixel, round);
+
+ h_total = sig->width + sig->h_sync_width + sig->h_start_width +
+ sig->h_end_width;
+ v_total = sig->height + sig->v_sync_width + sig->v_start_width +
+ sig->v_end_width;
+
+ div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff;
+ div = div / 16; /* Now divider is integer portion */
+
+ /* Setup pixel clock timing */
+ /* Down time is half of period */
+ ipu_di_write(di, (div << 16), DI_BS_CLKGEN1);
+
+ ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1);
+ ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2);
+
+ di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT;
+ di_gen |= DI_GEN_DI_VSYNC_EXT;
+
+ if (sig->interlaced) {
+ ipu_di_sync_config_interlaced(di, sig);
+
+ /* set y_sel = 1 */
+ di_gen |= 0x10000000;
+ di_gen |= DI_GEN_POLARITY_5;
+ di_gen |= DI_GEN_POLARITY_8;
+
+ vsync_cnt = 7;
+
+ if (sig->Hsync_pol)
+ di_gen |= DI_GEN_POLARITY_3;
+ if (sig->Vsync_pol)
+ di_gen |= DI_GEN_POLARITY_2;
+ } else {
+ ipu_di_sync_config_noninterlaced(di, sig, div);
+
+ vsync_cnt = 3;
+ if (di->id == 1)
+ /*
+ * TODO: change only for TVEv2, parallel display
+ * uses pin 2 / 3
+ */
+ if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3))
+ vsync_cnt = 6;
+
+ if (sig->Hsync_pol) {
+ if (sig->hsync_pin == 2)
+ di_gen |= DI_GEN_POLARITY_2;
+ else if (sig->hsync_pin == 4)
+ di_gen |= DI_GEN_POLARITY_4;
+ else if (sig->hsync_pin == 7)
+ di_gen |= DI_GEN_POLARITY_7;
+ }
+ if (sig->Vsync_pol) {
+ if (sig->vsync_pin == 3)
+ di_gen |= DI_GEN_POLARITY_3;
+ else if (sig->vsync_pin == 6)
+ di_gen |= DI_GEN_POLARITY_6;
+ else if (sig->vsync_pin == 8)
+ di_gen |= DI_GEN_POLARITY_8;
+ }
+ }
+
+ if (!sig->clk_pol)
+ di_gen |= DI_GEN_POLARITY_DISP_CLK;
+
+ ipu_di_write(di, di_gen, DI_GENERAL);
+
+ ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002,
+ DI_SYNC_AS_GEN);
+
+ reg = ipu_di_read(di, DI_POL);
+ reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15);
+
+ if (sig->enable_pol)
+ reg |= DI_POL_DRDY_POLARITY_15;
+ if (sig->data_pol)
+ reg |= DI_POL_DRDY_DATA_POLARITY;
+
+ ipu_di_write(di, reg, DI_POL);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel);
+
+int ipu_di_enable(struct ipu_di *di)
+{
+ int ret = clk_enable(&di->clk_di_pixel);
+ if (ret)
+ return ret;
+
+ ipu_module_enable(di->ipu, di->module);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_di_enable);
+
+int ipu_di_disable(struct ipu_di *di)
+{
+ ipu_module_disable(di->ipu, di->module);
+
+ clk_disable(&di->clk_di_pixel);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_di_disable);
+
+int ipu_di_get_num(struct ipu_di *di)
+{
+ return di->id;
+}
+EXPORT_SYMBOL_GPL(ipu_di_get_num);
+
+struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp)
+{
+ struct ipu_di *di;
+
+ if (disp > 1)
+ return ERR_PTR(-EINVAL);
+
+ di = ipu->di_priv[disp];
+
+ if (di->inuse) {
+ di = ERR_PTR(-EBUSY);
+ goto out;
+ }
+
+ di->inuse = true;
+out:
+ return di;
+}
+EXPORT_SYMBOL_GPL(ipu_di_get);
+
+void ipu_di_put(struct ipu_di *di)
+{
+ di->inuse = false;
+}
+EXPORT_SYMBOL_GPL(ipu_di_put);
+
+int ipu_di_init(struct ipu_soc *ipu, struct device_d *dev, int id,
+ void __iomem *base,
+ u32 module, struct clk *clk_ipu)
+{
+ struct ipu_di *di;
+ int ret;
+
+ if (id > 1)
+ return -ENODEV;
+
+ di = xzalloc(sizeof(*di));
+
+ ipu->di_priv[id] = di;
+
+ di->clk_di = clk_get(dev, id ? "di1" : "di0");
+ if (IS_ERR(di->clk_di))
+ return PTR_ERR(di->clk_di);
+
+ di->module = module;
+ di->id = id;
+ di->clk_ipu = clk_ipu;
+ di->base = base;
+
+ di->di_parent_names[0] = di->clk_ipu->name;
+ di->di_parent_names[1] = di->clk_di->name;
+
+ ipu_di_write(di, 0x10, DI_BS_CLKGEN0);
+
+ di->clk_di_pixel.parent_names = di->di_parent_names;
+ di->clk_name = asprintf("%s_di%d_pixel",
+ dev_name(dev), id);
+ if (!di->clk_name)
+ return -ENOMEM;
+
+ di->clk_di_pixel.ops = &clk_di_ops;
+ di->clk_di_pixel.num_parents = 2;
+ di->clk_di_pixel.name = di->clk_name;
+ ret = clk_register(&di->clk_di_pixel);
+ if (ret)
+ goto failed_clk_register;
+
+ dev_dbg(dev, "DI%d base: 0x%p\n", id, base);
+ di->inuse = false;
+ di->ipu = ipu;
+
+ return 0;
+
+failed_clk_register:
+
+ free(di->clk_name);
+
+ return ret;
+}
+
+void ipu_di_exit(struct ipu_soc *ipu, int id)
+{
+}
diff --git a/drivers/video/imx-ipu-v3/ipu-dmfc.c b/drivers/video/imx-ipu-v3/ipu-dmfc.c
new file mode 100644
index 0000000000..7b54e25001
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipu-dmfc.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 <common.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+#define DMFC_RD_CHAN 0x0000
+#define DMFC_WR_CHAN 0x0004
+#define DMFC_WR_CHAN_DEF 0x0008
+#define DMFC_DP_CHAN 0x000c
+#define DMFC_DP_CHAN_DEF 0x0010
+#define DMFC_GENERAL1 0x0014
+#define DMFC_GENERAL2 0x0018
+#define DMFC_IC_CTRL 0x001c
+#define DMFC_STAT 0x0020
+
+#define DMFC_WR_CHAN_1_28 0
+#define DMFC_WR_CHAN_2_41 8
+#define DMFC_WR_CHAN_1C_42 16
+#define DMFC_WR_CHAN_2C_43 24
+
+#define DMFC_DP_CHAN_5B_23 0
+#define DMFC_DP_CHAN_5F_27 8
+#define DMFC_DP_CHAN_6B_24 16
+#define DMFC_DP_CHAN_6F_29 24
+
+#define DMFC_FIFO_SIZE_64 (3 << 3)
+#define DMFC_FIFO_SIZE_128 (2 << 3)
+#define DMFC_FIFO_SIZE_256 (1 << 3)
+#define DMFC_FIFO_SIZE_512 (0 << 3)
+
+#define DMFC_SEGMENT(x) ((x & 0x7) << 0)
+#define DMFC_BURSTSIZE_128 (0 << 6)
+#define DMFC_BURSTSIZE_64 (1 << 6)
+#define DMFC_BURSTSIZE_32 (2 << 6)
+#define DMFC_BURSTSIZE_16 (3 << 6)
+
+struct dmfc_channel_data {
+ int ipu_channel;
+ unsigned long channel_reg;
+ unsigned long shift;
+ unsigned eot_shift;
+ unsigned max_fifo_lines;
+};
+
+static const struct dmfc_channel_data dmfcdata[] = {
+ {
+ .ipu_channel = IPUV3_CHANNEL_MEM_BG_SYNC,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_5B_23,
+ .eot_shift = 20,
+ .max_fifo_lines = 3,
+ }, {
+ .ipu_channel = 24,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_6B_24,
+ .eot_shift = 22,
+ .max_fifo_lines = 1,
+ }, {
+ .ipu_channel = IPUV3_CHANNEL_MEM_FG_SYNC,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_5F_27,
+ .eot_shift = 21,
+ .max_fifo_lines = 2,
+ }, {
+ .ipu_channel = IPUV3_CHANNEL_MEM_DC_SYNC,
+ .channel_reg = DMFC_WR_CHAN,
+ .shift = DMFC_WR_CHAN_1_28,
+ .eot_shift = 16,
+ .max_fifo_lines = 2,
+ }, {
+ .ipu_channel = 29,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_6F_29,
+ .eot_shift = 23,
+ .max_fifo_lines = 1,
+ },
+};
+
+#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata)
+
+struct ipu_dmfc_priv;
+
+struct dmfc_channel {
+ unsigned slots;
+ unsigned slotmask;
+ unsigned segment;
+ int burstsize;
+ struct ipu_soc *ipu;
+ struct ipu_dmfc_priv *priv;
+ const struct dmfc_channel_data *data;
+};
+
+struct ipu_dmfc_priv {
+ struct ipu_soc *ipu;
+ struct device_d *dev;
+ struct dmfc_channel channels[DMFC_NUM_CHANNELS];
+ unsigned long bandwidth_per_slot;
+ void __iomem *base;
+ int use_count;
+};
+
+int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+
+ if (!priv->use_count)
+ ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN);
+
+ priv->use_count++;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel);
+
+void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+
+ priv->use_count--;
+
+ if (!priv->use_count)
+ ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN);
+
+ if (priv->use_count < 0)
+ priv->use_count = 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel);
+
+static int ipu_dmfc_setup_channel(struct dmfc_channel *dmfc, int slots,
+ int segment, int burstsize)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ u32 val, field;
+
+ dev_dbg(priv->dev,
+ "dmfc: using %d slots starting from segment %d for IPU channel %d\n",
+ slots, segment, dmfc->data->ipu_channel);
+
+ if (!dmfc)
+ return -EINVAL;
+
+ switch (slots) {
+ case 1:
+ field = DMFC_FIFO_SIZE_64;
+ break;
+ case 2:
+ field = DMFC_FIFO_SIZE_128;
+ break;
+ case 4:
+ field = DMFC_FIFO_SIZE_256;
+ break;
+ case 8:
+ field = DMFC_FIFO_SIZE_512;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (burstsize) {
+ case 16:
+ field |= DMFC_BURSTSIZE_16;
+ break;
+ case 32:
+ field |= DMFC_BURSTSIZE_32;
+ break;
+ case 64:
+ field |= DMFC_BURSTSIZE_64;
+ break;
+ case 128:
+ field |= DMFC_BURSTSIZE_128;
+ break;
+ }
+
+ field |= DMFC_SEGMENT(segment);
+
+ val = ipureadl(priv->base + dmfc->data->channel_reg);
+
+ val &= ~(0xff << dmfc->data->shift);
+ val |= field << dmfc->data->shift;
+
+ ipuwritel("dmfc", val, priv->base + dmfc->data->channel_reg);
+
+ dmfc->slots = slots;
+ dmfc->segment = segment;
+ dmfc->burstsize = burstsize;
+ dmfc->slotmask = ((1 << slots) - 1) << segment;
+
+ return 0;
+}
+
+static int dmfc_bandwidth_to_slots(struct ipu_dmfc_priv *priv,
+ unsigned long bandwidth)
+{
+ int slots = 1;
+
+ while (slots * priv->bandwidth_per_slot < bandwidth)
+ slots *= 2;
+
+ return slots;
+}
+
+static int dmfc_find_slots(struct ipu_dmfc_priv *priv, int slots)
+{
+ unsigned slotmask_need, slotmask_used = 0;
+ int i, segment = 0;
+
+ slotmask_need = (1 << slots) - 1;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ slotmask_used |= priv->channels[i].slotmask;
+
+ while (slotmask_need <= 0xff) {
+ if (!(slotmask_used & slotmask_need))
+ return segment;
+
+ slotmask_need <<= 1;
+ segment++;
+ }
+
+ return -EBUSY;
+}
+
+void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ int i;
+
+ dev_dbg(priv->dev, "dmfc: freeing %d slots starting from segment %d\n",
+ dmfc->slots, dmfc->segment);
+
+ if (!dmfc->slots)
+ return;
+
+ dmfc->slotmask = 0;
+ dmfc->slots = 0;
+ dmfc->segment = 0;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ priv->channels[i].slotmask = 0;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
+ if (priv->channels[i].slots > 0) {
+ priv->channels[i].segment =
+ dmfc_find_slots(priv, priv->channels[i].slots);
+ priv->channels[i].slotmask =
+ ((1 << priv->channels[i].slots) - 1) <<
+ priv->channels[i].segment;
+ }
+ }
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
+ if (priv->channels[i].slots > 0)
+ ipu_dmfc_setup_channel(&priv->channels[i],
+ priv->channels[i].slots,
+ priv->channels[i].segment,
+ priv->channels[i].burstsize);
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_free_bandwidth);
+
+int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
+ unsigned long bandwidth_pixel_per_second, int burstsize)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ int slots = dmfc_bandwidth_to_slots(priv, bandwidth_pixel_per_second);
+ int segment = -1, ret = 0;
+
+ dev_dbg(priv->dev, "dmfc: trying to allocate %ldMpixel/s for IPU channel %d\n",
+ bandwidth_pixel_per_second / 1000000,
+ dmfc->data->ipu_channel);
+
+ ipu_dmfc_free_bandwidth(dmfc);
+
+ if (slots > 8) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* For the MEM_BG channel, first try to allocate twice the slots */
+ if (dmfc->data->ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC)
+ segment = dmfc_find_slots(priv, slots * 2);
+ else if (slots < 2)
+ /* Always allocate at least 128*4 bytes (2 slots) */
+ slots = 2;
+
+ if (segment >= 0)
+ slots *= 2;
+ else
+ segment = dmfc_find_slots(priv, slots);
+ if (segment < 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ipu_dmfc_setup_channel(dmfc, slots, segment, burstsize);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_alloc_bandwidth);
+
+int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ u32 dmfc_gen1;
+
+ dmfc_gen1 = ipureadl(priv->base + DMFC_GENERAL1);
+
+ if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines)
+ dmfc_gen1 |= 1 << dmfc->data->eot_shift;
+ else
+ dmfc_gen1 &= ~(1 << dmfc->data->eot_shift);
+
+ ipuwritel("dmfc", dmfc_gen1, priv->base + DMFC_GENERAL1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_init_channel);
+
+struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel)
+{
+ struct ipu_dmfc_priv *priv = ipu->dmfc_priv;
+ int i;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ if (dmfcdata[i].ipu_channel == ipu_channel)
+ return &priv->channels[i];
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_get);
+
+void ipu_dmfc_put(struct dmfc_channel *dmfc)
+{
+ ipu_dmfc_free_bandwidth(dmfc);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_put);
+
+int ipu_dmfc_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base,
+ struct clk *ipu_clk)
+{
+ struct ipu_dmfc_priv *priv;
+ int i;
+
+ priv = xzalloc(sizeof(*priv));
+
+ priv->base = base;
+
+ priv->dev = dev;
+ priv->ipu = ipu;
+
+ ipu->dmfc_priv = priv;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
+ priv->channels[i].priv = priv;
+ priv->channels[i].ipu = ipu;
+ priv->channels[i].data = &dmfcdata[i];
+ }
+
+ ipuwritel("dmfc", 0x0, priv->base + DMFC_WR_CHAN);
+ ipuwritel("dmfc", 0x0, priv->base + DMFC_DP_CHAN);
+
+ /*
+ * We have a total bandwidth of clkrate * 4pixel divided
+ * into 8 slots.
+ */
+ priv->bandwidth_per_slot = clk_get_rate(ipu_clk) * 4 / 8;
+
+ dev_dbg(dev, "dmfc: 8 slots with %ldMpixel/s bandwidth each\n",
+ priv->bandwidth_per_slot / 1000000);
+
+ ipuwritel("dmfc", 0x202020f6, priv->base + DMFC_WR_CHAN_DEF);
+ ipuwritel("dmfc", 0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF);
+ ipuwritel("dmfc", 0x00000003, priv->base + DMFC_GENERAL1);
+
+ return 0;
+}
+
+void ipu_dmfc_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/video/imx-ipu-v3/ipu-dp.c b/drivers/video/imx-ipu-v3/ipu-dp.c
new file mode 100644
index 0000000000..8829954db0
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipu-dp.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 <common.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <malloc.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+#define DP_SYNC 0
+#define DP_ASYNC0 0x60
+#define DP_ASYNC1 0xBC
+
+#define DP_COM_CONF 0x0
+#define DP_GRAPH_WIND_CTRL 0x0004
+#define DP_FG_POS 0x0008
+#define DP_CSC_A_0 0x0044
+#define DP_CSC_A_1 0x0048
+#define DP_CSC_A_2 0x004C
+#define DP_CSC_A_3 0x0050
+#define DP_CSC_0 0x0054
+#define DP_CSC_1 0x0058
+
+#define DP_COM_CONF_FG_EN (1 << 0)
+#define DP_COM_CONF_GWSEL (1 << 1)
+#define DP_COM_CONF_GWAM (1 << 2)
+#define DP_COM_CONF_GWCKE (1 << 3)
+#define DP_COM_CONF_CSC_DEF_MASK (3 << 8)
+#define DP_COM_CONF_CSC_DEF_OFFSET 8
+#define DP_COM_CONF_CSC_DEF_FG (3 << 8)
+#define DP_COM_CONF_CSC_DEF_BG (2 << 8)
+#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8)
+
+#define IPUV3_NUM_FLOWS 3
+
+struct ipu_dp_priv;
+
+struct ipu_dp {
+ u32 flow;
+ bool in_use;
+ bool foreground;
+ enum ipu_color_space in_cs;
+};
+
+struct ipu_flow {
+ struct ipu_dp foreground;
+ struct ipu_dp background;
+ enum ipu_color_space out_cs;
+ void __iomem *base;
+ struct ipu_dp_priv *priv;
+};
+
+struct ipu_dp_priv {
+ struct ipu_soc *ipu;
+ struct device_d *dev;
+ void __iomem *base;
+ struct ipu_flow flow[IPUV3_NUM_FLOWS];
+ int use_count;
+};
+
+static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
+
+static inline struct ipu_flow *to_flow(struct ipu_dp *dp)
+{
+ if (dp->foreground)
+ return container_of(dp, struct ipu_flow, foreground);
+ else
+ return container_of(dp, struct ipu_flow, background);
+}
+
+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable,
+ u8 alpha, bool bg_chan)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+ u32 reg;
+
+ reg = ipureadl(flow->base + DP_COM_CONF);
+ if (bg_chan)
+ reg &= ~DP_COM_CONF_GWSEL;
+ else
+ reg |= DP_COM_CONF_GWSEL;
+ ipuwritel("dp", reg, flow->base + DP_COM_CONF);
+
+ if (enable) {
+ reg = ipureadl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL;
+ ipuwritel("dp", reg | ((u32) alpha << 24),
+ flow->base + DP_GRAPH_WIND_CTRL);
+
+ reg = ipureadl(flow->base + DP_COM_CONF);
+ ipuwritel("dp", reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
+ } else {
+ reg = ipureadl(flow->base + DP_COM_CONF);
+ ipuwritel("dp", reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
+ }
+
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha);
+
+int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ ipuwritel("dp", (x_pos << 16) | y_pos, flow->base + DP_FG_POS);
+
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos);
+
+static void ipu_dp_csc_init(struct ipu_flow *flow,
+ enum ipu_color_space in,
+ enum ipu_color_space out,
+ u32 place)
+{
+ u32 reg;
+
+ reg = ipureadl(flow->base + DP_COM_CONF);
+ reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+
+ if (in == out) {
+ ipuwritel("dp", reg, flow->base + DP_COM_CONF);
+ return;
+ }
+
+ if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) {
+ ipuwritel("dp", 0x099 | (0x12d << 16), flow->base + DP_CSC_A_0);
+ ipuwritel("dp", 0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1);
+ ipuwritel("dp", 0x356 | (0x100 << 16), flow->base + DP_CSC_A_2);
+ ipuwritel("dp", 0x100 | (0x329 << 16), flow->base + DP_CSC_A_3);
+ ipuwritel("dp", 0x3d6 | (0x0000 << 16) | (2 << 30),
+ flow->base + DP_CSC_0);
+ ipuwritel("dp", 0x200 | (2 << 14) | (0x200 << 16) | (2 << 30),
+ flow->base + DP_CSC_1);
+ } else {
+ ipuwritel("dp", 0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
+ ipuwritel("dp", 0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1);
+ ipuwritel("dp", 0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2);
+ ipuwritel("dp", 0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3);
+ ipuwritel("dp", 0x000 | (0x3e42 << 16) | (1 << 30),
+ flow->base + DP_CSC_0);
+ ipuwritel("dp", 0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30),
+ flow->base + DP_CSC_1);
+ }
+
+ reg |= place;
+
+ ipuwritel("dp", reg, flow->base + DP_COM_CONF);
+}
+
+int ipu_dp_setup_channel(struct ipu_dp *dp,
+ enum ipu_color_space in,
+ enum ipu_color_space out)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ dp->in_cs = in;
+
+ if (!dp->foreground)
+ flow->out_cs = out;
+
+ if (flow->foreground.in_cs == flow->background.in_cs) {
+ /*
+ * foreground and background are of same colorspace, put
+ * colorspace converter after combining unit.
+ */
+ ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs,
+ DP_COM_CONF_CSC_DEF_BOTH);
+ } else {
+ if (flow->foreground.in_cs == flow->out_cs)
+ /*
+ * foreground identical to output, apply color
+ * conversion on background
+ */
+ ipu_dp_csc_init(flow, flow->background.in_cs,
+ flow->out_cs, DP_COM_CONF_CSC_DEF_BG);
+ else
+ ipu_dp_csc_init(flow, flow->foreground.in_cs,
+ flow->out_cs, DP_COM_CONF_CSC_DEF_FG);
+ }
+
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_setup_channel);
+
+int ipu_dp_enable_channel(struct ipu_dp *dp)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ if (!priv->use_count)
+ ipu_module_enable(priv->ipu, IPU_CONF_DP_EN);
+
+ priv->use_count++;
+
+ if (dp->foreground) {
+ u32 reg;
+
+ reg = ipureadl(flow->base + DP_COM_CONF);
+ reg |= DP_COM_CONF_FG_EN;
+ ipuwritel("dp", reg, flow->base + DP_COM_CONF);
+
+ ipu_srm_dp_sync_update(priv->ipu);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_enable_channel);
+
+void ipu_dp_disable_channel(struct ipu_dp *dp)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ priv->use_count--;
+
+ if (dp->foreground) {
+ u32 reg, csc;
+
+ reg = ipureadl(flow->base + DP_COM_CONF);
+ csc = reg & DP_COM_CONF_CSC_DEF_MASK;
+ if (csc == DP_COM_CONF_CSC_DEF_FG)
+ reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+
+ reg &= ~DP_COM_CONF_FG_EN;
+ ipuwritel("dp", reg, flow->base + DP_COM_CONF);
+
+ ipuwritel("dp", 0, flow->base + DP_FG_POS);
+ ipu_srm_dp_sync_update(priv->ipu);
+ }
+
+ if (!priv->use_count)
+ ipu_module_disable(priv->ipu, IPU_CONF_DP_EN);
+
+ if (priv->use_count < 0)
+ priv->use_count = 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
+
+struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow)
+{
+ struct ipu_dp_priv *priv = ipu->dp_priv;
+ struct ipu_dp *dp;
+
+ if ((flow >> 1) >= IPUV3_NUM_FLOWS)
+ return ERR_PTR(-EINVAL);
+
+ if (flow & 1)
+ dp = &priv->flow[flow >> 1].foreground;
+ else
+ dp = &priv->flow[flow >> 1].background;
+
+ if (dp->in_use)
+ return ERR_PTR(-EBUSY);
+
+ dp->in_use = true;
+
+ return dp;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_get);
+
+void ipu_dp_put(struct ipu_dp *dp)
+{
+ dp->in_use = false;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_put);
+
+int ipu_dp_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base)
+{
+ struct ipu_dp_priv *priv;
+ int i;
+
+ priv = xzalloc(sizeof(*priv));
+ priv->dev = dev;
+ priv->ipu = ipu;
+
+ ipu->dp_priv = priv;
+
+ priv->base = base;
+
+ for (i = 0; i < IPUV3_NUM_FLOWS; i++) {
+ priv->flow[i].foreground.foreground = true;
+ priv->flow[i].base = priv->base + ipu_dp_flow_base[i];
+ priv->flow[i].priv = priv;
+ }
+
+ return 0;
+}
+
+void ipu_dp_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/video/imx-ipu-v3/ipu-prv.h b/drivers/video/imx-ipu-v3/ipu-prv.h
new file mode 100644
index 0000000000..44d7802521
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipu-prv.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#ifndef __IPU_PRV_H__
+#define __IPU_PRV_H__
+
+struct ipu_soc;
+
+#include "imx-ipu-v3.h"
+
+#define IPU_PIX_FMT_GBR24 v4l2_fourcc('G', 'B', 'R', '3')
+
+#define IPUV3_CHANNEL_CSI0 0
+#define IPUV3_CHANNEL_CSI1 1
+#define IPUV3_CHANNEL_CSI2 2
+#define IPUV3_CHANNEL_CSI3 3
+#define IPUV3_CHANNEL_MEM_BG_SYNC 23
+#define IPUV3_CHANNEL_MEM_FG_SYNC 27
+#define IPUV3_CHANNEL_MEM_DC_SYNC 28
+#define IPUV3_CHANNEL_MEM_FG_SYNC_ALPHA 31
+#define IPUV3_CHANNEL_MEM_DC_ASYNC 41
+#define IPUV3_CHANNEL_ROT_ENC_MEM 45
+#define IPUV3_CHANNEL_ROT_VF_MEM 46
+#define IPUV3_CHANNEL_ROT_PP_MEM 47
+#define IPUV3_CHANNEL_ROT_ENC_MEM_OUT 48
+#define IPUV3_CHANNEL_ROT_VF_MEM_OUT 49
+#define IPUV3_CHANNEL_ROT_PP_MEM_OUT 50
+#define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA 51
+
+#define IPU_MCU_T_DEFAULT 8
+#define IPU_CM_IDMAC_REG_OFS 0x00008000
+#define IPU_CM_IC_REG_OFS 0x00020000
+#define IPU_CM_IRT_REG_OFS 0x00028000
+#define IPU_CM_CSI0_REG_OFS 0x00030000
+#define IPU_CM_CSI1_REG_OFS 0x00038000
+#define IPU_CM_SMFC_REG_OFS 0x00050000
+#define IPU_CM_DC_REG_OFS 0x00058000
+#define IPU_CM_DMFC_REG_OFS 0x00060000
+
+/* Register addresses */
+/* IPU Common registers */
+#define IPU_CM_REG(offset) (offset)
+
+#define IPU_CONF IPU_CM_REG(0)
+
+#define IPU_SRM_PRI1 IPU_CM_REG(0x00a0)
+#define IPU_SRM_PRI2 IPU_CM_REG(0x00a4)
+#define IPU_FS_PROC_FLOW1 IPU_CM_REG(0x00a8)
+#define IPU_FS_PROC_FLOW2 IPU_CM_REG(0x00ac)
+#define IPU_FS_PROC_FLOW3 IPU_CM_REG(0x00b0)
+#define IPU_FS_DISP_FLOW1 IPU_CM_REG(0x00b4)
+#define IPU_FS_DISP_FLOW2 IPU_CM_REG(0x00b8)
+#define IPU_SKIP IPU_CM_REG(0x00bc)
+#define IPU_DISP_ALT_CONF IPU_CM_REG(0x00c0)
+#define IPU_DISP_GEN IPU_CM_REG(0x00c4)
+#define IPU_DISP_ALT1 IPU_CM_REG(0x00c8)
+#define IPU_DISP_ALT2 IPU_CM_REG(0x00cc)
+#define IPU_DISP_ALT3 IPU_CM_REG(0x00d0)
+#define IPU_DISP_ALT4 IPU_CM_REG(0x00d4)
+#define IPU_SNOOP IPU_CM_REG(0x00d8)
+#define IPU_MEM_RST IPU_CM_REG(0x00dc)
+#define IPU_PM IPU_CM_REG(0x00e0)
+#define IPU_GPR IPU_CM_REG(0x00e4)
+#define IPU_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0150 + 4 * ((ch) / 32))
+#define IPU_ALT_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0168 + 4 * ((ch) / 32))
+#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32))
+#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244)
+#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248)
+#define IPU_SRM_STAT IPU_CM_REG(0x024C)
+#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250)
+#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254)
+#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32))
+#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32))
+#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32))
+#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32))
+
+#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n))
+#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n))
+
+#define IPU_DI0_COUNTER_RELEASE (1 << 24)
+#define IPU_DI1_COUNTER_RELEASE (1 << 25)
+
+#define IPU_IDMAC_REG(offset) (offset)
+
+#define IDMAC_CONF IPU_IDMAC_REG(0x0000)
+#define IDMAC_CHA_EN(ch) IPU_IDMAC_REG(0x0004 + 4 * ((ch) / 32))
+#define IDMAC_SEP_ALPHA IPU_IDMAC_REG(0x000c)
+#define IDMAC_ALT_SEP_ALPHA IPU_IDMAC_REG(0x0010)
+#define IDMAC_CHA_PRI(ch) IPU_IDMAC_REG(0x0014 + 4 * ((ch) / 32))
+#define IDMAC_WM_EN(ch) IPU_IDMAC_REG(0x001c + 4 * ((ch) / 32))
+#define IDMAC_CH_LOCK_EN_1 IPU_IDMAC_REG(0x0024)
+#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028)
+#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c)
+#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030)
+#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034)
+#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32))
+#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32))
+
+#define IPU_NUM_IRQS (32 * 15)
+
+enum ipu_modules {
+ IPU_CONF_CSI0_EN = (1 << 0),
+ IPU_CONF_CSI1_EN = (1 << 1),
+ IPU_CONF_IC_EN = (1 << 2),
+ IPU_CONF_ROT_EN = (1 << 3),
+ IPU_CONF_ISP_EN = (1 << 4),
+ IPU_CONF_DP_EN = (1 << 5),
+ IPU_CONF_DI0_EN = (1 << 6),
+ IPU_CONF_DI1_EN = (1 << 7),
+ IPU_CONF_SMFC_EN = (1 << 8),
+ IPU_CONF_DC_EN = (1 << 9),
+ IPU_CONF_DMFC_EN = (1 << 10),
+
+ IPU_CONF_VDI_EN = (1 << 12),
+
+ IPU_CONF_IDMAC_DIS = (1 << 22),
+
+ IPU_CONF_IC_DMFC_SEL = (1 << 25),
+ IPU_CONF_IC_DMFC_SYNC = (1 << 26),
+ IPU_CONF_VDI_DMFC_SYNC = (1 << 27),
+
+ IPU_CONF_CSI0_DATA_SOURCE = (1 << 28),
+ IPU_CONF_CSI1_DATA_SOURCE = (1 << 29),
+ IPU_CONF_IC_INPUT = (1 << 30),
+ IPU_CONF_CSI_SEL = (1 << 31),
+};
+
+struct ipuv3_channel {
+ unsigned int num;
+
+ bool enabled;
+ bool busy;
+
+ struct ipu_soc *ipu;
+};
+
+struct ipu_dc_priv;
+struct ipu_dmfc_priv;
+struct ipu_di;
+struct ipu_devtype;
+
+struct ipu_soc {
+ struct device_d *dev;
+ const struct ipu_devtype *devtype;
+ enum ipuv3_type ipu_type;
+ spinlock_t lock;
+ struct mutex channel_lock;
+
+ void __iomem *base;
+ void __iomem *cm_reg;
+ void __iomem *idmac_reg;
+ struct ipu_ch_param __iomem *cpmem_base;
+
+ int usecount;
+
+ struct clk *clk;
+
+ struct ipuv3_channel channel[64];
+
+ int irq_sync;
+ int irq_err;
+ struct irq_domain *domain;
+
+ struct ipu_dc_priv *dc_priv;
+ struct ipu_dp_priv *dp_priv;
+ struct ipu_dmfc_priv *dmfc_priv;
+ struct ipu_di *di_priv[2];
+};
+
+void ipu_srm_dp_sync_update(struct ipu_soc *ipu);
+
+int ipu_module_enable(struct ipu_soc *ipu, u32 mask);
+int ipu_module_disable(struct ipu_soc *ipu, u32 mask);
+
+int ipu_di_init(struct ipu_soc *ipu, struct device_d *dev, int id,
+ void __iomem *base, u32 module, struct clk *ipu_clk);
+void ipu_di_exit(struct ipu_soc *ipu, int id);
+
+int ipu_dmfc_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base,
+ struct clk *ipu_clk);
+void ipu_dmfc_exit(struct ipu_soc *ipu);
+
+int ipu_dp_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base);
+void ipu_dp_exit(struct ipu_soc *ipu);
+
+int ipu_dc_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base,
+ void __iomem *template_base);
+void ipu_dc_exit(struct ipu_soc *ipu);
+
+int ipu_cpmem_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base);
+void ipu_cpmem_exit(struct ipu_soc *ipu);
+
+#endif /* __IPU_PRV_H__ */
diff --git a/drivers/video/imx-ipu-v3/ipufb.c b/drivers/video/imx-ipu-v3/ipufb.c
new file mode 100644
index 0000000000..14a099e14a
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipufb.c
@@ -0,0 +1,353 @@
+/*
+ * Freescale i.MX Frame Buffer device driver
+ *
+ * Copyright (C) 2004 Sascha Hauer, Pengutronix
+ * Based on acornfb.c Copyright (C) Russell King.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ */
+#define pr_fmt(fmt) "IPU: " fmt
+
+#include <common.h>
+#include <fb.h>
+#include <io.h>
+#include <driver.h>
+#include <malloc.h>
+#include <errno.h>
+#include <init.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <asm-generic/div64.h>
+#include <asm/mmu.h>
+
+#include "imx-ipu-v3.h"
+#include "ipuv3-plane.h"
+
+/*
+ * These are the bitfields for each
+ * display depth that we support.
+ */
+struct ipufb_rgb {
+ struct fb_bitfield red;
+ struct fb_bitfield green;
+ struct fb_bitfield blue;
+ struct fb_bitfield transp;
+};
+
+struct ipufb_info {
+ void __iomem *regs;
+
+ struct clk *ahb_clk;
+ struct clk *ipg_clk;
+ struct clk *per_clk;
+
+ struct fb_videomode *mode;
+
+ struct fb_info info;
+ struct device_d *dev;
+
+ /* plane[0] is the full plane, plane[1] is the partial plane */
+ struct ipu_plane *plane[2];
+
+ void (*enable)(int enable);
+
+ unsigned int di_clkflags;
+ u32 interface_pix_fmt;
+ struct ipu_dc *dc;
+ struct ipu_di *di;
+
+ struct list_head list;
+ char *name;
+ int id;
+
+ struct ipu_output *output;
+};
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static LIST_HEAD(ipu_outputs);
+static LIST_HEAD(ipu_fbs);
+
+int ipu_register_output(struct ipu_output *ouput)
+{
+ list_add_tail(&ouput->list, &ipu_outputs);
+
+ return 0;
+}
+
+static int ipu_register_fb(struct ipufb_info *ipufb)
+{
+ list_add_tail(&ipufb->list, &ipu_fbs);
+
+ return 0;
+}
+
+int ipu_crtc_mode_set(struct ipufb_info *fbi,
+ struct fb_videomode *mode,
+ int x, int y)
+{
+ struct fb_info *info = &fbi->info;
+ int ret;
+ struct ipu_di_signal_cfg sig_cfg = {};
+ u32 out_pixel_fmt;
+
+ dev_info(fbi->dev, "%s: mode->xres: %d\n", __func__,
+ mode->xres);
+ dev_info(fbi->dev, "%s: mode->yres: %d\n", __func__,
+ mode->yres);
+
+ out_pixel_fmt = fbi->output->out_pixel_fmt;
+
+ if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
+ sig_cfg.Hsync_pol = 1;
+ if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
+ sig_cfg.Vsync_pol = 1;
+
+ sig_cfg.enable_pol = 1;
+ sig_cfg.clk_pol = 1;
+ sig_cfg.width = mode->xres;
+ sig_cfg.height = mode->yres;
+ sig_cfg.pixel_fmt = out_pixel_fmt;
+ sig_cfg.h_start_width = mode->left_margin;
+ sig_cfg.h_sync_width = mode->hsync_len;
+ sig_cfg.h_end_width = mode->right_margin;
+
+ sig_cfg.v_start_width = mode->upper_margin;
+ sig_cfg.v_sync_width = mode->vsync_len;
+ sig_cfg.v_end_width = mode->lower_margin;
+ sig_cfg.pixelclock = PICOS2KHZ(mode->pixclock) * 1000UL;
+ sig_cfg.clkflags = fbi->output->di_clkflags;
+
+ sig_cfg.v_to_h_sync = 0;
+
+ sig_cfg.hsync_pin = 2;
+ sig_cfg.vsync_pin = 3;
+
+ ret = ipu_dc_init_sync(fbi->dc, fbi->di, sig_cfg.interlaced,
+ out_pixel_fmt, mode->xres);
+ if (ret) {
+ dev_err(fbi->dev,
+ "initializing display controller failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ipu_di_init_sync_panel(fbi->di, &sig_cfg);
+ if (ret) {
+ dev_err(fbi->dev,
+ "initializing panel failed with %d\n", ret);
+ return ret;
+ }
+
+ ret = ipu_plane_mode_set(fbi->plane[0], mode, info, DRM_FORMAT_XRGB8888,
+ 0, 0, mode->xres, mode->yres,
+ x, y, mode->xres, mode->yres);
+ if (ret) {
+ dev_err(fbi->dev, "initialising plane failed with %s\n", strerror(-ret));
+ return ret;
+ }
+
+ ret = ipu_di_enable(fbi->di);
+ if (ret) {
+ dev_err(fbi->dev, "enabling di failed with %s\n", strerror(-ret));
+ return ret;
+ }
+
+ ipu_dc_enable_channel(fbi->dc);
+
+ ipu_plane_enable(fbi->plane[0]);
+
+ return 0;
+}
+
+static void ipufb_enable_controller(struct fb_info *info)
+{
+ struct ipufb_info *fbi = container_of(info, struct ipufb_info, info);
+ struct ipu_output *output = fbi->output;
+
+ if (output->ops->prepare)
+ output->ops->prepare(output, info->mode, fbi->id);
+
+ ipu_crtc_mode_set(fbi, info->mode, 0, 0);
+
+ if (output->ops->enable)
+ output->ops->enable(output, info->mode, fbi->id);
+}
+
+static void ipufb_disable_controller(struct fb_info *info)
+{
+ struct ipufb_info *fbi = container_of(info, struct ipufb_info, info);
+ struct ipu_output *output = fbi->output;
+
+ if (output->ops->disable)
+ output->ops->disable(output);
+
+ ipu_plane_disable(fbi->plane[0]);
+ ipu_dc_disable_channel(fbi->dc);
+ ipu_di_disable(fbi->di);
+
+ if (output->ops->unprepare)
+ output->ops->unprepare(output);
+}
+
+static int ipufb_activate_var(struct fb_info *info)
+{
+ struct ipufb_info *fbi = container_of(info, struct ipufb_info, info);
+
+ info->line_length = info->xres * (info->bits_per_pixel >> 3);
+ fbi->info.screen_base = dma_alloc_coherent(info->line_length * info->yres);
+ if (!fbi->info.screen_base)
+ return -ENOMEM;
+
+ memset(fbi->info.screen_base, 0, info->line_length * info->yres);
+
+ return 0;
+}
+
+static struct fb_ops ipufb_ops = {
+ .fb_enable = ipufb_enable_controller,
+ .fb_disable = ipufb_disable_controller,
+ .fb_activate_var = ipufb_activate_var,
+};
+
+static struct ipufb_info *ipu_output_find_di(struct ipu_output *output)
+{
+ struct ipufb_info *ipufb;
+
+ list_for_each_entry(ipufb, &ipu_fbs, list) {
+ if (!(output->ipu_mask & (1 << ipufb->id)))
+ continue;
+ if (ipufb->output)
+ continue;
+
+ return ipufb;
+ }
+
+ return NULL;
+}
+
+static int ipu_init(void)
+{
+ struct ipu_output *output;
+ struct ipufb_info *ipufb;
+ int ret;
+
+ list_for_each_entry(output, &ipu_outputs, list) {
+ pr_info("found output: %s\n", output->name);
+ ipufb = ipu_output_find_di(output);
+ if (!ipufb) {
+ pr_info("no di found for output %s\n", output->name);
+ continue;
+ }
+ pr_info("using di %s for output %s\n", ipufb->name, output->name);
+
+ ipufb->output = output;
+
+ ipufb->info.edid_i2c_adapter = output->edid_i2c_adapter;
+ if (output->modes) {
+ ipufb->info.modes.modes = output->modes->modes;
+ ipufb->info.modes.num_modes = output->modes->num_modes;
+ }
+
+ ret = register_framebuffer(&ipufb->info);
+ if (ret < 0) {
+ dev_err(ipufb->dev, "failed to register framebuffer\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+late_initcall(ipu_init);
+
+static int ipu_get_resources(struct ipufb_info *fbi,
+ struct ipu_client_platformdata *pdata)
+{
+ struct ipu_soc *ipu = fbi->dev->parent->priv;
+ int ret;
+ int dp = -EINVAL;
+
+ fbi->dc = ipu_dc_get(ipu, pdata->dc);
+ if (IS_ERR(fbi->dc)) {
+ ret = PTR_ERR(fbi->dc);
+ goto err_out;
+ }
+
+ fbi->di = ipu_di_get(ipu, pdata->di);
+ if (IS_ERR(fbi->di)) {
+ ret = PTR_ERR(fbi->di);
+ goto err_out;
+ }
+
+ if (pdata->dp >= 0)
+ dp = IPU_DP_FLOW_SYNC_BG;
+
+ fbi->plane[0] = ipu_plane_init(ipu, pdata->dma[0], dp);
+ ret = ipu_plane_get_resources(fbi->plane[0]);
+ if (ret) {
+ dev_err(fbi->dev, "getting plane 0 resources failed with %d.\n",
+ ret);
+ goto err_out;
+ }
+
+ return 0;
+err_out:
+ return ret;
+}
+
+static int ipufb_probe(struct device_d *dev)
+{
+ struct ipufb_info *fbi;
+ struct fb_info *info;
+ int ret, ipuid;
+ struct ipu_client_platformdata *pdata = dev->platform_data;
+ struct ipu_rgb *ipu_rgb;
+
+ fbi = xzalloc(sizeof(*fbi));
+ info = &fbi->info;
+
+ ipuid = of_alias_get_id(dev->parent->device_node, "ipu");
+ fbi->name = asprintf("ipu%d-di%d", ipuid + 1, pdata->di);
+ fbi->id = ipuid * 2 + pdata->di;
+
+ fbi->dev = dev;
+ info->priv = fbi;
+ info->fbops = &ipufb_ops;
+
+ ipu_rgb = drm_fourcc_to_rgb(DRM_FORMAT_RGB888);
+
+ info->bits_per_pixel = 32;
+ info->red = ipu_rgb->red;
+ info->green = ipu_rgb->green;
+ info->blue = ipu_rgb->blue;
+ info->transp = ipu_rgb->transp;
+
+ dev_dbg(dev, "i.MX Framebuffer driver\n");
+
+ ret = ipu_get_resources(fbi, pdata);
+ if (ret)
+ return ret;
+
+ ret = ipu_register_fb(fbi);
+
+ return ret;
+}
+
+static void ipufb_remove(struct device_d *dev)
+{
+}
+
+static struct driver_d ipufb_driver = {
+ .name = "imx-ipuv3-crtc",
+ .probe = ipufb_probe,
+ .remove = ipufb_remove,
+};
+device_platform_driver(ipufb_driver);
diff --git a/drivers/video/imx-ipu-v3/ipuv3-plane.c b/drivers/video/imx-ipu-v3/ipuv3-plane.c
new file mode 100644
index 0000000000..9dffcfc670
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipuv3-plane.c
@@ -0,0 +1,239 @@
+/*
+ * i.MX IPUv3 DP Overlay Planes
+ *
+ * Copyright (C) 2013 Philipp Zabel, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 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 <common.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <malloc.h>
+#include <fb.h>
+
+#include "ipuv3-plane.h"
+#include "imx-ipu-v3.h"
+
+static int calc_vref(struct fb_videomode *mode)
+{
+ unsigned long htotal, vtotal;
+
+ htotal = mode->left_margin + mode->right_margin + mode->xres + mode->hsync_len;
+ vtotal = mode->upper_margin + mode->lower_margin + mode->yres + mode->vsync_len;
+
+ return DIV_ROUND_UP(PICOS2KHZ(mode->pixclock) * 1000, vtotal * htotal);
+}
+
+static inline int calc_bandwidth(int width, int height, unsigned int vref)
+{
+ return width * height * vref;
+}
+
+int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct fb_info *info,
+ int x, int y)
+{
+ struct ipu_ch_param __iomem *cpmem;
+
+ pr_debug("phys = 0x%p, x = %d, y = %d, line_length = %d\n",
+ info->screen_base, x, y, info->line_length);
+
+ cpmem = ipu_get_cpmem(ipu_plane->ipu_ch);
+ ipu_cpmem_set_stride(cpmem, info->line_length);
+ ipu_cpmem_set_buffer(cpmem, 0, (unsigned long)info->screen_base +
+ info->line_length * y + x);
+ ipu_cpmem_set_buffer(cpmem, 1, (unsigned long)info->screen_base +
+ info->line_length * y + x);
+
+ return 0;
+}
+
+int ipu_plane_mode_set(struct ipu_plane *ipu_plane,
+ struct fb_videomode *mode,
+ struct fb_info *info, u32 pixel_format,
+ int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
+{
+ struct ipu_ch_param __iomem *cpmem;
+ struct device_d *dev = &info->dev;
+ int ret;
+
+ /* no scaling */
+ if (src_w != crtc_w || src_h != crtc_h)
+ return -EINVAL;
+
+ /* clip to crtc bounds */
+ if (crtc_x < 0) {
+ if (-crtc_x > crtc_w)
+ return -EINVAL;
+ src_x += -crtc_x;
+ src_w -= -crtc_x;
+ crtc_w -= -crtc_x;
+ crtc_x = 0;
+ }
+ if (crtc_y < 0) {
+ if (-crtc_y > crtc_h)
+ return -EINVAL;
+ src_y += -crtc_y;
+ src_h -= -crtc_y;
+ crtc_h -= -crtc_y;
+ crtc_y = 0;
+ }
+ if (crtc_x + crtc_w > mode->xres) {
+ if (crtc_x > mode->xres)
+ return -EINVAL;
+ crtc_w = mode->xres - crtc_x;
+ src_w = crtc_w;
+ }
+ if (crtc_y + crtc_h > mode->yres) {
+ if (crtc_y > mode->yres)
+ return -EINVAL;
+ crtc_h = mode->yres - crtc_y;
+ src_h = crtc_h;
+ }
+ /* full plane minimum width is 13 pixels */
+ if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG))
+ return -EINVAL;
+ if (crtc_h < 2)
+ return -EINVAL;
+
+ switch (ipu_plane->dp_flow) {
+ case IPU_DP_FLOW_SYNC_BG:
+ ret = ipu_dp_setup_channel(ipu_plane->dp,
+ IPUV3_COLORSPACE_RGB,
+ IPUV3_COLORSPACE_RGB);
+ if (ret) {
+ dev_err(dev,
+ "initializing display processor failed with %d\n",
+ ret);
+ return ret;
+ }
+ ipu_dp_set_global_alpha(ipu_plane->dp, 1, 0, 1);
+ break;
+ case IPU_DP_FLOW_SYNC_FG:
+ ipu_dp_setup_channel(ipu_plane->dp,
+ IPUV3_COLORSPACE_RGB,
+ IPUV3_COLORSPACE_UNKNOWN);
+ ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y);
+ break;
+ }
+
+ ret = ipu_dmfc_init_channel(ipu_plane->dmfc, crtc_w);
+ if (ret) {
+ dev_err(dev, "initializing dmfc channel failed with %d\n", ret);
+ return ret;
+ }
+
+ ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc,
+ calc_bandwidth(crtc_w, crtc_h,
+ calc_vref(mode)), 64);
+ if (ret) {
+ dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret);
+ return ret;
+ }
+
+ cpmem = ipu_get_cpmem(ipu_plane->ipu_ch);
+ ipu_ch_param_zero(cpmem);
+ ipu_cpmem_set_resolution(cpmem, src_w, src_h);
+ ret = ipu_cpmem_set_fmt(cpmem, pixel_format);
+ if (ret < 0) {
+ dev_err(dev, "unsupported pixel format 0x%08x\n",
+ pixel_format);
+ return ret;
+ }
+ ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
+
+ ret = ipu_plane_set_base(ipu_plane, info, src_x, src_y);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
+{
+ if (!IS_ERR_OR_NULL(ipu_plane->dp))
+ ipu_dp_put(ipu_plane->dp);
+ if (!IS_ERR_OR_NULL(ipu_plane->dmfc))
+ ipu_dmfc_put(ipu_plane->dmfc);
+ if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch))
+ ipu_idmac_put(ipu_plane->ipu_ch);
+}
+
+int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
+{
+ int ret;
+
+ ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma);
+ if (IS_ERR(ipu_plane->ipu_ch)) {
+ ret = PTR_ERR(ipu_plane->ipu_ch);
+ pr_err("failed to get idmac channel: %d\n", ret);
+ return ret;
+ }
+
+ ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma);
+ if (IS_ERR(ipu_plane->dmfc)) {
+ ret = PTR_ERR(ipu_plane->dmfc);
+ pr_err("failed to get dmfc: ret %d\n", ret);
+ goto err_out;
+ }
+
+ if (ipu_plane->dp_flow >= 0) {
+ ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow);
+ if (IS_ERR(ipu_plane->dp)) {
+ ret = PTR_ERR(ipu_plane->dp);
+ pr_err("failed to get dp flow: %d\n", ret);
+ goto err_out;
+ }
+ }
+
+ return 0;
+err_out:
+ ipu_plane_put_resources(ipu_plane);
+
+ return ret;
+}
+
+void ipu_plane_enable(struct ipu_plane *ipu_plane)
+{
+ ipu_dmfc_enable_channel(ipu_plane->dmfc);
+ ipu_idmac_enable_channel(ipu_plane->ipu_ch);
+ if (ipu_plane->dp)
+ ipu_dp_enable_channel(ipu_plane->dp);
+
+ ipu_plane->enabled = true;
+}
+
+void ipu_plane_disable(struct ipu_plane *ipu_plane)
+{
+ ipu_plane->enabled = false;
+
+ ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);
+
+ if (ipu_plane->dp)
+ ipu_dp_disable_channel(ipu_plane->dp);
+ ipu_idmac_disable_channel(ipu_plane->ipu_ch);
+ ipu_dmfc_disable_channel(ipu_plane->dmfc);
+}
+
+struct ipu_plane *ipu_plane_init(struct ipu_soc *ipu,
+ int dma, int dp)
+{
+ struct ipu_plane *ipu_plane;
+
+ ipu_plane = xzalloc(sizeof(*ipu_plane));
+
+ ipu_plane->ipu = ipu;
+ ipu_plane->dma = dma;
+ ipu_plane->dp_flow = dp;
+
+ return ipu_plane;
+}
diff --git a/drivers/video/imx-ipu-v3/ipuv3-plane.h b/drivers/video/imx-ipu-v3/ipuv3-plane.h
new file mode 100644
index 0000000000..3d52f807fe
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipuv3-plane.h
@@ -0,0 +1,51 @@
+#ifndef __IPUV3_PLANE_H__
+#define __IPUV3_PLANE_H__
+
+struct drm_plane;
+struct drm_device;
+struct ipu_soc;
+struct drm_crtc;
+struct drm_framebuffer;
+
+struct ipuv3_channel;
+struct dmfc_channel;
+struct ipu_dp;
+
+struct ipu_plane {
+ struct ipu_soc *ipu;
+ struct ipuv3_channel *ipu_ch;
+ struct dmfc_channel *dmfc;
+ struct ipu_dp *dp;
+
+ int dma;
+ int dp_flow;
+
+ int x;
+ int y;
+
+ bool enabled;
+};
+
+struct ipu_plane *ipu_plane_init(struct ipu_soc *ipu,
+ int dma, int dp);
+
+/* Init IDMAC, DMFC, DP */
+int ipu_plane_mode_set(struct ipu_plane *ipu_plane,
+ struct fb_videomode *mode,
+ struct fb_info *info, u32 pixel_format,
+ int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h);
+
+void ipu_plane_enable(struct ipu_plane *plane);
+void ipu_plane_disable(struct ipu_plane *plane);
+int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct fb_info *info,
+ int x, int y);
+
+int ipu_plane_get_resources(struct ipu_plane *plane);
+void ipu_plane_put_resources(struct ipu_plane *plane);
+
+int ipu_plane_irq(struct ipu_plane *plane);
+
+#endif
diff --git a/drivers/video/imx.c b/drivers/video/imx.c
index 05c6105c7a..b12c09c8cf 100644
--- a/drivers/video/imx.c
+++ b/drivers/video/imx.c
@@ -294,7 +294,7 @@ static int imxfb_activate_var(struct fb_info *info)
u32 pcr;
int i;
- for (i = 0; i < info->num_modes; i++) {
+ for (i = 0; i < info->modes.num_modes; i++) {
if (!strcmp(fbi->mode[i].mode.name, mode->name)) {
fbi->pcr = fbi->mode[i].pcr;
break;
@@ -551,8 +551,8 @@ static int imxfb_probe(struct device_d *dev)
fbi->enable = pdata->enable;
fbi->dev = dev;
info->priv = fbi;
- info->mode_list = mode_list;
- info->num_modes = pdata->num_modes;
+ info->modes.modes = mode_list;
+ info->modes.num_modes = pdata->num_modes;
info->mode = &pdata->mode->mode;
info->xres = pdata->mode->mode.xres;
info->yres = pdata->mode->mode.yres;
@@ -580,13 +580,8 @@ static int imxfb_probe(struct device_d *dev)
return 0;
}
-static void imxfb_remove(struct device_d *dev)
-{
-}
-
static struct driver_d imxfb_driver = {
.name = "imxfb",
.probe = imxfb_probe,
- .remove = imxfb_remove,
};
device_platform_driver(imxfb_driver);
diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c
new file mode 100644
index 0000000000..5dfd5b3f1c
--- /dev/null
+++ b/drivers/video/of_display_timing.c
@@ -0,0 +1,238 @@
+/*
+ * OF helpers for parsing display timings
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This file is released under the GPLv2
+ */
+#include <common.h>
+#include <of.h>
+#include <fb.h>
+#include <malloc.h>
+
+void display_timings_release(struct display_timings *disp)
+{
+ free(disp->modes);
+ free(disp);
+}
+EXPORT_SYMBOL_GPL(display_timings_release);
+
+/**
+ * parse_timing_property - parse timing_entry from device_node
+ * @np: device_node with the property
+ * @name: name of the property
+ * @result: will be set to the return value
+ *
+ * DESCRIPTION:
+ * Every display_timing can be specified with either just the typical value or
+ * a range consisting of min/typ/max. This function helps handling this
+ **/
+static int parse_timing_property(const struct device_node *np, const char *name,
+ u32 *res)
+{
+ struct property *prop;
+ int length, cells, ret;
+
+ prop = of_find_property(np, name, &length);
+ if (!prop) {
+ pr_err("%s: could not find property %s\n",
+ np->full_name, name);
+ return -EINVAL;
+ }
+
+ cells = length / sizeof(u32);
+ if (cells == 1) {
+ ret = of_property_read_u32(np, name, res);
+ } else {
+ pr_err("%s: illegal timing specification in %s\n",
+ np->full_name, name);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * of_parse_display_timing - parse display_timing entry from device_node
+ * @np: device_node with the properties
+ **/
+static int of_parse_display_timing(const struct device_node *np,
+ struct fb_videomode *mode)
+{
+ u32 val = 0, pixelclock = 0;
+ int ret = 0;
+
+ memset(mode, 0, sizeof(*mode));
+
+ ret |= parse_timing_property(np, "hback-porch", &mode->left_margin);
+ ret |= parse_timing_property(np, "hfront-porch", &mode->right_margin);
+ ret |= parse_timing_property(np, "hactive", &mode->xres);
+ ret |= parse_timing_property(np, "hsync-len", &mode->hsync_len);
+ ret |= parse_timing_property(np, "vback-porch", &mode->upper_margin);
+ ret |= parse_timing_property(np, "vfront-porch", &mode->lower_margin);
+ ret |= parse_timing_property(np, "vactive", &mode->yres);
+ ret |= parse_timing_property(np, "vsync-len", &mode->vsync_len);
+ ret |= parse_timing_property(np, "clock-frequency", &pixelclock);
+
+ mode->pixclock = pixelclock ? KHZ2PICOS(pixelclock / 1000) : 0;
+
+ if (!of_property_read_u32(np, "vsync-active", &val))
+ mode->sync |= val ? FB_SYNC_VERT_HIGH_ACT : 0;
+ if (!of_property_read_u32(np, "hsync-active", &val))
+ mode->sync |= val ? FB_SYNC_HOR_HIGH_ACT : 0;
+ if (!of_property_read_u32(np, "de-active", &val))
+ mode->display_flags |= val ? DISPLAY_FLAGS_DE_HIGH :
+ DISPLAY_FLAGS_DE_LOW;
+ if (!of_property_read_u32(np, "pixelclk-active", &val))
+ mode->display_flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE :
+ DISPLAY_FLAGS_PIXDATA_NEGEDGE;
+
+ if (ret) {
+ pr_err("%s: error reading timing properties\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * of_get_display_timing - parse a display_timing entry
+ * @np: device_node with the timing subnode
+ * @name: name of the timing node
+ * @dt: display_timing struct to fill
+ **/
+int of_get_display_timing(struct device_node *np, const char *name,
+ struct fb_videomode *mode)
+{
+ struct device_node *timing_np;
+
+ if (!np) {
+ pr_err("%s: no devicenode given\n", np->full_name);
+ return -EINVAL;
+ }
+
+ timing_np = of_get_child_by_name(np, name);
+ if (!timing_np) {
+ pr_err("%s: could not find node '%s'\n",
+ np->full_name, name);
+ return -ENOENT;
+ }
+
+ return of_parse_display_timing(timing_np, mode);
+}
+EXPORT_SYMBOL_GPL(of_get_display_timing);
+
+/**
+ * of_get_display_timings - parse all display_timing entries from a device_node
+ * @np: device_node with the subnodes
+ **/
+struct display_timings *of_get_display_timings(struct device_node *np)
+{
+ struct device_node *timings_np;
+ struct device_node *entry;
+ struct device_node *native_mode;
+ struct display_timings *disp;
+
+ if (!np) {
+ pr_err("%s: no device node given\n", np->full_name);
+ return NULL;
+ }
+
+ timings_np = of_get_child_by_name(np, "display-timings");
+ if (!timings_np) {
+ pr_debug("%s: could not find display-timings node\n",
+ np->full_name);
+ return NULL;
+ }
+
+ disp = xzalloc(sizeof(*disp));
+
+ entry = of_parse_phandle(timings_np, "native-mode", 0);
+ /* assume first child as native mode if none provided */
+ if (!entry)
+ entry = of_get_next_available_child(np, NULL);
+ /* if there is no child, it is useless to go on */
+ if (!entry) {
+ pr_err("%s: no timing specifications given\n",
+ np->full_name);
+ goto entryfail;
+ }
+
+ pr_debug("%s: using %s as default timing\n",
+ np->full_name, entry->name);
+
+ native_mode = entry;
+
+ disp->num_modes = of_get_child_count(timings_np);
+ if (disp->num_modes == 0) {
+ /* should never happen, as entry was already found above */
+ pr_err("%s: no timings specified\n", np->full_name);
+ goto entryfail;
+ }
+
+ disp->modes = xzalloc(sizeof(struct fb_videomode) * disp->num_modes);
+
+ disp->num_modes = 0;
+ disp->native_mode = 0;
+
+ for_each_child_of_node(timings_np, entry) {
+ struct fb_videomode *mode;
+ int r;
+
+ mode = &disp->modes[disp->num_modes];
+
+ r = of_parse_display_timing(entry, mode);
+ if (r) {
+ /*
+ * to not encourage wrong devicetrees, fail in case of
+ * an error
+ */
+ pr_err("%s: error in timing %d\n",
+ np->full_name, disp->num_modes + 1);
+ goto timingfail;
+ }
+
+ mode->name = xstrdup(entry->name);
+
+ if (native_mode == entry)
+ disp->native_mode = disp->num_modes;
+
+ disp->num_modes++;
+ }
+
+ pr_debug("%s: got %d timings. Using timing #%d as default\n",
+ np->full_name, disp->num_modes,
+ disp->native_mode + 1);
+
+ return disp;
+
+timingfail:
+ display_timings_release(disp);
+entryfail:
+ free(disp);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(of_get_display_timings);
+
+/**
+ * of_display_timings_exist - check if a display-timings node is provided
+ * @np: device_node with the timing
+ **/
+int of_display_timings_exist(struct device_node *np)
+{
+ struct device_node *timings_np;
+
+ if (!np)
+ return -EINVAL;
+
+ timings_np = of_parse_phandle(np, "display-timings", 0);
+ if (!timings_np)
+ return -EINVAL;
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(of_display_timings_exist);
diff --git a/drivers/video/omap.c b/drivers/video/omap.c
index 487aca6f26..485cc7538b 100644
--- a/drivers/video/omap.c
+++ b/drivers/video/omap.c
@@ -467,8 +467,8 @@ static int omapfb_probe(struct device_d *dev)
for (i = 0; i < pdata->num_displays; ++i)
fbi->video_modes[i] = pdata->displays[i].mode;
- info->mode_list = fbi->video_modes;
- info->num_modes = pdata->num_displays;
+ info->modes.modes = fbi->video_modes;
+ info->modes.num_modes = pdata->num_displays;
info->priv = fbi;
info->fbops = &omapfb_ops;
diff --git a/drivers/video/pxa.c b/drivers/video/pxa.c
index 529190baec..b4ce3a1bf8 100644
--- a/drivers/video/pxa.c
+++ b/drivers/video/pxa.c
@@ -536,13 +536,8 @@ static int pxafb_probe(struct device_d *dev)
return 0;
}
-static void pxafb_remove(struct device_d *dev)
-{
-}
-
static struct driver_d pxafb_driver = {
.name = "pxafb",
.probe = pxafb_probe,
- .remove = pxafb_remove,
};
device_platform_driver(pxafb_driver);
diff --git a/drivers/video/s3c24xx.c b/drivers/video/s3c24xx.c
index c3e05c6cd7..b1883e232c 100644
--- a/drivers/video/s3c24xx.c
+++ b/drivers/video/s3c24xx.c
@@ -376,9 +376,9 @@ static int s3cfb_probe(struct device_d *hw_dev)
hw_dev->priv = &fbi;
/* add runtime video info */
- fbi.info.mode_list = pdata->mode_list;
- fbi.info.num_modes = pdata->mode_cnt;
- fbi.info.mode = &fbi.info.mode_list[1];
+ fbi.info.modes.modes = pdata->mode_list;
+ fbi.info.modes.num_modes = pdata->mode_cnt;
+ fbi.info.mode = &fbi.info.modes.modes[0];
fbi.info.xres = fbi.info.mode->xres;
fbi.info.yres = fbi.info.mode->yres;
if (pdata->bits_per_pixel)
diff --git a/drivers/video/sdl.c b/drivers/video/sdl.c
index 8dec5e5778..a56834004c 100644
--- a/drivers/video/sdl.c
+++ b/drivers/video/sdl.c
@@ -37,8 +37,8 @@ static int sdlfb_probe(struct device_d *dev)
return -EIO;
fb = xzalloc(sizeof(*fb));
- fb->mode_list = fb->mode = dev->platform_data;
- fb->num_modes = 1;
+ fb->modes.modes = fb->mode = dev->platform_data;
+ fb->modes.num_modes = 1;
fb->bits_per_pixel = 4 << 3;
fb->xres = fb->mode->xres;
fb->yres = fb->mode->yres;
diff --git a/drivers/video/stm.c b/drivers/video/stm.c
index fdeaf9c645..175e4b611d 100644
--- a/drivers/video/stm.c
+++ b/drivers/video/stm.c
@@ -499,9 +499,9 @@ static int stmfb_probe(struct device_d *hw_dev)
clk_enable(fbi.clk);
/* add runtime video info */
- fbi.info.mode_list = pdata->mode_list;
- fbi.info.num_modes = pdata->mode_cnt;
- fbi.info.mode = &fbi.info.mode_list[0];
+ fbi.info.modes.modes = pdata->mode_list;
+ fbi.info.modes.num_modes = pdata->mode_cnt;
+ fbi.info.mode = &fbi.info.modes.modes[0];
fbi.info.xres = fbi.info.mode->xres;
fbi.info.yres = fbi.info.mode->yres;
if (pdata->bits_per_pixel)