new file mode 100644
@@ -0,0 +1,44 @@
+Freescale MX3 IPU.
+==================
+
+Required properties:
+- compatible: Should be "fsl,<chip>-ipu". compatible chips include the imx31 and the
+ imx35.
+- reg: should be register base and length as documented in the datasheet.
+- clocks: Handle to the ipu_gate clock.
+- display: Phandle to a "fsl,mx3-parallel-display" compatible display node
+ which is described below.
+
+Example:
+
+ipu: ipu@53fc0000 {
+ compatible = "fsl,imx35-ipu";
+ reg = <0x53fc0000 0x4000>;
+ clocks = <&clks 55>;
+};
+
+Parallel display support
+========================
+
+Required properties:
+- compatible: Should be "fsl,mx3-parallel-display".
+- model : The user-visible name of the display.
+
+Optional properties:
+- interface_pix_fmt: How this display is connected to the
+ crtc. Currently supported types: "rgb24", "rgb565", "rgb666".
+
+It can also have an optional timing subnode as described in
+ Documentation/devicetree/bindings/video/display-timing.txt.
+
+Example:
+
+display0: display@di0 {
+ compatible = "fsl,mx3-parallel-display";
+ interface-pix-fmt = "rgb666";
+ model = "CMO-QVGA";
+};
+
+&ipu {
+ display = <&display0>;
+}
@@ -2330,6 +2330,8 @@ config FB_MX3
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
+ select VIDEOMODE_HELPERS
+ select FB_MODE_HELPERS
default y
help
This is a framebuffer device for the i.MX31 LCD Controller. So
@@ -31,6 +31,10 @@
#include <linux/platform_data/dma-imx.h>
#include <linux/platform_data/video-mx3fb.h>
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -237,6 +241,8 @@ static const struct fb_videomode mx3fb_modedb[] = {
struct mx3fb_data {
struct fb_info *fbi;
+ struct videomode *vm;
+ struct fb_videomode *fb_vm;
int backlight_level;
void __iomem *reg_base;
spinlock_t lock;
@@ -269,6 +275,7 @@ struct mx3fb_info {
struct scatterlist sg[2];
struct fb_var_screeninfo cur_var; /* current var info */
+ uint32_t flags;
};
static void mx3fb_dma_done(void *);
@@ -753,16 +760,32 @@ static int __set_par(struct fb_info *fbi, bool lock)
if (mx3_fbi->ipu_ch == IDMAC_SDC_0) {
memset(&sig_cfg, 0, sizeof(sig_cfg));
+
if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT)
sig_cfg.Hsync_pol = true;
+
if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)
sig_cfg.Vsync_pol = true;
- if (fbi->var.sync & FB_SYNC_CLK_INVERT)
- sig_cfg.clk_pol = true;
+
+ if (fbi->device->of_node) {
+ if (mx3_fbi->flags & FB_SYNC_CLK_INVERT)
+ sig_cfg.clk_pol = true;
+ } else {
+ if (fbi->var.sync & FB_SYNC_CLK_INVERT)
+ sig_cfg.clk_pol = true;
+ }
+
if (fbi->var.sync & FB_SYNC_DATA_INVERT)
sig_cfg.data_pol = true;
- if (fbi->var.sync & FB_SYNC_OE_ACT_HIGH)
- sig_cfg.enable_pol = true;
+
+ if (fbi->device->of_node) {
+ if (mx3_fbi->flags & FB_SYNC_OE_ACT_HIGH)
+ sig_cfg.enable_pol = true;
+ } else {
+ if (fbi->var.sync & FB_SYNC_OE_ACT_HIGH)
+ sig_cfg.enable_pol = true;
+ }
+
if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN)
sig_cfg.clkidle_en = true;
if (fbi->var.sync & FB_SYNC_CLK_SEL_EN)
@@ -1266,7 +1289,8 @@ static int mx3fb_map_video_memory(struct fb_info *fbi, unsigned int mem_len,
&addr, GFP_DMA | GFP_KERNEL);
if (!fbi->screen_base) {
- dev_err(fbi->device, "Cannot allocate %u bytes framebuffer memory\n",
+ dev_err(fbi->device,
+ "Cannot allocate %u bytes framebuffer memory\n",
mem_len);
retval = -EBUSY;
goto err0;
@@ -1280,7 +1304,8 @@ static int mx3fb_map_video_memory(struct fb_info *fbi, unsigned int mem_len,
mutex_unlock(&fbi->mm_lock);
dev_dbg(fbi->device, "allocated fb @ p=0x%08x, v=0x%p, size=%d.\n",
- (uint32_t) fbi->fix.smem_start, fbi->screen_base, fbi->fix.smem_len);
+ (uint32_t) fbi->fix.smem_start,
+ fbi->screen_base, fbi->fix.smem_len);
fbi->screen_size = fbi->fix.smem_len;
@@ -1351,21 +1376,68 @@ static struct fb_info *mx3fb_init_fbinfo(struct device *dev, struct fb_ops *ops)
return fbi;
}
+static int match_dt_disp_data(const char *property)
+{
+ if (!strcmp("rgb666", property))
+ return IPU_DISP_DATA_MAPPING_RGB666;
+ else if (!strcmp("rgb565", property))
+ return IPU_DISP_DATA_MAPPING_RGB565;
+ else if (!strcmp("rgb24", property))
+ return IPU_DISP_DATA_MAPPING_RGB888;
+ else
+ return -EINVAL;
+}
+
static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan)
{
struct device *dev = mx3fb->dev;
struct mx3fb_platform_data *mx3fb_pdata = dev_get_platdata(dev);
- const char *name = mx3fb_pdata->name;
+ struct device_node *np = dev->of_node;
+ const char *name;
+ const char *ipu_disp_format;
unsigned int irq;
struct fb_info *fbi;
struct mx3fb_info *mx3fbi;
const struct fb_videomode *mode;
int ret, num_modes;
+ struct device_node *display_np = NULL;
+
+ if (np) {
+ display_np = of_parse_phandle(np, "display", 0);
+ if (!display_np) {
+ dev_err(dev, "Can't get the display device node.\n");
+ return -EINVAL;
+ }
+
+ of_property_read_string(display_np, "interface-pix-fmt",
+ &ipu_disp_format);
+ if (!ipu_disp_format) {
+ mx3fb->disp_data_fmt = IPU_DISP_DATA_MAPPING_RGB666;
+ dev_warn(dev,
+ "ipu display data mapping was not defined, using the default rgb666.\n");
+ } else {
+ mx3fb->disp_data_fmt =
+ match_dt_disp_data(ipu_disp_format);
+ }
- if (mx3fb_pdata->disp_data_fmt >= ARRAY_SIZE(di_mappings)) {
- dev_err(dev, "Illegal display data format %d\n",
+ if (mx3fb->disp_data_fmt == -EINVAL) {
+ dev_err(dev, "Illegal display data format \"%s\"\n",
+ ipu_disp_format);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_string(display_np, "model", &name);
+ if (ret) {
+ dev_err(dev, "Missing display model name\n");
+ return -EINVAL;
+ }
+ } else {
+ name = mx3fb_pdata->name;
+ if (mx3fb_pdata->disp_data_fmt >= ARRAY_SIZE(di_mappings)) {
+ dev_err(dev, "Illegal display data format %d\n",
mx3fb_pdata->disp_data_fmt);
- return -EINVAL;
+ return -EINVAL;
+ }
}
ichan->client = mx3fb;
@@ -1386,12 +1458,36 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan)
goto emode;
}
- if (mx3fb_pdata->mode && mx3fb_pdata->num_modes) {
- mode = mx3fb_pdata->mode;
- num_modes = mx3fb_pdata->num_modes;
+ mx3fbi = fbi->par;
+
+ if (np) {
+ ret = of_get_videomode(display_np, mx3fb->vm,
+ OF_USE_NATIVE_MODE);
+ if (ret) {
+ dev_err(dev, "failed to get videomode from DT\n");
+ goto put_display_node;
+ }
+
+ ret = fb_videomode_from_videomode(mx3fb->vm, mx3fb->fb_vm);
+ if (ret < 0)
+ goto put_display_node;
+
+ if (mx3fb->vm->flags & DISPLAY_FLAGS_DE_HIGH)
+ mx3fbi->flags |= FB_SYNC_OE_ACT_HIGH;
+
+ if (mx3fb->vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+ mx3fbi->flags |= FB_SYNC_CLK_INVERT;
+
+ mode = mx3fb->fb_vm;
+ num_modes = 1;
} else {
- mode = mx3fb_modedb;
- num_modes = ARRAY_SIZE(mx3fb_modedb);
+ if (mx3fb_pdata->mode && mx3fb_pdata->num_modes) {
+ mode = mx3fb_pdata->mode;
+ num_modes = mx3fb_pdata->num_modes;
+ } else {
+ mode = mx3fb_modedb;
+ num_modes = ARRAY_SIZE(mx3fb_modedb);
+ }
}
if (!fb_find_mode(&fbi->var, fbi, fb_mode, mode,
@@ -1415,13 +1511,13 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan)
sdc_set_global_alpha(mx3fb, true, 0xFF);
sdc_set_color_key(mx3fb, IDMAC_SDC_0, false, 0);
- mx3fbi = fbi->par;
mx3fbi->idmac_channel = ichan;
mx3fbi->ipu_ch = ichan->dma_chan.chan_id;
mx3fbi->mx3fb = mx3fb;
mx3fbi->blank = FB_BLANK_NORMAL;
- mx3fb->disp_data_fmt = mx3fb_pdata->disp_data_fmt;
+ if (!np)
+ mx3fb->disp_data_fmt = mx3fb_pdata->disp_data_fmt;
init_completion(&mx3fbi->flip_cmpl);
disable_irq(ichan->eof_irq);
@@ -1440,6 +1536,8 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan)
return 0;
+put_display_node:
+ of_node_put(display_np);
erfb:
esetpar:
emode:
@@ -1455,17 +1553,21 @@ static bool chan_filter(struct dma_chan *chan, void *arg)
struct device *dev;
struct mx3fb_platform_data *mx3fb_pdata;
- if (!imx_dma_is_ipu(chan))
- return false;
-
if (!rq)
return false;
dev = rq->mx3fb->dev;
mx3fb_pdata = dev_get_platdata(dev);
- return rq->id == chan->chan_id &&
- mx3fb_pdata->dma_dev == chan->device->dev;
+ if (!imx_dma_is_ipu(chan) && mx3fb_pdata)
+ return false;
+
+ /* When using the devicetree, mx3fb_pdata is NULL */
+ if (mx3fb_pdata)
+ return rq->id == chan->chan_id &&
+ mx3fb_pdata->dma_dev == chan->device->dev;
+ else
+ return rq->id == chan->chan_id;
}
static void release_fbi(struct fb_info *fbi)
@@ -1487,6 +1589,9 @@ static int mx3fb_probe(struct platform_device *pdev)
dma_cap_mask_t mask;
struct dma_chan *chan;
struct dma_chan_request rq;
+ struct device_node *np = dev->of_node;
+ struct videomode *vm;
+ struct fb_videomode *fb_vm;
/*
* Display Interface (DI) and Synchronous Display Controller (SDC)
@@ -1508,7 +1613,33 @@ static int mx3fb_probe(struct platform_device *pdev)
goto eremap;
}
- pr_debug("Remapped %pR at %p\n", sdc_reg, mx3fb->reg_base);
+ /* The full IPU registers range is passed by the device tree,
+ * whereas the platform data only passes the SDC registers range.
+ */
+ if (np) {
+ vm = devm_kzalloc(&pdev->dev, sizeof(struct videomode),
+ GFP_KERNEL);
+ if (!vm) {
+ ret = -ENOMEM;
+ goto eremap;
+ }
+
+ fb_vm = devm_kzalloc(&pdev->dev, sizeof(struct fb_videomode),
+ GFP_KERNEL);
+ if (!fb_vm) {
+ ret = -ENOMEM;
+ goto eremap;
+ }
+
+ mx3fb->vm = vm;
+ mx3fb->fb_vm = fb_vm;
+
+ mx3fb->reg_base += MX3FB_REG_OFFSET;
+ pr_debug("Remapped %pR at %p\n", sdc_reg + MX3FB_REG_OFFSET,
+ mx3fb->reg_base);
+ } else {
+ pr_debug("Remapped %pR at %p\n", sdc_reg, mx3fb->reg_base);
+ }
/* IDMAC interface */
dmaengine_get();
@@ -1563,9 +1694,17 @@ static int mx3fb_remove(struct platform_device *dev)
return 0;
}
+static struct of_device_id mx3fb_of_dev_id[] = {
+ { .compatible = "fsl,imx31-ipu", },
+ { .compatible = "fsl,imx35-ipu", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mx3fb_of_dev_id);
+
static struct platform_driver mx3fb_driver = {
.driver = {
.name = MX3FB_NAME,
+ .of_match_table = mx3fb_of_dev_id,
.owner = THIS_MODULE,
},
.probe = mx3fb_probe,
This patch is based on: 838bdf7 video: mxsfb: fix broken videomode selection Signed-off-by: Denis Carikli <denis@eukrea.com> --- ChangeLog v6->v7: - Removed the Cc from the patch, they went into git send-email instead. ChangeLog v5->v6: - Shrinked the Cc list. - de-active and pixelclk-active dt properties are now handled in this patch to get rid of the "fbdev: Add the lacking FB_SYNC_* for matching the DISPLAY_FLAGS_*" patch ChangeLog v4->v5: - Added some people in the Cc list. - The full ipu register range is now passed to the driver, the code and the documentation were adapted to it. - Updated the documentation not to mention the lcd controller, the ipu was mentioned instead. - The ipu patch was removed from this patchset, as a consequence the mx3fb code has been adapted not to expect the dma ipu driver to be probed trough the device tree. ChangeLog v3->v4: - Updated bindings. - Updated documentation accordinly. - Updated code accordinly. - Fixed the lack of "ret =" in of_property_read_string(display_np, "model", &name); - Supressed some compilation warnings. ChangeLog v2->v3: - The device tree bindings were reworked in order to make it look more like the IPUv3 bindings. - The interface_pix_fmt property now looks like the IPUv3 one. --- .../devicetree/bindings/video/fsl,mx3-fb.txt | 44 +++++ drivers/video/fbdev/Kconfig | 2 + drivers/video/fbdev/mx3fb.c | 185 +++++++++++++++++--- 3 files changed, 208 insertions(+), 23 deletions(-) create mode 100644 Documentation/devicetree/bindings/video/fsl,mx3-fb.txt