summaryrefslogtreecommitdiffstats
path: root/drivers/video
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 /drivers/video
parent0c5c47b511763a2a9b216918e041178826fbdacc (diff)
parent427d637327bb4714085ea748bc5e7564ea0acfe6 (diff)
downloadbarebox-98b5672c4d474f25227450a29f82f14e84c85f3c.tar.gz
barebox-98b5672c4d474f25227450a29f82f14e84c85f3c.tar.xz
Merge branch 'for-next/vpl'
Diffstat (limited to 'drivers/video')
-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
11 files changed, 778 insertions, 182 deletions
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;
+}