summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2015-08-07 07:55:00 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2015-08-07 07:55:00 +0200
commit98b5672c4d474f25227450a29f82f14e84c85f3c (patch)
tree38f074aca05571b2bed9295e6f83e098c350c94b
parent0c5c47b511763a2a9b216918e041178826fbdacc (diff)
parent427d637327bb4714085ea748bc5e7564ea0acfe6 (diff)
downloadbarebox-98b5672c4d474f25227450a29f82f14e84c85f3c.tar.gz
barebox-98b5672c4d474f25227450a29f82f14e84c85f3c.tar.xz
Merge branch 'for-next/vpl'
-rw-r--r--arch/arm/boards/efika-mx-smartbook/board.c3
-rw-r--r--arch/arm/dts/imx51-genesi-efika-sb.dts130
-rw-r--r--drivers/of/base.c206
-rw-r--r--drivers/video/Kconfig21
-rw-r--r--drivers/video/Makefile3
-rw-r--r--drivers/video/imx-ipu-v3/Kconfig1
-rw-r--r--drivers/video/imx-ipu-v3/imx-hdmi.c93
-rw-r--r--drivers/video/imx-ipu-v3/imx-ipu-v3.h25
-rw-r--r--drivers/video/imx-ipu-v3/imx-ldb.c116
-rw-r--r--drivers/video/imx-ipu-v3/ipu-di.c2
-rw-r--r--drivers/video/imx-ipu-v3/ipufb.c144
-rw-r--r--drivers/video/mtl017.c280
-rw-r--r--drivers/video/simple-panel.c160
-rw-r--r--drivers/video/vpl.c115
-rw-r--r--include/fb.h2
-rw-r--r--include/of.h8
-rw-r--r--include/of_graph.h50
-rw-r--r--include/video/vpl.h46
18 files changed, 1179 insertions, 226 deletions
diff --git a/arch/arm/boards/efika-mx-smartbook/board.c b/arch/arm/boards/efika-mx-smartbook/board.c
index 4a38afd27b..f023d70c28 100644
--- a/arch/arm/boards/efika-mx-smartbook/board.c
+++ b/arch/arm/boards/efika-mx-smartbook/board.c
@@ -187,6 +187,7 @@ static int efikamx_usb_init(void)
gpio_direction_output(GPIO_WIFI_RESET, 0);
gpio_direction_output(GPIO_SMSC3317_RESET, 0);
gpio_direction_output(GPIO_HUB_RESET, 0);
+ gpio_direction_output(GPIO_BACKLIGHT_POWER, 1);
mdelay(10);
@@ -241,8 +242,6 @@ static int efikamx_late_init(void)
defaultenv_append_directory(defaultenv_efikasb);
- gpio_direction_output(GPIO_BACKLIGHT_POWER, 1);
-
for (i = 0; i < ARRAY_SIZE(leds); i++)
led_gpio_register(&leds[i]);
diff --git a/arch/arm/dts/imx51-genesi-efika-sb.dts b/arch/arm/dts/imx51-genesi-efika-sb.dts
index 26829d14c0..78cb1b7184 100644
--- a/arch/arm/dts/imx51-genesi-efika-sb.dts
+++ b/arch/arm/dts/imx51-genesi-efika-sb.dts
@@ -90,12 +90,34 @@
mux-ext-port = <3>;
};
- backlight {
+ backlight: backlight {
compatible = "pwm-backlight";
+ enable-gpios = <&gpio4 12 GPIO_ACTIVE_LOW>;
pwms = <&pwm1 0 78770>;
- brightness-levels = <0 4 8 16 32 64 128 255>;
- default-brightness-level = <6>;
- };
+ brightness-levels = <0 1 2 4 8 16 32 64 128 255>;
+ default-brightness-level = <9>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_backlight>;
+ };
+
+ panel {
+ compatible = "simple-panel";
+ backlight = <&backlight>;
+ enable-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>;
+ ddc-i2c-bus = <&i2c2>;
+ enable-delay = <200>;
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&mtl017_out>;
+ };
+ };
+ };
+
+ lvds_reg: lvds_regulator {
+ compatible = "regulator-fixed";
+ gpio = <&gpio3 7 GPIO_ACTIVE_HIGH>;
+ };
};
&ssi1 {
@@ -115,16 +137,17 @@
MX51_PAD_DI1_PIN12__GPIO3_1 0x80000000 /* WLAN switch */
MX51_PAD_EIM_A17__GPIO2_11 0x80000000 /* Bluetooth power */
MX51_PAD_EIM_A23__GPIO2_17 0x80000000 /* Audio amp enable, 1 = on */
- MX51_PAD_CSI1_D8__GPIO3_12 0x80000000 /* LVDS enable, 1 = on */
- MX51_PAD_GPIO1_2__GPIO1_2 0x80000000 /* Backlight PWM */
- MX51_PAD_CSI2_D19__GPIO4_12 0x80000000 /* Backlight power, 0 = on */
- MX51_PAD_DISPB2_SER_DIN__GPIO3_5 0x80000000 /* LVDS reset (1 = reset) */
- MX51_PAD_DISPB2_SER_CLK__GPIO3_7 0x80000000 /* LVDS power, 1 = on */
- MX51_PAD_CSI1_D9__GPIO3_13 0x80000000 /* LCD enable (1 = on */
MX51_PAD_NANDF_CS0__GPIO3_16 0x80000000 /* Camera power, 0 = on */
MX51_PAD_GPIO1_5__GPIO1_5 0x80000000 /* USB hub reset, 0 = reset */
MX51_PAD_EIM_D27__GPIO2_9 0x80000000 /* USB phy reset, 0 = reset */
MX51_PAD_CSPI1_RDY__GPIO4_26 0x80000000 /* Audio clk enable */
+ MX51_PAD_CSI2_D19__GPIO4_12 0x80000000 /* Backlight power, 0 = on */
+ >;
+ };
+
+ pinctrl_backlight: backlightgrp {
+ fsl,pins = <
+ MX51_PAD_GPIO1_2__PWM1_PWMO 0x80000000 /* Backlight PWM */
>;
};
@@ -187,34 +210,32 @@
>;
};
- pinctrl_ipu_disp1: ipudisp1grp {
+ pinctrl_ipu_disp2: ipudisp2grp {
fsl,pins = <
- MX51_PAD_DISP1_DAT0__DISP1_DAT0 0x5
- MX51_PAD_DISP1_DAT1__DISP1_DAT1 0x5
- MX51_PAD_DISP1_DAT2__DISP1_DAT2 0x5
- MX51_PAD_DISP1_DAT3__DISP1_DAT3 0x5
- MX51_PAD_DISP1_DAT4__DISP1_DAT4 0x5
- MX51_PAD_DISP1_DAT5__DISP1_DAT5 0x5
- MX51_PAD_DISP1_DAT6__DISP1_DAT6 0x5
- MX51_PAD_DISP1_DAT7__DISP1_DAT7 0x5
- MX51_PAD_DISP1_DAT8__DISP1_DAT8 0x5
- MX51_PAD_DISP1_DAT9__DISP1_DAT9 0x5
- MX51_PAD_DISP1_DAT10__DISP1_DAT10 0x5
- MX51_PAD_DISP1_DAT11__DISP1_DAT11 0x5
- MX51_PAD_DISP1_DAT12__DISP1_DAT12 0x5
- MX51_PAD_DISP1_DAT13__DISP1_DAT13 0x5
- MX51_PAD_DISP1_DAT14__DISP1_DAT14 0x5
- MX51_PAD_DISP1_DAT15__DISP1_DAT15 0x5
- MX51_PAD_DISP1_DAT16__DISP1_DAT16 0x5
- MX51_PAD_DISP1_DAT17__DISP1_DAT17 0x5
- MX51_PAD_DISP1_DAT18__DISP1_DAT18 0x5
- MX51_PAD_DISP1_DAT19__DISP1_DAT19 0x5
- MX51_PAD_DISP1_DAT20__DISP1_DAT20 0x5
- MX51_PAD_DISP1_DAT21__DISP1_DAT21 0x5
- MX51_PAD_DISP1_DAT22__DISP1_DAT22 0x5
- MX51_PAD_DISP1_DAT23__DISP1_DAT23 0x5
- MX51_PAD_DI1_PIN2__DI1_PIN2 0x5
- MX51_PAD_DI1_PIN3__DI1_PIN3 0x5
+ MX51_PAD_DISP2_DAT0__DISP2_DAT0 0x5
+ MX51_PAD_DISP2_DAT1__DISP2_DAT1 0x5
+ MX51_PAD_DISP2_DAT2__DISP2_DAT2 0x5
+ MX51_PAD_DISP2_DAT3__DISP2_DAT3 0x5
+ MX51_PAD_DISP2_DAT4__DISP2_DAT4 0x5
+ MX51_PAD_DISP2_DAT5__DISP2_DAT5 0x5
+ MX51_PAD_DISP2_DAT6__DISP2_DAT6 0x5
+ MX51_PAD_DISP2_DAT7__DISP2_DAT7 0x5
+ MX51_PAD_DISP2_DAT8__DISP2_DAT8 0x5
+ MX51_PAD_DISP2_DAT9__DISP2_DAT9 0x5
+ MX51_PAD_DISP2_DAT10__DISP2_DAT10 0x5
+ MX51_PAD_DISP2_DAT11__DISP2_DAT11 0x5
+ MX51_PAD_DISP2_DAT12__DISP2_DAT12 0x5
+ MX51_PAD_DISP2_DAT13__DISP2_DAT13 0x5
+ MX51_PAD_DISP2_DAT14__DISP2_DAT14 0x5
+ MX51_PAD_DISP2_DAT15__DISP2_DAT15 0x5
+ MX51_PAD_DI2_PIN2__DI2_PIN2 0x5 /* hsync */
+ MX51_PAD_DI2_PIN3__DI2_PIN3 0x5 /* vsync */
+ MX51_PAD_DI2_DISP_CLK__DI2_DISP_CLK 0x5
+ MX51_PAD_DI_GP4__DI2_PIN15 0x5
+ MX51_PAD_CSI1_D8__GPIO3_12 0x80000000 /* LVDS enable, 1 = on */
+ MX51_PAD_DISPB2_SER_DIN__GPIO3_5 0x80000000 /* LVDS reset (1 = reset) */
+ MX51_PAD_DISPB2_SER_CLK__GPIO3_7 0x80000000 /* LVDS power, 1 = on */
+ MX51_PAD_CSI1_D9__GPIO3_13 0x80000000 /* LCD enable (1 = on */
>;
};
@@ -342,13 +363,38 @@
};
lvds: mtl017@3a {
+ #address-cells = <1>;
+ #size-cells = <0>;
compatible = "mtl017";
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ipu_disp1>;
+ pinctrl-0 = <&pinctrl_ipu_disp2>;
+ enable-gpios = <&gpio3 12 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&gpio3 5 GPIO_ACTIVE_HIGH>;
+ vdd-supply = <&lvds_reg>;
reg = <0x3a>;
- crtcs = <&ipu 1>;
- edid-i2c = <&i2c2>;
- interface-pix-fmt = "rgb565";
+
+ port@0 {
+ reg = <0>;
+
+ mtl017_in: endpoint {
+ remote-endpoint = <&ipu_di1_disp1>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ mtl017_out: endpoint {
+ remote-endpoint = <&panel_in>;
+ };
+ };
+ };
+};
+
+&ipu_di1 {
+ interface-pix-fmt = "rgb565";
+ endpoint {
+ remote-endpoint = <&mtl017_in>;
};
};
@@ -371,7 +417,7 @@
partition@0 {
label = "barebox-environment";
- reg = <0x80000 0x80000>;
+ reg = <0xc0000 0x40000>;
};
};
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 7f35ee5b1f..d12bfe328a 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -25,6 +25,7 @@
#include <init.h>
#include <memory.h>
#include <linux/sizes.h>
+#include <of_graph.h>
#include <linux/ctype.h>
#include <linux/amba/bus.h>
#include <linux/err.h>
@@ -1570,6 +1571,23 @@ struct device_node *of_get_next_available_child(const struct device_node *node,
EXPORT_SYMBOL(of_get_next_available_child);
/**
+ * of_get_next_child - Iterate a node childs
+ * @node: parent node
+ * @prev: previous child of the parent node, or NULL to get first
+ *
+ * Returns a node pointer with refcount incremented.
+ */
+struct device_node *of_get_next_child(const struct device_node *node,
+ struct device_node *prev)
+{
+ prev = list_prepare_entry(prev, &node->children, parent_list);
+ list_for_each_entry_continue(prev, &node->children, parent_list)
+ return prev;
+ return NULL;
+}
+EXPORT_SYMBOL(of_get_next_child);
+
+/**
* of_get_child_count - Count child nodes of given parent node
* @parent: parent node
*
@@ -2026,3 +2044,191 @@ int of_device_disable_path(const char *path)
return of_device_disable(node);
}
+
+/**
+ * of_graph_parse_endpoint() - parse common endpoint node properties
+ * @node: pointer to endpoint device_node
+ * @endpoint: pointer to the OF endpoint data structure
+ *
+ * The caller should hold a reference to @node.
+ */
+int of_graph_parse_endpoint(const struct device_node *node,
+ struct of_endpoint *endpoint)
+{
+ struct device_node *port_node = of_get_parent(node);
+
+ if (!port_node)
+ pr_warn("%s(): endpoint %s has no parent node\n",
+ __func__, node->full_name);
+
+ memset(endpoint, 0, sizeof(*endpoint));
+
+ endpoint->local_node = node;
+ /*
+ * It doesn't matter whether the two calls below succeed.
+ * If they don't then the default value 0 is used.
+ */
+ of_property_read_u32(port_node, "reg", &endpoint->port);
+ of_property_read_u32(node, "reg", &endpoint->id);
+
+ return 0;
+}
+EXPORT_SYMBOL(of_graph_parse_endpoint);
+
+/**
+ * of_graph_get_port_by_id() - get the port matching a given id
+ * @parent: pointer to the parent device node
+ * @id: id of the port
+ *
+ * Return: A 'port' node pointer with refcount incremented.
+ */
+struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id)
+{
+ struct device_node *port;
+
+ for_each_child_of_node(node, port) {
+ u32 port_id = 0;
+
+ if (strncmp(port->name, "port", 4) != 0)
+ continue;
+ of_property_read_u32(port, "reg", &port_id);
+ if (id == port_id)
+ return port;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(of_graph_get_port_by_id);
+
+/**
+ * of_graph_get_next_endpoint() - get next endpoint node
+ * @parent: pointer to the parent device node
+ * @prev: previous endpoint node, or NULL to get first
+ *
+ * Return: An 'endpoint' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is decremented.
+ */
+struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
+ struct device_node *prev)
+{
+ struct device_node *endpoint;
+ struct device_node *port;
+
+ if (!parent)
+ return NULL;
+
+ /*
+ * Start by locating the port node. If no previous endpoint is specified
+ * search for the first port node, otherwise get the previous endpoint
+ * parent port node.
+ */
+ if (!prev) {
+ struct device_node *node;
+
+ node = of_get_child_by_name(parent, "ports");
+ if (node)
+ parent = node;
+
+ port = of_get_child_by_name(parent, "port");
+ if (!port) {
+ pr_err("%s(): no port node found in %s\n",
+ __func__, parent->full_name);
+ return NULL;
+ }
+ } else {
+ port = of_get_parent(prev);
+ if (!port) {
+ pr_warn("%s(): endpoint %s has no parent node\n",
+ __func__, prev->full_name);
+ return NULL;
+ }
+ }
+
+ while (1) {
+ /*
+ * Now that we have a port node, get the next endpoint by
+ * getting the next child. If the previous endpoint is NULL this
+ * will return the first child.
+ */
+ endpoint = of_get_next_child(port, prev);
+ if (endpoint)
+ return endpoint;
+
+ /* No more endpoints under this port, try the next one. */
+ prev = NULL;
+
+ do {
+ port = of_get_next_child(parent, port);
+ if (!port)
+ return NULL;
+ } while (of_node_cmp(port->name, "port"));
+ }
+}
+EXPORT_SYMBOL(of_graph_get_next_endpoint);
+
+/**
+ * of_graph_get_remote_port_parent() - get remote port's parent node
+ * @node: pointer to a local endpoint device_node
+ *
+ * Return: Remote device node associated with remote endpoint node linked
+ * to @node.
+ */
+struct device_node *of_graph_get_remote_port_parent(
+ const struct device_node *node)
+{
+ struct device_node *np;
+ unsigned int depth;
+
+ /* Get remote endpoint node. */
+ np = of_parse_phandle(node, "remote-endpoint", 0);
+
+ /* Walk 3 levels up only if there is 'ports' node. */
+ for (depth = 3; depth && np; depth--) {
+ np = np->parent;
+ if (depth == 2 && of_node_cmp(np->name, "ports"))
+ break;
+ }
+ return np;
+}
+EXPORT_SYMBOL(of_graph_get_remote_port_parent);
+
+/**
+ * of_graph_get_remote_port() - get remote port node
+ * @node: pointer to a local endpoint device_node
+ *
+ * Return: Remote port node associated with remote endpoint node linked
+ * to @node.
+ */
+struct device_node *of_graph_get_remote_port(const struct device_node *node)
+{
+ struct device_node *np;
+
+ /* Get remote endpoint node. */
+ np = of_parse_phandle(node, "remote-endpoint", 0);
+ if (!np)
+ return NULL;
+ return np->parent;
+}
+EXPORT_SYMBOL(of_graph_get_remote_port);
+
+int of_graph_port_is_available(struct device_node *node)
+{
+ struct device_node *endpoint;
+ int available = 0;
+
+ for_each_child_of_node(node, endpoint) {
+ struct device_node *remote_parent;
+
+ remote_parent = of_graph_get_remote_port_parent(endpoint);
+ if (!remote_parent)
+ continue;
+
+ if (!of_device_is_available(remote_parent))
+ continue;
+
+ available = 1;
+ }
+
+ return available;
+}
+EXPORT_SYMBOL(of_graph_port_is_available);
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 85ba91d3c0..d7f5b07637 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -11,6 +11,9 @@ config FRAMEBUFFER_CONSOLE
select FONTS
prompt "framebuffer console support"
+config VIDEO_VPL
+ bool
+
config DRIVER_VIDEO_ATMEL
bool "Atmel LCDC framebuffer driver"
depends on ARCH_AT91
@@ -111,4 +114,22 @@ config DRIVER_VIDEO_BACKLIGHT_PWM
help
Enable this to get support for backlight devices driven by a PWM.
+comment "Video encoder chips"
+
+config DRIVER_VIDEO_MTL017
+ bool "MTL017 LVDS encoder"
+ select VIDEO_VPL
+ help
+ The MTL017 is a parallel to lvds video encoder chip found on the
+ Efika MX Smartbook.
+
+config DRIVER_VIDEO_SIMPLE_PANEL
+ bool "Simple panel support"
+ select VIDEO_VPL
+ help
+ This enabled support for simple panels, i.e. panels which consist of
+ a mode definition and enable gpios in the devicetree. Unlike the
+ Linux Kernel implementation this one is able to understand display-timings
+ nodes so that it's not necessary to keep a list of all known displays
+ with their corresponding timings in barebox.
endif
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 359135ea5e..57e4864a6d 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -4,6 +4,9 @@ obj-$(CONFIG_OFDEVICE) += of_display_timing.o
obj-$(CONFIG_DRIVER_VIDEO_BACKLIGHT) += backlight.o
obj-$(CONFIG_DRIVER_VIDEO_BACKLIGHT_PWM) += backlight-pwm.o
obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbconsole.o
+obj-$(CONFIG_VIDEO_VPL) += vpl.o
+obj-$(CONFIG_DRIVER_VIDEO_MTL017) += mtl017.o
+obj-$(CONFIG_DRIVER_VIDEO_SIMPLE_PANEL) += simple-panel.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
diff --git a/drivers/video/imx-ipu-v3/Kconfig b/drivers/video/imx-ipu-v3/Kconfig
index 2ca9132f22..386ff5bc2e 100644
--- a/drivers/video/imx-ipu-v3/Kconfig
+++ b/drivers/video/imx-ipu-v3/Kconfig
@@ -1,6 +1,7 @@
config DRIVER_VIDEO_IMX_IPUV3
bool "i.MX IPUv3 driver"
depends on ARCH_IMX
+ select VIDEO_VPL
help
Support the IPUv3 found on Freescale i.MX51/53/6 SoCs
diff --git a/drivers/video/imx-ipu-v3/imx-hdmi.c b/drivers/video/imx-ipu-v3/imx-hdmi.c
index e01bfe8f31..f5a2e3c601 100644
--- a/drivers/video/imx-ipu-v3/imx-hdmi.c
+++ b/drivers/video/imx-ipu-v3/imx-hdmi.c
@@ -23,6 +23,7 @@
#include <asm-generic/div64.h>
#include <linux/clk.h>
#include <i2c/i2c.h>
+#include <video/vpl.h>
#include <mach/imx6-regs.h>
#include <mach/imx53-regs.h>
@@ -133,13 +134,13 @@ struct imx_hdmi {
bool phy_enabled;
struct regmap *regmap;
- struct i2c_adapter *ddc;
+ struct device_node *ddc_node;;
void __iomem *regs;
unsigned int sample_rate;
int ratio;
- struct ipu_output output;
+ struct vpl vpl;
};
static void imx_hdmi_set_ipu_di_mux(struct imx_hdmi *hdmi, int ipu_di)
@@ -1011,7 +1012,7 @@ static void imx_hdmi_clear_overflow(struct imx_hdmi *hdmi)
hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
}
-static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct fb_videomode *mode)
+static int imx_hdmi_setup(struct imx_hdmi *hdmi)
{
int ret;
@@ -1134,43 +1135,58 @@ static struct of_device_id imx_hdmi_dt_ids[] = {
}
};
-static int imx_hdmi_prepare(struct ipu_output *output, struct fb_videomode *mode, int di)
+static int imx_hdmi_get_modes(struct imx_hdmi *hdmi, struct display_timings *timings)
{
- struct imx_hdmi *hdmi = container_of(output, struct imx_hdmi, output);
+ int ret = -ENOENT;
- imx_hdmi_set_ipu_di_mux(hdmi, di);
+ if (hdmi->ddc_node) {
+ struct i2c_adapter *i2c;
- return 0;
-}
+ i2c = of_find_i2c_adapter_by_node(hdmi->ddc_node);
+ if (!i2c)
+ return -ENODEV;
+ timings->edid = edid_read_i2c(i2c);
+ if (!timings->edid)
+ return -EINVAL;
-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);
+ ret = edid_to_display_timings(timings, timings->edid);
+ }
- return 0;
+ return ret;
}
-static int imx_hdmi_disable(struct ipu_output *output)
+static int imx_hdmi_ioctl(struct vpl *vpl, unsigned int port,
+ unsigned int cmd, void *data)
{
- struct imx_hdmi *hdmi = container_of(output, struct imx_hdmi, output);
-
- imx_hdmi_phy_disable(hdmi);
+ struct imx_hdmi *hdmi = container_of(vpl, struct imx_hdmi, vpl);
+ struct ipu_di_mode *mode;
+
+ switch (cmd) {
+ case VPL_ENABLE:
+ return imx_hdmi_setup(hdmi);
+ case VPL_DISABLE:
+ imx_hdmi_phy_disable(hdmi);
+ return 0;
+ case VPL_PREPARE:
+ imx_hdmi_set_ipu_di_mux(hdmi, port);
+ return 0;
+ case VPL_GET_VIDEOMODES:
+ return imx_hdmi_get_modes(hdmi, data);
+ case IMX_IPU_VPL_DI_MODE:
+ mode = data;
+
+ mode->di_clkflags = IPU_DI_CLKMODE_EXT | IPU_DI_CLKMODE_SYNC;
+ mode->interface_pix_fmt = V4L2_PIX_FMT_RGB24;
+
+ return 0;
+ }
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;
@@ -1190,18 +1206,7 @@ static int imx_hdmi_probe(struct device_d *dev)
if (ret)
return ret;
- if (IS_ENABLED(CONFIG_DRIVER_VIDEO_EDID)) {
- 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->ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
hdmi->regs = dev_request_mem_region(dev, 0);
if (!hdmi->regs)
@@ -1269,15 +1274,11 @@ static int imx_hdmi_probe(struct device_d *dev)
/* 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);
+ hdmi->vpl.node = np;
+ hdmi->vpl.ioctl = imx_hdmi_ioctl;
+ ret = vpl_register(&hdmi->vpl);
+ if (ret)
+ return ret;
return 0;
diff --git a/drivers/video/imx-ipu-v3/imx-ipu-v3.h b/drivers/video/imx-ipu-v3/imx-ipu-v3.h
index 7c48a7ce38..783535ea54 100644
--- a/drivers/video/imx-ipu-v3/imx-ipu-v3.h
+++ b/drivers/video/imx-ipu-v3/imx-ipu-v3.h
@@ -14,6 +14,7 @@
#include <io.h>
#include <fb.h>
+#include <video/vpl.h>
#include <video/fourcc.h>
struct ipu_soc;
@@ -44,7 +45,6 @@ struct ipu_di_signal_cfg {
u16 width;
u16 height;
- u32 pixel_fmt;
u16 h_start_width;
u16 h_sync_width;
u16 h_end_width;
@@ -319,26 +319,11 @@ struct ipu_client_platformdata {
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;
+struct ipu_di_mode {
+ u32 di_clkflags;
+ u32 interface_pix_fmt;
};
-int ipu_register_output(struct ipu_output *ouput);
+#define IMX_IPU_VPL_DI_MODE 0x12660001
#endif /* __DRM_IPU_H__ */
diff --git a/drivers/video/imx-ipu-v3/imx-ldb.c b/drivers/video/imx-ipu-v3/imx-ldb.c
index a05bfad8c0..db5f8b631d 100644
--- a/drivers/video/imx-ipu-v3/imx-ldb.c
+++ b/drivers/video/imx-ipu-v3/imx-ldb.c
@@ -21,10 +21,13 @@
#include <common.h>
#include <fb.h>
#include <io.h>
+#include <of_graph.h>
#include <driver.h>
#include <malloc.h>
#include <errno.h>
#include <init.h>
+#include <video/vpl.h>
+#include <mfd/imx6q-iomuxc-gpr.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <asm-generic/div64.h>
@@ -57,13 +60,17 @@ struct imx_ldb_channel {
int chno;
int mode_valid;
struct display_timings *modes;
- struct ipu_output output;
+ struct device_node *remote;
+ struct vpl vpl;
+ int output_port;
+ int datawidth;
};
struct imx_ldb_data {
void __iomem *base;
int (*prepare)(struct imx_ldb_channel *imx_ldb_ch, int di);
unsigned ipu_mask;
+ int have_mux;
};
struct imx_ldb {
@@ -102,16 +109,11 @@ static const int of_get_data_mapping(struct device_node *np)
return -EINVAL;
}
-static int imx_ldb_prepare(struct ipu_output *output, struct fb_videomode *mode, int di)
+static int imx_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, 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 */
@@ -141,7 +143,20 @@ static int imx_ldb_prepare(struct ipu_output *output, struct fb_videomode *mode,
writel(ldb->ldb_ctrl, ldb->base);
- return 0;
+ return vpl_ioctl(&imx_ldb_ch->vpl, imx_ldb_ch->output_port,
+ VPL_PREPARE, NULL);
+}
+
+static int imx_ldb_enable(struct imx_ldb_channel *imx_ldb_ch, int di)
+{
+ return vpl_ioctl(&imx_ldb_ch->vpl, imx_ldb_ch->output_port,
+ VPL_ENABLE, NULL);
+}
+
+static int imx_ldb_disable(struct imx_ldb_channel *imx_ldb_ch, int di)
+{
+ return vpl_ioctl(&imx_ldb_ch->vpl, imx_ldb_ch->output_port,
+ VPL_DISABLE, NULL);
}
static int imx6q_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di)
@@ -226,6 +241,7 @@ static struct imx_ldb_data imx_ldb_data_imx6q = {
.base = (void *)MX6_IOMUXC_BASE_ADDR + 0x8,
.prepare = imx6q_ldb_prepare,
.ipu_mask = 0xf,
+ .have_mux = 1,
};
static struct imx_ldb_data imx_ldb_data_imx53 = {
@@ -234,9 +250,42 @@ static struct imx_ldb_data imx_ldb_data_imx53 = {
.ipu_mask = 0x3,
};
-static struct ipu_output_ops imx_ldb_ops = {
- .prepare = imx_ldb_prepare,
-};
+static int imx_ldb_ioctl(struct vpl *vpl, unsigned int port,
+ unsigned int cmd, void *data)
+{
+ struct imx_ldb_channel *imx_ldb_ch = container_of(vpl,
+ struct imx_ldb_channel, vpl);
+ int ret;
+ struct ipu_di_mode *mode;
+
+ switch (cmd) {
+ case VPL_ENABLE:
+ ret = vpl_ioctl(vpl, imx_ldb_ch->output_port, cmd, data);
+ if (ret)
+ return ret;
+ return imx_ldb_enable(imx_ldb_ch, port);
+ case VPL_DISABLE:
+ ret = vpl_ioctl(vpl, imx_ldb_ch->output_port, cmd, data);
+ if (ret)
+ return ret;
+ return imx_ldb_disable(imx_ldb_ch, port);
+ case VPL_PREPARE:
+ ret = vpl_ioctl(vpl, imx_ldb_ch->output_port, cmd, data);
+ if (ret)
+ return ret;
+ return imx_ldb_prepare(imx_ldb_ch, data, port);
+ case IMX_IPU_VPL_DI_MODE:
+ mode = data;
+
+ mode->di_clkflags = IPU_DI_CLKMODE_EXT | IPU_DI_CLKMODE_SYNC;
+ mode->interface_pix_fmt = (imx_ldb_ch->datawidth == 24) ?
+ V4L2_PIX_FMT_RGB24 : V4L2_PIX_FMT_BGR666;
+
+ return 0;
+ default:
+ return vpl_ioctl(vpl, imx_ldb_ch->output_port, cmd, data);
+ }
+}
static int imx_ldb_probe(struct device_d *dev)
{
@@ -245,7 +294,6 @@ static int imx_ldb_probe(struct device_d *dev)
struct imx_ldb *imx_ldb;
int ret, i;
int dual = 0;
- int datawidth;
int mapping;
const struct imx_ldb_data *devtype;
@@ -259,6 +307,8 @@ static int imx_ldb_probe(struct device_d *dev)
for_each_child_of_node(np, child) {
struct imx_ldb_channel *channel;
+ struct device_node *port;
+ struct device_node *endpoint;
ret = of_property_read_u32(child, "reg", &i);
if (ret || i < 0 || i > 1)
@@ -275,17 +325,37 @@ static int imx_ldb_probe(struct device_d *dev)
channel = &imx_ldb->channel[i];
channel->ldb = imx_ldb;
channel->chno = i;
+ channel->output_port = imx_ldb->soc_data->have_mux ? 4 : 1;
+
+ /* The output port is port@4 with mux or port@1 without mux */
+ port = of_graph_get_port_by_id(child, channel->output_port);
+ if (!port) {
+ dev_warn(dev, "No port found for %s\n", child->full_name);
+ continue;
+ }
- ret = of_property_read_u32(child, "fsl,data-width", &datawidth);
+ endpoint = of_get_child_by_name(port, "endpoint");
+ if (!endpoint) {
+ dev_warn(dev, "No endpoint found on %s\n", port->full_name);
+ continue;
+ }
+
+ channel->vpl.node = child;
+ channel->vpl.ioctl = &imx_ldb_ioctl;
+ ret = vpl_register(&channel->vpl);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32(child, "fsl,data-width", &channel->datawidth);
if (ret)
- datawidth = 0;
- else if (datawidth != 18 && datawidth != 24)
+ channel->datawidth = 0;
+ else if (channel->datawidth != 18 && channel->datawidth != 24)
return -EINVAL;
mapping = of_get_data_mapping(child);
switch (mapping) {
case LVDS_BIT_MAP_SPWG:
- if (datawidth == 24) {
+ if (channel->datawidth == 24) {
if (i == 0 || dual)
imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;
if (i == 1 || dual)
@@ -293,7 +363,7 @@ static int imx_ldb_probe(struct device_d *dev)
}
break;
case LVDS_BIT_MAP_JEIDA:
- if (datawidth == 18) {
+ if (channel->datawidth == 18) {
dev_err(dev, "JEIDA standard only supported in 24 bit\n");
return -EINVAL;
}
@@ -306,16 +376,6 @@ static int imx_ldb_probe(struct device_d *dev)
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;
diff --git a/drivers/video/imx-ipu-v3/ipu-di.c b/drivers/video/imx-ipu-v3/ipu-di.c
index e3338d09ba..8df9c9f06b 100644
--- a/drivers/video/imx-ipu-v3/ipu-di.c
+++ b/drivers/video/imx-ipu-v3/ipu-di.c
@@ -627,7 +627,7 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
}
}
- if (!sig->clk_pol)
+ if (sig->clk_pol)
di_gen |= DI_GEN_POLARITY_DISP_CLK;
ipu_di_write(di, di_gen, DI_GENERAL);
diff --git a/drivers/video/imx-ipu-v3/ipufb.c b/drivers/video/imx-ipu-v3/ipufb.c
index 7ee4ae3627..e804c31b29 100644
--- a/drivers/video/imx-ipu-v3/ipufb.c
+++ b/drivers/video/imx-ipu-v3/ipufb.c
@@ -19,6 +19,7 @@
#include <malloc.h>
#include <errno.h>
#include <init.h>
+#include <of_graph.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <asm-generic/div64.h>
@@ -62,8 +63,9 @@ struct ipufb_info {
struct list_head list;
char *name;
int id;
+ int dino;
- struct ipu_output *output;
+ struct vpl vpl;
};
static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
@@ -73,23 +75,6 @@ static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
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)
@@ -97,14 +82,17 @@ int ipu_crtc_mode_set(struct ipufb_info *fbi,
struct fb_info *info = &fbi->info;
int ret;
struct ipu_di_signal_cfg sig_cfg = {};
- u32 out_pixel_fmt;
+ struct ipu_di_mode di_mode = {};
+ u32 interface_pix_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;
+ vpl_ioctl(&fbi->vpl, 2 + fbi->dino, IMX_IPU_VPL_DI_MODE, &di_mode);
+ interface_pix_fmt = di_mode.interface_pix_fmt ?
+ di_mode.interface_pix_fmt : fbi->interface_pix_fmt;
if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
sig_cfg.Hsync_pol = 1;
@@ -112,10 +100,9 @@ int ipu_crtc_mode_set(struct ipufb_info *fbi,
sig_cfg.Vsync_pol = 1;
sig_cfg.enable_pol = 1;
- sig_cfg.clk_pol = 1;
+ sig_cfg.clk_pol = 0;
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;
@@ -124,7 +111,7 @@ int ipu_crtc_mode_set(struct ipufb_info *fbi,
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.clkflags = di_mode.di_clkflags;
sig_cfg.v_to_h_sync = 0;
@@ -132,7 +119,7 @@ int ipu_crtc_mode_set(struct ipufb_info *fbi,
sig_cfg.vsync_pin = 3;
ret = ipu_dc_init_sync(fbi->dc, fbi->di, sig_cfg.interlaced,
- out_pixel_fmt, mode->xres);
+ interface_pix_fmt, mode->xres);
if (ret) {
dev_err(fbi->dev,
"initializing display controller failed with %d\n",
@@ -171,31 +158,25 @@ int ipu_crtc_mode_set(struct ipufb_info *fbi,
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);
+ vpl_ioctl_prepare(&fbi->vpl, 2 + fbi->dino, info->mode);
ipu_crtc_mode_set(fbi, info->mode, 0, 0);
- if (output->ops->enable)
- output->ops->enable(output, info->mode, fbi->id);
+ vpl_ioctl_enable(&fbi->vpl, 2 + fbi->dino);
}
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);
+ vpl_ioctl_disable(&fbi->vpl, 2 + fbi->dino);
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);
+ vpl_ioctl_unprepare(&fbi->vpl, 2 + fbi->dino);
}
static int ipufb_activate_var(struct fb_info *info)
@@ -208,7 +189,7 @@ static int ipufb_activate_var(struct fb_info *info)
if (!fbi->info.screen_base)
return -ENOMEM;
- memset(fbi->info.screen_base, 0, info->line_length * info->yres);
+ memset(fbi->info.screen_base, 0x0, info->line_length * info->yres);
return 0;
}
@@ -219,56 +200,6 @@ static struct fb_ops ipufb_ops = {
.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)
{
@@ -311,6 +242,8 @@ static int ipufb_probe(struct device_d *dev)
int ret, ipuid;
struct ipu_client_platformdata *pdata = dev->platform_data;
struct ipu_rgb *ipu_rgb;
+ struct device_node *node;
+ const char *fmt;
fbi = xzalloc(sizeof(*fbi));
info = &fbi->info;
@@ -318,6 +251,7 @@ static int ipufb_probe(struct device_d *dev)
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->dino = pdata->di;
fbi->dev = dev;
info->priv = fbi;
@@ -337,7 +271,38 @@ static int ipufb_probe(struct device_d *dev)
if (ret)
return ret;
- ret = ipu_register_fb(fbi);
+ node = of_graph_get_port_by_id(dev->parent->device_node, 2 + pdata->di);
+ if (node && of_graph_port_is_available(node)) {
+ dev_info(fbi->dev, "register vpl for %s\n", dev->parent->device_node->full_name);
+
+ fbi->vpl.node = dev->parent->device_node;
+ ret = vpl_register(&fbi->vpl);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_string(node, "interface-pix-fmt", &fmt);
+ if (!ret) {
+ if (!strcmp(fmt, "rgb24"))
+ fbi->interface_pix_fmt = V4L2_PIX_FMT_RGB24;
+ else if (!strcmp(fmt, "rgb565"))
+ fbi->interface_pix_fmt = V4L2_PIX_FMT_RGB565;
+ else if (!strcmp(fmt, "bgr666"))
+ fbi->interface_pix_fmt = V4L2_PIX_FMT_BGR666;
+ else if (!strcmp(fmt, "lvds666"))
+ fbi->interface_pix_fmt =
+ v4l2_fourcc('L', 'V', 'D', '6');
+ }
+
+ ret = vpl_ioctl(&fbi->vpl, 2 + fbi->dino, VPL_GET_VIDEOMODES, &info->modes);
+ if (ret)
+ return ret;
+
+ ret = register_framebuffer(info);
+ if (ret < 0) {
+ dev_err(fbi->dev, "failed to register framebuffer\n");
+ return ret;
+ }
+ }
return ret;
}
@@ -351,4 +316,9 @@ static struct driver_d ipufb_driver = {
.probe = ipufb_probe,
.remove = ipufb_remove,
};
-device_platform_driver(ipufb_driver);
+
+static int ipufb_register(void)
+{
+ return platform_driver_register(&ipufb_driver);
+}
+late_initcall(ipufb_register);
diff --git a/drivers/video/mtl017.c b/drivers/video/mtl017.c
new file mode 100644
index 0000000000..1a1f686223
--- /dev/null
+++ b/drivers/video/mtl017.c
@@ -0,0 +1,280 @@
+/*
+ * (C) Copyright 2014 Sascha Hauer, Pengutronix
+ *
+ * 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 <init.h>
+#include <driver.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <regulator.h>
+#include <of_gpio.h>
+#include <gpio.h>
+#include <fb.h>
+#include <video/vpl.h>
+
+#include <i2c/i2c.h>
+
+struct mtl017 {
+ struct vpl vpl;
+ struct device_d *dev;
+ struct i2c_client *client;
+ u8 *regs;
+ int enable_gpio;
+ int enable_active_high;
+ int reset_gpio;
+ int reset_active_high;
+ struct regulator *regulator;
+};
+
+/*
+ * Unfortunately we know nothing about the mtl017 except the following
+ * register tables which are derived from the Efika kernel. The displays
+ * provide EDID data, but this does not work with the mtl017 or at least
+ * not with the below register settings, so we have to provide hardcoded
+ * modelines and use the EDID data only to match against the vendor/display.
+ */
+static u8 mtl017_44_54_tbl[] = {
+ /* 44M to 53.9M */
+ 0x00, 0x20, 0xAF, 0x59, 0x2B, 0xDE, 0x51, 0x00,
+ 0x00, 0x04, 0x17, 0x00, 0x58, 0x02, 0x00, 0x00,
+ 0x00, 0x3B, 0x01, 0x08, 0x00, 0x1E, 0x01, 0x05,
+ 0x00, 0x01, 0x72, 0x05, 0x32, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x20, 0xA8, 0x02, 0x12, 0x00, 0x58,
+ 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00,
+ 0x00, 0x02, 0x10, 0x01, 0x68, 0x03, 0xC2, 0x01,
+ 0x4A, 0x03, 0x46, 0x00, 0xF1, 0x01, 0x5C, 0x04,
+ 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3A,
+ 0x18, 0x4B, 0x29, 0x5C, 0xDE, 0xF6, 0xE0, 0x1C,
+ 0x03, 0xFC, 0xE3, 0x1F, 0xF3, 0x75, 0x26, 0x45,
+ 0x4A, 0x91, 0x8A, 0xFF, 0x3F, 0x83, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x4E, 0x48,
+ 0x00, 0x01, 0x10, 0x01, 0x00, 0x00, 0x10, 0x04,
+ 0x02, 0x1F, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x04, 0x12, 0x00, 0x58, 0x02,
+ 0x02, 0x7C, 0x04, 0x98, 0x02, 0x11, 0x78, 0x18,
+ 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+#define REGMAP_LENGTH (sizeof(mtl017_44_54_tbl) / sizeof(u8))
+#define BLOCK_TX_SIZE 32
+
+struct mtl017_panel_info {
+ char *manufacturer;
+ char *product_name;
+ struct fb_videomode *mode;
+ u8 *regs;
+};
+
+
+static struct fb_videomode auo_bw101aw02_mode = {
+ .name = "AUO B101AW02 1024x600",
+ .refresh = 60,
+ .xres = 1024,
+ .yres = 600,
+ .pixclock = 22800,
+ .left_margin = 80,
+ .right_margin = 40,
+ .upper_margin = 21,
+ .lower_margin = 21,
+ .hsync_len = 4,
+ .vsync_len = 4,
+ .vmode = FB_VMODE_NONINTERLACED,
+};
+
+static struct mtl017_panel_info panels[] = {
+ {
+ .manufacturer = "AUO",
+ .product_name = "B101AW02 V0",
+ .mode = &auo_bw101aw02_mode,
+ .regs = mtl017_44_54_tbl,
+ },
+ {
+ .manufacturer = "CMO",
+ .product_name = "N101L6-L0D",
+ .mode = &auo_bw101aw02_mode,
+ .regs = mtl017_44_54_tbl,
+ },
+};
+
+static int mtl017_get_videomodes(struct mtl017 *mtl017, struct display_timings *timings)
+{
+ int ret, i;
+
+ ret = vpl_ioctl(&mtl017->vpl, 1, VPL_GET_VIDEOMODES, timings);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(panels); i ++) {
+ if (memcmp(timings->edid + 0x71, panels[i].product_name,
+ strlen(panels[i].product_name)))
+ continue;
+
+ dev_dbg(mtl017->dev, "found LCD Panel: %s %s\n",
+ panels[i].manufacturer,
+ panels[i].product_name);
+ mtl017->regs = panels[i].regs;
+
+ timings->modes[0].name = panels[i].mode->name;
+ timings->modes[0].refresh = panels[i].mode->refresh;
+ timings->modes[0].xres = panels[i].mode->xres;
+ timings->modes[0].yres = panels[i].mode->yres;
+ timings->modes[0].pixclock = panels[i].mode->pixclock;
+ timings->modes[0].left_margin = panels[i].mode->left_margin;
+ timings->modes[0].right_margin = panels[i].mode->right_margin;
+ timings->modes[0].upper_margin = panels[i].mode->upper_margin;
+ timings->modes[0].lower_margin = panels[i].mode->lower_margin;
+ timings->modes[0].hsync_len = panels[i].mode->hsync_len;
+ timings->modes[0].vsync_len = panels[i].mode->vsync_len;
+ timings->num_modes = 1;
+ }
+
+ return 0;
+}
+
+static int mtl017_enable(struct mtl017 *mtl017)
+{
+ int i;
+ int ret;
+ int retry = 5;
+ u8 *reg_tbl = mtl017->regs;
+
+ dev_dbg(mtl017->dev, "Initializing MTL017 LVDS Controller\n");
+
+ ret = regulator_enable(mtl017->regulator);
+ if (ret)
+ return ret;
+
+ gpio_direction_output(mtl017->reset_gpio, mtl017->reset_active_high);
+ mdelay(5);
+ gpio_direction_output(mtl017->reset_gpio, !mtl017->reset_active_high);
+ mdelay(5);
+ gpio_direction_output(mtl017->enable_gpio, mtl017->enable_active_high);
+
+ /* Write configuration table */
+ for (i = 0; i < REGMAP_LENGTH; i+=BLOCK_TX_SIZE) {
+retry:
+ mdelay(1);
+ ret = i2c_smbus_write_i2c_block_data(mtl017->client, i, BLOCK_TX_SIZE, &(reg_tbl[i]));
+ if (ret < 0) {
+ dev_warn(mtl017->dev, "failed to initialize: %d\n", ret);
+ if (retry-- > 0)
+ goto retry;
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int mtl017_ioctl(struct vpl *vpl, unsigned int port,
+ unsigned int cmd, void *ptr)
+{
+ struct mtl017 *mtl017 = container_of(vpl,
+ struct mtl017, vpl);
+ int ret = 0;
+
+ switch (cmd) {
+ case VPL_PREPARE:
+ dev_dbg(mtl017->dev, "VPL_PREPARE\n");
+ goto forward;
+ case VPL_ENABLE:
+ dev_dbg(mtl017->dev, "VPL_ENABLE\n");
+
+ ret = mtl017_enable(mtl017);
+ if (ret < 0)
+ break;
+
+ goto forward;
+ case VPL_DISABLE:
+ dev_dbg(mtl017->dev, "VPL_DISABLE\n");
+
+ goto forward;
+ case VPL_GET_VIDEOMODES:
+ dev_dbg(mtl017->dev, "VPL_GET_VIDEOMODES\n");
+
+ ret = mtl017_get_videomodes(mtl017, ptr);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+
+forward:
+ return vpl_ioctl(&mtl017->vpl, 1, cmd, ptr);
+}
+
+static int mtl017_probe(struct device_d *dev)
+{
+ struct mtl017 *mtl017;
+ int ret;
+ enum of_gpio_flags flags;
+
+ mtl017 = xzalloc(sizeof(struct mtl017));
+ mtl017->vpl.node = dev->device_node;
+ mtl017->vpl.ioctl = mtl017_ioctl;
+ mtl017->dev = dev;
+ mtl017->client = to_i2c_client(dev);
+ mtl017->regulator = regulator_get(dev, "vdd");
+
+ mtl017->enable_gpio = of_get_named_gpio_flags(dev->device_node,
+ "enable-gpios", 0, &flags);
+ if (gpio_is_valid(mtl017->enable_gpio)) {
+ if (!(flags & OF_GPIO_ACTIVE_LOW))
+ mtl017->enable_active_high = 1;
+ }
+
+ mtl017->reset_gpio = of_get_named_gpio_flags(dev->device_node,
+ "reset-gpios", 0, &flags);
+ if (gpio_is_valid(mtl017->reset_gpio)) {
+ if (!(flags & OF_GPIO_ACTIVE_LOW))
+ mtl017->reset_active_high = 1;
+ }
+
+ ret = vpl_register(&mtl017->vpl);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct driver_d twl_driver = {
+ .name = "mtl017",
+ .probe = mtl017_probe,
+};
+
+static int mtl017_init(void)
+{
+ i2c_driver_register(&twl_driver);
+ return 0;
+}
+
+device_initcall(mtl017_init);
diff --git a/drivers/video/simple-panel.c b/drivers/video/simple-panel.c
new file mode 100644
index 0000000000..dceedc60c3
--- /dev/null
+++ b/drivers/video/simple-panel.c
@@ -0,0 +1,160 @@
+/*
+ * simple panel support for barebox
+ *
+ * (C) Copyright 2014 Sascha Hauer, Pengutronix
+ *
+ * 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 <malloc.h>
+#include <init.h>
+#include <linux/err.h>
+#include <of.h>
+#include <fb.h>
+#include <gpio.h>
+#include <of_gpio.h>
+#include <video/backlight.h>
+#include <video/vpl.h>
+#include <i2c/i2c.h>
+
+struct simple_panel {
+ struct vpl vpl;
+ int enable_gpio;
+ int enable_active_high;
+ int enabled;
+ struct device_node *backlight_node;
+ struct backlight_device *backlight;
+ struct device_node *ddc_node;
+ int enable_delay;
+};
+
+static int simple_panel_enable(struct simple_panel *panel)
+{
+ int ret;
+
+ if (panel->backlight_node && !panel->backlight) {
+ panel->backlight = of_backlight_find(panel->backlight_node);
+ if (!panel->backlight)
+ return -ENODEV;
+ }
+
+ if (gpio_is_valid(panel->enable_gpio))
+ gpio_direction_output(panel->enable_gpio,
+ panel->enable_active_high);
+
+ if (panel->enable_delay)
+ mdelay(panel->enable_delay);
+
+ if (panel->backlight) {
+ ret = backlight_set_brightness_default(panel->backlight);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int simple_panel_disable(struct simple_panel *panel)
+{
+ if (panel->backlight)
+ backlight_set_brightness(panel->backlight, 0);
+
+ if (gpio_is_valid(panel->enable_gpio))
+ gpio_direction_output(panel->enable_gpio,
+ !panel->enable_active_high);
+
+ return 0;
+}
+
+static int simple_panel_get_modes(struct simple_panel *panel, struct display_timings *timings)
+{
+ int ret = -ENOENT;
+
+ if (panel->ddc_node) {
+ struct i2c_adapter *i2c;
+
+ i2c = of_find_i2c_adapter_by_node(panel->ddc_node);
+ if (!i2c)
+ return -ENODEV;
+ timings->edid = edid_read_i2c(i2c);
+ if (!timings->edid)
+ return -EINVAL;
+
+ ret = edid_to_display_timings(timings, timings->edid);
+ }
+
+ return ret;
+}
+
+static int simple_panel_ioctl(struct vpl *vpl, unsigned int port,
+ unsigned int cmd, void *ptr)
+{
+ struct simple_panel *panel = container_of(vpl,
+ struct simple_panel, vpl);
+
+ switch (cmd) {
+ case VPL_ENABLE:
+ return simple_panel_enable(panel);
+ case VPL_DISABLE:
+ return simple_panel_disable(panel);
+ case VPL_GET_VIDEOMODES:
+ return simple_panel_get_modes(panel, ptr);
+ default:
+ return -ENOSYS;
+ }
+}
+
+static int simple_panel_probe(struct device_d *dev)
+{
+ struct simple_panel *panel;
+ struct device_node *node = dev->device_node;
+ enum of_gpio_flags flags;
+ int ret;
+
+ panel = xzalloc(sizeof(*panel));
+
+ panel->enable_gpio = of_get_named_gpio_flags(node, "enable-gpios", 0, &flags);
+ panel->vpl.node = node;
+ panel->vpl.ioctl = simple_panel_ioctl;
+
+ if (gpio_is_valid(panel->enable_gpio)) {
+ if (!(flags & OF_GPIO_ACTIVE_LOW))
+ panel->enable_active_high = 1;
+ }
+
+ panel->ddc_node = of_parse_phandle(node, "ddc-i2c-bus", 0);
+
+ of_property_read_u32(node, "enable-delay", &panel->enable_delay);
+
+ panel->backlight_node = of_parse_phandle(node, "backlight", 0);
+
+ ret = vpl_register(&panel->vpl);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct of_device_id simple_panel_of_ids[] = {
+ { .compatible = "simple-panel", },
+ { }
+};
+
+static struct driver_d simple_panel_driver = {
+ .name = "simple-panel",
+ .probe = simple_panel_probe,
+ .of_compatible = DRV_OF_COMPAT(simple_panel_of_ids),
+};
+device_platform_driver(simple_panel_driver);
diff --git a/drivers/video/vpl.c b/drivers/video/vpl.c
new file mode 100644
index 0000000000..99ad180eec
--- /dev/null
+++ b/drivers/video/vpl.c
@@ -0,0 +1,115 @@
+/*
+ * Video pipeline (VPL) support for barebox
+ *
+ * (C) Copyright 2014 Sascha Hauer, Pengutronix
+ *
+ * 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.
+ *
+ */
+#define pr_fmt(fmt) "VPL: " fmt
+
+#include <common.h>
+#include <driver.h>
+#include <of_graph.h>
+#include <linux/list.h>
+#include <video/vpl.h>
+
+static LIST_HEAD(vpls);
+
+int vpl_register(struct vpl *vpl)
+{
+ list_add_tail(&vpl->list, &vpls);
+
+ pr_debug("%s: %s\n", __func__, vpl->node->full_name);
+
+ return 0;
+}
+
+struct vpl *of_find_vpl(struct device_node *node)
+{
+ struct vpl *vpl;
+
+ list_for_each_entry(vpl, &vpls, list)
+ if (vpl->node == node)
+ return vpl;
+
+ return NULL;
+}
+
+struct vpl *of_vpl_get(struct device_node *node, int port)
+{
+ node = of_graph_get_port_by_id(node, port);
+ if (!node)
+ return NULL;
+
+ pr_debug("%s: port: %s\n", __func__, node->full_name);
+
+ node = of_graph_get_remote_port_parent(node);
+ if (!node)
+ return NULL;
+
+ pr_debug("%s: remote port parent: %s\n", __func__, node->full_name);
+
+ return of_find_vpl(node);
+}
+
+int vpl_ioctl(struct vpl *vpl, unsigned int port,
+ unsigned int cmd, void *ptr)
+{
+ struct device_node *node, *endpoint;
+ int ret;
+
+ pr_debug("%s: %s port %d\n", __func__, vpl->node->full_name, port);
+
+ node = of_graph_get_port_by_id(vpl->node, port);
+ if (!node) {
+ pr_err("%s: no port %d on %s\n", __func__, port, vpl->node->full_name);
+ return -ENODEV;
+ }
+
+ for_each_child_of_node(node, endpoint) {
+ struct device_node *remote, *remote_parent;
+ struct vpl *remote_vpl;
+ u32 remote_port_id;
+
+ remote = of_graph_get_remote_port(endpoint);
+ if (!remote) {
+ pr_debug("%s: no remote for endpoint %s\n", __func__, endpoint->full_name);
+ continue;
+ }
+
+ of_property_read_u32(remote, "reg", &remote_port_id);
+
+ remote_parent = of_graph_get_remote_port_parent(endpoint);
+ if (!remote_parent) {
+ pr_debug("%s: no remote_parent\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!of_device_is_available(remote_parent))
+ continue;
+
+ remote_vpl = of_find_vpl(remote_parent);
+ if (!remote_vpl) {
+ pr_debug("%s: cannot find remote vpl %s\n", __func__, remote->full_name);
+ continue;
+ }
+
+ ret = remote_vpl->ioctl(remote_vpl, remote_port_id, cmd, ptr);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/include/fb.h b/include/fb.h
index d0bab48bdd..311d5db192 100644
--- a/include/fb.h
+++ b/include/fb.h
@@ -99,6 +99,7 @@ struct display_timings {
unsigned int num_modes;
struct fb_videomode *modes;
+ void *edid;
};
struct i2c_adapter;
@@ -143,6 +144,7 @@ struct fb_info {
};
struct display_timings *of_get_display_timings(struct device_node *np);
+void display_timings_release(struct display_timings *);
int register_framebuffer(struct fb_info *info);
diff --git a/include/of.h b/include/of.h
index 1db210b38a..e0ebc39b74 100644
--- a/include/of.h
+++ b/include/of.h
@@ -153,6 +153,8 @@ extern int of_device_is_available(const struct device_node *device);
extern struct device_node *of_get_parent(const struct device_node *node);
extern struct device_node *of_get_next_available_child(
const struct device_node *node, struct device_node *prev);
+struct device_node *of_get_next_child(const struct device_node *node,
+ struct device_node *prev);
extern int of_get_child_count(const struct device_node *parent);
extern int of_get_available_child_count(const struct device_node *parent);
extern struct device_node *of_get_child_by_name(const struct device_node *node,
@@ -308,6 +310,12 @@ static inline struct device_node *of_get_next_available_child(
return NULL;
}
+static inline struct device_node *of_get_next_child(const struct device_node *node,
+ struct device_node *prev)
+{
+ return NULL;
+}
+
static inline int of_get_child_count(const struct device_node *parent)
{
return -ENOSYS;
diff --git a/include/of_graph.h b/include/of_graph.h
new file mode 100644
index 0000000000..254dd2ca28
--- /dev/null
+++ b/include/of_graph.h
@@ -0,0 +1,50 @@
+/*
+ * OF graph binding parsing helpers
+ *
+ * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * Copyright (C) 2012 Renesas Electronics Corp.
+ * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ */
+#ifndef __LINUX_OF_GRAPH_H
+#define __LINUX_OF_GRAPH_H
+
+/**
+ * struct of_endpoint - the OF graph endpoint data structure
+ * @port: identifier (value of reg property) of a port this endpoint belongs to
+ * @id: identifier (value of reg property) of this endpoint
+ * @local_node: pointer to device_node of this endpoint
+ */
+struct of_endpoint {
+ unsigned int port;
+ unsigned int id;
+ const struct device_node *local_node;
+};
+
+/**
+ * for_each_endpoint_of_node - iterate over every endpoint in a device node
+ * @parent: parent device node containing ports and endpoints
+ * @child: loop variable pointing to the current endpoint node
+ *
+ * When breaking out of the loop, of_node_put(child) has to be called manually.
+ */
+#define for_each_endpoint_of_node(parent, child) \
+ for (child = of_graph_get_next_endpoint(parent, NULL); child != NULL; \
+ child = of_graph_get_next_endpoint(parent, child))
+
+int of_graph_parse_endpoint(const struct device_node *node,
+ struct of_endpoint *endpoint);
+struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
+struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
+ struct device_node *previous);
+struct device_node *of_graph_get_remote_port_parent(
+ const struct device_node *node);
+struct device_node *of_graph_get_remote_port(const struct device_node *node);
+int of_graph_port_is_available(struct device_node *node);
+
+#endif /* __LINUX_OF_GRAPH_H */
diff --git a/include/video/vpl.h b/include/video/vpl.h
new file mode 100644
index 0000000000..846007f4f5
--- /dev/null
+++ b/include/video/vpl.h
@@ -0,0 +1,46 @@
+#ifndef __VIDEO_VPL_H
+#define __VIDEO_VPL_H
+
+#include <fb.h>
+
+#define VPL_PREPARE 0x67660001
+#define VPL_UNPREPARE 0x67660002
+#define VPL_ENABLE 0x67660003
+#define VPL_DISABLE 0x67660004
+#define VPL_GET_VIDEOMODES 0x67660005
+
+struct vpl {
+ int (*ioctl)(struct vpl *, unsigned int port,
+ unsigned int cmd, void *ptr);
+ struct device_node *node;
+ struct list_head list;
+};
+
+int vpl_register(struct vpl *);
+int vpl_ioctl(struct vpl *, unsigned int port,
+ unsigned int cmd, void *ptr);
+
+struct vpl *of_vpl_get(struct device_node *node, int port);
+
+static inline int vpl_ioctl_prepare(struct vpl *vpl, unsigned int port,
+ struct fb_videomode *mode)
+{
+ return vpl_ioctl(vpl, port, VPL_PREPARE, mode);
+}
+
+static inline int vpl_ioctl_unprepare(struct vpl *vpl, unsigned int port)
+{
+ return vpl_ioctl(vpl, port, VPL_UNPREPARE, NULL);
+}
+
+static inline int vpl_ioctl_enable(struct vpl *vpl, unsigned int port)
+{
+ return vpl_ioctl(vpl, port, VPL_ENABLE, NULL);
+}
+
+static inline int vpl_ioctl_disable(struct vpl *vpl, unsigned int port)
+{
+ return vpl_ioctl(vpl, port, VPL_DISABLE, NULL);
+}
+
+#endif /* __VIDEO_VPL_H */