Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* [RFC v2] video: ARM CLCD: Add DT & CDF support
From: Pawel Moll @ 2013-04-18 17:33 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel, Laurent Pinchart, Linus Walleij, Pawel Moll
In-Reply-To: <20130418102444.GL14496@n2100.arm.linux.org.uk>

This patch adds basic DT bindings for the PL11x CLCD cells
and make their fbdev driver use them, together with the
Common Display Framework.

The DT provides information about the hardware configuration
and limitations (eg. the largest supported resolution)
but the video modes come exclusively from the Common
Display Framework drivers, referenced to by the standard CDF
binding.

Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
 .../devicetree/bindings/video/arm,pl11x.txt        |   35 +++
 drivers/video/Kconfig                              |    1 +
 drivers/video/amba-clcd.c                          |  247 ++++++++++++++++++++
 include/linux/amba/clcd.h                          |    2 +
 4 files changed, 285 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/arm,pl11x.txt

diff --git a/Documentation/devicetree/bindings/video/arm,pl11x.txt b/Documentation/devicetree/bindings/video/arm,pl11x.txt
new file mode 100644
index 0000000..ee9534a
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/arm,pl11x.txt
@@ -0,0 +1,35 @@
+* ARM PrimeCell Color LCD Controller (CLCD) PL110/PL111
+
+Required properties:
+
+- compatible : must be one of:
+			"arm,pl110", "arm,primecell"
+			"arm,pl111", "arm,primecell"
+- reg : base address and size of the control registers block
+- interrupts : the combined interrupt
+- clocks : phandles to the CLCD (pixel) clock and the APB clocks
+- clock-names : "clcdclk", "apb_pclk"
+- display : phandle to the display entity connected to the controller
+
+Optional properties:
+
+- label : string describing the controller location and/or usage
+- video-ram : phandle to DT node of the specialized video RAM to be used
+- max-hactive : maximum frame buffer width in pixels
+- max-vactive : maximum frame buffer height in pixels
+- max-bpp : maximum number of bits per pixel
+- big-endian-pixels : defining this property makes the pixel bytes being
+			accessed in Big Endian organization
+
+Example:
+
+			clcd@1f0000 {
+				compatible = "arm,pl111", "arm,primecell";
+				reg = <0x1f0000 0x1000>;
+				interrupts = <14>;
+				clocks = <&v2m_oscclk1>, <&smbclk>;
+				clock-names = "clcdclk", "apb_pclk";
+				label = "IOFPGA CLCD";
+				video-ram = <&v2m_vram>;
+				display = <&v2m_muxfpga>;
+			};
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 281e548..bad8166 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -340,6 +340,7 @@ config FB_ARMCLCD
 	select FB_CFB_FILLRECT
 	select FB_CFB_COPYAREA
 	select FB_CFB_IMAGEBLIT
+	select FB_MODE_HELPERS if OF
 	help
 	  This framebuffer device driver is for the ARM PrimeCell PL110
 	  Colour LCD controller.  ARM PrimeCells provide the building
diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c
index 0a2cce7..853f74c 100644
--- a/drivers/video/amba-clcd.c
+++ b/drivers/video/amba-clcd.c
@@ -25,6 +25,11 @@
 #include <linux/amba/clcd.h>
 #include <linux/clk.h>
 #include <linux/hardirq.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <video/display.h>
+#include <video/videomode.h>
 
 #include <asm/sizes.h>
 
@@ -62,6 +67,10 @@ static void clcdfb_disable(struct clcd_fb *fb)
 {
 	u32 val;
 
+	if (fb->panel->display)
+		display_entity_set_state(fb->panel->display,
+				DISPLAY_ENTITY_STATE_OFF);
+
 	if (fb->board->disable)
 		fb->board->disable(fb);
 
@@ -115,6 +124,11 @@ static void clcdfb_enable(struct clcd_fb *fb, u32 cntl)
 	 */
 	if (fb->board->enable)
 		fb->board->enable(fb);
+
+	if (fb->panel->display)
+		display_entity_set_state(fb->panel->display,
+				DISPLAY_ENTITY_STATE_ON);
+
 }
 
 static int
@@ -304,6 +318,13 @@ static int clcdfb_set_par(struct fb_info *info)
 
 	clcdfb_enable(fb, regs.cntl);
 
+	if (fb->panel->display) {
+		struct videomode mode;
+
+		videomode_from_fb_var_screeninfo(&fb->fb.var, &mode);
+		display_entity_update(fb->panel->display, &mode);
+	}
+
 #ifdef DEBUG
 	printk(KERN_INFO
 	       "CLCD: Registers set to\n"
@@ -542,6 +563,229 @@ static int clcdfb_register(struct clcd_fb *fb)
 	return ret;
 }
 
+#if defined(CONFIG_OF)
+static int clcdfb_of_get_tft_parallel_panel(struct clcd_panel *panel,
+		struct display_entity_interface_params *params)
+{
+	int r = params->p.tft_parallel.r_bits;
+	int g = params->p.tft_parallel.g_bits;
+	int b = params->p.tft_parallel.b_bits;
+
+	/* Bypass pixel clock divider, data output on the falling edge */
+	panel->tim2 = TIM2_BCD | TIM2_IPC;
+
+	/* TFT display, vert. comp. interrupt at the start of the back porch */
+	panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1);
+
+	if (r >= 4 && g >= 4 && b >= 4)
+		panel->caps |= CLCD_CAP_444;
+	if (r >= 5 && g >= 5 && b >= 5)
+		panel->caps |= CLCD_CAP_5551;
+	if (r >= 5 && g >= 6 && b >= 5)
+		panel->caps |= CLCD_CAP_565;
+	if (r >= 8 && g >= 8 && b >= 8)
+		panel->caps |= CLCD_CAP_888;
+
+	if (params->p.tft_parallel.r_b_swapped)
+		panel->caps &= ~CLCD_CAP_RGB;
+	else
+		panel->caps &= ~CLCD_CAP_BGR;
+
+	return 0;
+}
+
+static int clcdfb_of_init_display(struct clcd_fb *fb)
+{
+	struct device_node *node = fb->dev->dev.of_node;
+	struct display_entity_interface_params params;
+	const struct videomode *modes;
+	int modes_num;
+	int best_mode = -1;
+	u32 max_bpp = 32;
+	u32 max_hactive = (u32)~0UL;
+	u32 max_vactive = (u32)~0UL;
+	unsigned int width, height;
+	char *mode_name;
+	int i, err;
+
+	fb->panel = devm_kzalloc(&fb->dev->dev, sizeof(*fb->panel), GFP_KERNEL);
+	if (!fb->panel)
+		return -ENOMEM;
+
+	fb->panel->display = of_display_entity_get(node, 0);
+	if (!fb->panel->display)
+		return -EPROBE_DEFER;
+
+	modes_num = display_entity_get_modes(fb->panel->display, &modes);
+	if (modes_num < 0)
+		return modes_num;
+
+	/* Pick the "best" (the widest, then the highest) mode from the list */
+	of_property_read_u32(node, "max-hactive", &max_hactive);
+	of_property_read_u32(node, "max-vactive", &max_vactive);
+	for (i = 0; i < modes_num; i++) {
+		if (modes[i].hactive > max_hactive ||
+				modes[i].vactive > max_vactive)
+			continue;
+		if (best_mode < 0 ||
+				(modes[i].hactive >= modes[best_mode].hactive &&
+				modes[i].vactive > modes[best_mode].vactive))
+			best_mode = i;
+	}
+	if (best_mode < 0)
+		return -ENODEV;
+
+	err = fb_videomode_from_videomode(&modes[best_mode], &fb->panel->mode);
+	if (err)
+		return err;
+
+	i = snprintf(NULL, 0, "%ux%u@%u", fb->panel->mode.xres,
+			fb->panel->mode.yres, fb->panel->mode.refresh);
+	mode_name = devm_kzalloc(&fb->dev->dev, i + 1, GFP_KERNEL);
+	snprintf(mode_name, i + 1, "%ux%u@%u", fb->panel->mode.xres,
+			fb->panel->mode.yres, fb->panel->mode.refresh);
+	fb->panel->mode.name = mode_name;
+
+	of_property_read_u32(node, "max-bpp", &max_bpp);
+	fb->panel->bpp = max_bpp;
+
+	if (of_property_read_bool(node, "big-endian-pixels"))
+		fb->panel->cntl |= CNTL_BEBO;
+
+	if (display_entity_get_size(fb->panel->display, &width, &height) != 0)
+		width = height = -1;
+	fb->panel->width = width;
+	fb->panel->height = height;
+
+	err = display_entity_get_params(fb->panel->display, &params);
+	if (err)
+		return err;
+
+	switch (params.type) {
+	case DISPLAY_ENTITY_INTERFACE_TFT_PARALLEL:
+		return clcdfb_of_get_tft_parallel_panel(fb->panel, &params);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int clcdfb_of_vram_setup(struct clcd_fb *fb)
+{
+	const __be32 *prop = of_get_property(fb->dev->dev.of_node, "video-ram",
+			NULL);
+	struct device_node *node = of_find_node_by_phandle(be32_to_cpup(prop));
+	u64 size;
+	int err;
+
+	if (!node)
+		return -ENODEV;
+
+	err = clcdfb_of_init_display(fb);
+	if (err)
+		return err;
+
+	fb->fb.screen_base = of_iomap(node, 0);
+	if (!fb->fb.screen_base)
+		return -ENOMEM;
+
+	fb->fb.fix.smem_start = of_translate_address(node,
+			of_get_address(node, 0, &size, NULL));
+	fb->fb.fix.smem_len = size;
+
+	return 0;
+}
+
+static int clcdfb_of_vram_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+	unsigned long off, user_size, kernel_size;
+
+	off = vma->vm_pgoff << PAGE_SHIFT;
+	user_size = vma->vm_end - vma->vm_start;
+	kernel_size = fb->fb.fix.smem_len;
+
+	if (off >= kernel_size || user_size > (kernel_size - off))
+		return -ENXIO;
+
+	return remap_pfn_range(vma, vma->vm_start,
+			__phys_to_pfn(fb->fb.fix.smem_start) + vma->vm_pgoff,
+			user_size,
+			pgprot_writecombine(vma->vm_page_prot));
+}
+
+static void clcdfb_of_vram_remove(struct clcd_fb *fb)
+{
+	iounmap(fb->fb.screen_base);
+}
+
+static int clcdfb_of_dma_setup(struct clcd_fb *fb)
+{
+	unsigned long framesize;
+	dma_addr_t dma;
+	int err;
+
+	err = clcdfb_of_init_display(fb);
+	if (err)
+		return err;
+
+	framesize = fb->panel->mode.xres * fb->panel->mode.yres *
+			fb->panel->bpp / 8;
+	fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev, framesize,
+			&dma, GFP_KERNEL);
+	if (!fb->fb.screen_base)
+		return -ENOMEM;
+
+	fb->fb.fix.smem_start = dma;
+	fb->fb.fix.smem_len = framesize;
+
+	return 0;
+}
+
+static int clcdfb_of_dma_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+	return dma_mmap_writecombine(&fb->dev->dev, vma, fb->fb.screen_base,
+			fb->fb.fix.smem_start, fb->fb.fix.smem_len);
+}
+
+static void clcdfb_of_dma_remove(struct clcd_fb *fb)
+{
+	dma_free_writecombine(&fb->dev->dev, fb->fb.fix.smem_len,
+			fb->fb.screen_base, fb->fb.fix.smem_start);
+}
+
+static struct clcd_board *clcdfb_of_get_board(struct amba_device *dev)
+{
+	struct clcd_board *board = devm_kzalloc(&dev->dev, sizeof(*board),
+			GFP_KERNEL);
+	struct device_node *node = dev->dev.of_node;
+
+	if (!board)
+		return NULL;
+
+	board->name = of_get_property(node, "label", NULL);
+	if (!board->name)
+		board->name = of_node_full_name(node);
+	board->caps = CLCD_CAP_ALL;
+	board->check = clcdfb_check;
+	board->decode = clcdfb_decode;
+	if (of_find_property(node, "video-ram", NULL)) {
+		board->setup = clcdfb_of_vram_setup;
+		board->mmap = clcdfb_of_vram_mmap;
+		board->remove = clcdfb_of_vram_remove;
+	} else {
+		board->setup = clcdfb_of_dma_setup;
+		board->mmap = clcdfb_of_dma_mmap;
+		board->remove = clcdfb_of_dma_remove;
+	}
+
+	return board;
+}
+#else
+static struct clcd_board *clcdfb_of_get_board(struct amba_dev *dev)
+{
+	return NULL;
+}
+#endif
+
 static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
 {
 	struct clcd_board *board = dev->dev.platform_data;
@@ -549,6 +793,9 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
 	int ret;
 
 	if (!board)
+		board = clcdfb_of_get_board(dev);
+
+	if (!board)
 		return -EINVAL;
 
 	ret = amba_request_regions(dev, NULL);
diff --git a/include/linux/amba/clcd.h b/include/linux/amba/clcd.h
index e82e3ee..73b199b 100644
--- a/include/linux/amba/clcd.h
+++ b/include/linux/amba/clcd.h
@@ -10,6 +10,7 @@
  * for more details.
  */
 #include <linux/fb.h>
+#include <video/display.h>
 
 /*
  * CLCD Controller Internal Register addresses
@@ -105,6 +106,7 @@ struct clcd_panel {
 				fixedtimings:1,
 				grayscale:1;
 	unsigned int		connector;
+	struct display_entity	*display;
 };
 
 struct clcd_regs {
-- 
1.7.10.4



^ permalink raw reply related

* Re: [PATCH v4 2/2] video: imxfb: Add DT support
From: Jean-Christophe PLAGNIOL-VILLARD @ 2013-04-18 16:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366290183-367-3-git-send-email-mpa@pengutronix.de>

On 15:03 Thu 18 Apr     , Markus Pargmann wrote:
> Add devicetree support for imx framebuffer driver. It uses the generic
> display bindings and helper functions.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> Cc: Fabio Estevam <festevam@gmail.com>
> Cc: Mark Rutland <mark.rutland@arm.com>
> ---
> 
> Notes:
>     Changes in v4:
>     - Remove eukrea specific dmacr property.
>     - Add optional dmacr property. If not present, the dmacr reset value is not
>       changed.
>     
>     Changes in v3:
>     - Fix returncodes of of_read_mode function and print error messages
>     - Introduce a lower bound check for bits per pixel
>     - Calculate correct bytes per pixel value before using it for the calculation of
>     	memory size
>     - Change DT property names
>     
>     Changes in v2:
>     - Removed pwmr register property
>     - Cleanup of devicetree binding documentation
>     - Use default values for pwmr and lscr1
> 
>  .../devicetree/bindings/video/fsl,imx-fb.txt       |  51 ++++++
>  drivers/video/imxfb.c                              | 194 +++++++++++++++++----
>  2 files changed, 210 insertions(+), 35 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/video/fsl,imx-fb.txt
> 
> diff --git a/Documentation/devicetree/bindings/video/fsl,imx-fb.txt b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt
> new file mode 100644
> index 0000000..aff16a4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt
> @@ -0,0 +1,51 @@
> +Freescale imx21 Framebuffer
> +
> +This framebuffer driver supports devices imx1, imx21, imx25, and imx27.
> +
> +Required properties:
> +- compatible : "fsl,<chip>-fb", chip should be imx1 or imx21
> +- reg : Should contain 1 register ranges(address and length)
> +- interrupts : One interrupt of the fb dev
> +
> +Required nodes:
> +- display: Phandle to a display node as described in
> +	Documentation/devicetree/bindings/video/display-timing.txt
> +	Additional, the display node has to define properties:
> +	- fsl,bpp: Bits per pixel
> +	- fsl,pcr: LCDC PCR value
> +
> +Optional properties:
> +- fsl,dmacr: DMA Control Register value. This is optional. By default, the
> +	register is not modified as recommended by the datasheet.
> +- fsl,lscr1: LCDC Sharp Configuration Register value.
> +
> +Example:
> +
> +	imxfb: fb@10021000 {
> +		compatible = "fsl,imx27-fb", "fsl,imx21-fb";
you put both when in the doc you request one
> +		interrupts = <61>;
> +		reg = <0x10021000 0x1000>;
> +		display = <&display0>;
> +	};
> +
> +	...
> +
> +	display0: display0 {
> +		model = "Primeview-PD050VL1";
> +		native-mode = <&timing_disp0>;
> +		fsl,bpp = <16>;		/* non-standard but required */
there is a generic binding bit-per-pixel use a cross other IP
> +		fsl,pcr = <0xf0c88080>;	/* non-standard but required */
> +		display-timings {
> +			timing_disp0: 640x480 {
> +				hactive = <640>;
> +				vactive = <480>;
> +				hback-porch = <112>;
> +				hfront-porch = <36>;
> +				hsync-len = <32>;
> +				vback-porch = <33>;
> +				vfront-porch = <33>;
> +				vsync-len = <2>;
> +				clock-frequency = <25000000>;
> +			};
> +		};
> +	};
> diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
> index ef2b587..e0230f8 100644
> --- a/drivers/video/imxfb.c
> +++ b/drivers/video/imxfb.c
> @@ -32,6 +32,12 @@
>  #include <linux/io.h>
>  #include <linux/math64.h>
>  #include <linux/uaccess.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +
> +#include <video/of_display_timing.h>
> +#include <video/of_videomode.h>
> +#include <video/videomode.h>
>  
>  #include <linux/platform_data/video-imxfb.h>
>  
> @@ -117,10 +123,11 @@
>  #define LGWCR_GWAV(alpha)	(((alpha) & 0xff) << 24)
>  #define LGWCR_GWE	(1 << 22)
>  
> +#define IMXFB_LSCR1_DEFAULT 0x00120300
> +
>  /* Used fb-mode. Can be set on kernel command line, therefore file-static. */
>  static const char *fb_mode;
>  
> -
>  /*
>   * These are the bitfields for each
>   * display depth that we support.
> @@ -192,6 +199,19 @@ static struct platform_device_id imxfb_devtype[] = {
>  };
>  MODULE_DEVICE_TABLE(platform, imxfb_devtype);
>  
> +static struct of_device_id imxfb_of_dev_id[] = {
> +	{
> +		.compatible = "fsl,imx1-fb",
> +		.data = &imxfb_devtype[IMX1_FB],
> +	}, {
> +		.compatible = "fsl,imx21-fb",
> +		.data = &imxfb_devtype[IMX21_FB],
> +	}, {
> +		/* sentinel */
> +	}
> +};
> +MODULE_DEVICE_TABLE(of, imxfb_of_dev_id);
> +
>  static inline int is_imx1_fb(struct imxfb_info *fbi)
>  {
>  	return fbi->devtype = IMX1_FB;
> @@ -324,6 +344,9 @@ static const struct imx_fb_videomode *imxfb_find_mode(struct imxfb_info *fbi)
>  	struct imx_fb_videomode *m;
>  	int i;
>  
> +	if (!fb_mode)
> +		return &fbi->mode[0];
> +
>  	for (i = 0, m = &fbi->mode[0]; i < fbi->num_modes; i++, m++) {
>  		if (!strcmp(m->mode.name, fb_mode))
>  			return m;
> @@ -479,6 +502,9 @@ static int imxfb_bl_update_status(struct backlight_device *bl)
>  	struct imxfb_info *fbi = bl_get_data(bl);
>  	int brightness = bl->props.brightness;
>  
> +	if (!fbi->pwmr)
> +		return 0;
> +
>  	if (bl->props.power != FB_BLANK_UNBLANK)
>  		brightness = 0;
>  	if (bl->props.fb_blank != FB_BLANK_UNBLANK)
> @@ -719,10 +745,14 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
>  
>  	writel(fbi->pcr, fbi->regs + LCDC_PCR);
>  #ifndef PWMR_BACKLIGHT_AVAILABLE
> -	writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
> +	if (fbi->pwmr)
> +		writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
>  #endif
>  	writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
> -	writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
> +
> +	/* dmacr = 0 is no valid value, as we need DMA control marks. */
> +	if (fbi->dmacr)
> +		writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
>  
>  	return 0;
>  }
> @@ -758,13 +788,12 @@ static int imxfb_resume(struct platform_device *dev)
>  #define imxfb_resume	NULL
>  #endif
>  
> -static int __init imxfb_init_fbinfo(struct platform_device *pdev)
> +static int imxfb_init_fbinfo(struct platform_device *pdev)
>  {
>  	struct imx_fb_platform_data *pdata = pdev->dev.platform_data;
>  	struct fb_info *info = dev_get_drvdata(&pdev->dev);
>  	struct imxfb_info *fbi = info->par;
> -	struct imx_fb_videomode *m;
> -	int i;
> +	struct device_node *np;
>  
>  	pr_debug("%s\n",__func__);
>  
> @@ -795,41 +824,95 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev)
>  	info->fbops			= &imxfb_ops;
>  	info->flags			= FBINFO_FLAG_DEFAULT |
>  					  FBINFO_READS_FAST;
> -	info->var.grayscale		= pdata->cmap_greyscale;
> -	fbi->cmap_inverse		= pdata->cmap_inverse;
> -	fbi->cmap_static		= pdata->cmap_static;
> -	fbi->lscr1			= pdata->lscr1;
> -	fbi->dmacr			= pdata->dmacr;
> -	fbi->pwmr			= pdata->pwmr;
> -	fbi->lcd_power			= pdata->lcd_power;
> -	fbi->backlight_power		= pdata->backlight_power;
> -
> -	for (i = 0, m = &pdata->mode[0]; i < pdata->num_modes; i++, m++)
> -		info->fix.smem_len = max_t(size_t, info->fix.smem_len,
> -				m->mode.xres * m->mode.yres * m->bpp / 8);
> +	if (pdata) {
> +		info->var.grayscale		= pdata->cmap_greyscale;
> +		fbi->cmap_inverse		= pdata->cmap_inverse;
> +		fbi->cmap_static		= pdata->cmap_static;
> +		fbi->lscr1			= pdata->lscr1;
> +		fbi->dmacr			= pdata->dmacr;
> +		fbi->pwmr			= pdata->pwmr;
> +		fbi->lcd_power			= pdata->lcd_power;
> +		fbi->backlight_power		= pdata->backlight_power;
> +	} else {
> +		np = pdev->dev.of_node;
> +		info->var.grayscale = of_property_read_bool(np,
> +						"cmap-greyscale");
> +		fbi->cmap_inverse = of_property_read_bool(np, "cmap-inverse");
> +		fbi->cmap_static = of_property_read_bool(np, "cmap-static");
> +
> +		fbi->lscr1 = IMXFB_LSCR1_DEFAULT;
> +		of_property_read_u32(np, "fsl,lscr1", &fbi->lscr1);
> +
> +		of_property_read_u32(np, "fsl,dmacr", &fbi->dmacr);
> +
> +		/* These two function pointers could be used by some specific
> +		 * platforms. */
> +		fbi->lcd_power = NULL;
> +		fbi->backlight_power = NULL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int imxfb_of_read_mode(struct device *dev, struct device_node *np,
> +		struct imx_fb_videomode *imxfb_mode)
> +{
> +	int ret;
> +	struct fb_videomode *of_mode = &imxfb_mode->mode;
> +	u32 bpp;
> +	u32 pcr;
> +
> +	ret = of_property_read_string(np, "model", &of_mode->name);
> +	if (ret)
> +		of_mode->name = NULL;
> +
> +	ret = of_get_fb_videomode(np, of_mode, OF_USE_NATIVE_MODE);
> +	if (ret) {
> +		dev_err(dev, "Failed to get videomode from DT\n");
> +		return ret;
> +	}
> +
> +	ret = of_property_read_u32(np, "fsl,bpp", &bpp);
> +	ret |= of_property_read_u32(np, "fsl,pcr", &pcr);
> +
> +	if (ret) {
> +		dev_err(dev, "Failed to read bpp and pcr from DT\n");
> +		return -EINVAL;
> +	}
> +
> +	if (bpp < 1 || bpp > 255) {
> +		dev_err(dev, "Bits per pixel have to be between 1 and 255\n");
> +		return -EINVAL;
> +	}
> +
> +	imxfb_mode->bpp = bpp;
> +	imxfb_mode->pcr = pcr;
>  
>  	return 0;
>  }
>  
> -static int __init imxfb_probe(struct platform_device *pdev)
> +static int imxfb_probe(struct platform_device *pdev)
>  {
>  	struct imxfb_info *fbi;
>  	struct fb_info *info;
>  	struct imx_fb_platform_data *pdata;
>  	struct resource *res;
> +	struct imx_fb_videomode *m;
> +	const struct of_device_id *of_id;
>  	int ret, i;
> +	int bytes_per_pixel;
>  
>  	dev_info(&pdev->dev, "i.MX Framebuffer driver\n");
>  
> +	of_id = of_match_device(imxfb_of_dev_id, &pdev->dev);
> +	if (of_id)
> +		pdev->id_entry = of_id->data;
> +
>  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	if (!res)
>  		return -ENODEV;
>  
>  	pdata = pdev->dev.platform_data;
> -	if (!pdata) {
> -		dev_err(&pdev->dev,"No platform_data available\n");
> -		return -ENOMEM;
> -	}
>  
>  	info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev);
>  	if (!info)
> @@ -837,15 +920,55 @@ static int __init imxfb_probe(struct platform_device *pdev)
>  
>  	fbi = info->par;
>  
> -	if (!fb_mode)
> -		fb_mode = pdata->mode[0].mode.name;
> -
>  	platform_set_drvdata(pdev, info);
>  
>  	ret = imxfb_init_fbinfo(pdev);
>  	if (ret < 0)
>  		goto failed_init;
>  
> +	if (pdata) {
> +		if (!fb_mode)
> +			fb_mode = pdata->mode[0].mode.name;
> +
> +		fbi->mode = pdata->mode;
> +		fbi->num_modes = pdata->num_modes;
> +	} else {
> +		struct device_node *display_np;
> +		fb_mode = NULL;
> +
> +		display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);
> +		if (!display_np) {
> +			dev_err(&pdev->dev, "No display defined in devicetree\n");
> +			ret = -EINVAL;
> +			goto failed_of_parse;
> +		}
> +
> +		/*
> +		 * imxfb does not support more modes, we choose only the native
> +		 * mode.
> +		 */
> +		fbi->num_modes = 1;
> +
> +		fbi->mode = devm_kzalloc(&pdev->dev,
> +				sizeof(struct imx_fb_videomode), GFP_KERNEL);
> +		if (!fbi->mode) {
> +			ret = -ENOMEM;
> +			goto failed_of_parse;
> +		}
> +
> +		ret = imxfb_of_read_mode(&pdev->dev, display_np, fbi->mode);
> +		if (ret)
> +			goto failed_of_parse;
> +	}
> +
> +	/* Calculate maximum bytes used per pixel. In most cases this should
> +	 * be the same as m->bpp/8 */
> +	m = &fbi->mode[0];
> +	bytes_per_pixel = (m->bpp + 7) / 8;
> +	for (i = 0; i < fbi->num_modes; i++, m++)
> +		info->fix.smem_len = max_t(size_t, info->fix.smem_len,
> +				m->mode.xres * m->mode.yres * bytes_per_pixel);
> +
>  	res = request_mem_region(res->start, resource_size(res),
>  				DRIVER_NAME);
>  	if (!res) {
> @@ -878,7 +1001,8 @@ static int __init imxfb_probe(struct platform_device *pdev)
>  		goto failed_ioremap;
>  	}
>  
> -	if (!pdata->fixed_screen_cpu) {
> +	/* Seems not being used by anyone, so no support for oftree */
> +	if (!pdata || !pdata->fixed_screen_cpu) {
>  		fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
>  		fbi->map_cpu = dma_alloc_writecombine(&pdev->dev,
>  				fbi->map_size, &fbi->map_dma, GFP_KERNEL);
> @@ -903,18 +1027,16 @@ static int __init imxfb_probe(struct platform_device *pdev)
>  		info->fix.smem_start = fbi->screen_dma;
>  	}
>  
> -	if (pdata->init) {
> +	if (pdata && pdata->init) {
>  		ret = pdata->init(fbi->pdev);
>  		if (ret)
>  			goto failed_platform_init;
>  	}
>  
> -	fbi->mode = pdata->mode;
> -	fbi->num_modes = pdata->num_modes;
>  
>  	INIT_LIST_HEAD(&info->modelist);
> -	for (i = 0; i < pdata->num_modes; i++)
> -		fb_add_videomode(&pdata->mode[i].mode, &info->modelist);
> +	for (i = 0; i < fbi->num_modes; i++)
> +		fb_add_videomode(&fbi->mode[i].mode, &info->modelist);
>  
>  	/*
>  	 * This makes sure that our colour bitfield
> @@ -944,10 +1066,10 @@ static int __init imxfb_probe(struct platform_device *pdev)
>  failed_register:
>  	fb_dealloc_cmap(&info->cmap);
>  failed_cmap:
> -	if (pdata->exit)
> +	if (pdata && pdata->exit)
>  		pdata->exit(fbi->pdev);
>  failed_platform_init:
> -	if (!pdata->fixed_screen_cpu)
> +	if (pdata && !pdata->fixed_screen_cpu)
>  		dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu,
>  			fbi->map_dma);
>  failed_map:
> @@ -956,6 +1078,7 @@ failed_ioremap:
>  failed_getclock:
>  	release_mem_region(res->start, resource_size(res));
>  failed_req:
> +failed_of_parse:
>  	kfree(info->pseudo_palette);
>  failed_init:
>  	platform_set_drvdata(pdev, NULL);
> @@ -980,7 +1103,7 @@ static int imxfb_remove(struct platform_device *pdev)
>  	unregister_framebuffer(info);
>  
>  	pdata = pdev->dev.platform_data;
> -	if (pdata->exit)
> +	if (pdata && pdata->exit)
>  		pdata->exit(fbi->pdev);
>  
>  	fb_dealloc_cmap(&info->cmap);
> @@ -1009,6 +1132,7 @@ static struct platform_driver imxfb_driver = {
>  	.shutdown	= imxfb_shutdown,
>  	.driver		= {
>  		.name	= DRIVER_NAME,
> +		.of_match_table = imxfb_of_dev_id,
>  	},
>  	.id_table	= imxfb_devtype,
>  };
> -- 
> 1.8.1.5
> 

^ permalink raw reply

* Re: [PATCH] video: mxsfb: Fix colors display on lower color depth
From: Shawn Guo @ 2013-04-18 14:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366277032-671-1-git-send-email-maxime.ripard@free-electrons.com>

Copy Sascha.

Shawn

On Thu, Apr 18, 2013 at 11:23:52AM +0200, Maxime Ripard wrote:
> The current code always registers as a 32 bits display, and uses the
> hardware to drop the MSB of each color to abjust to the interface width
> used by the panel.
> 
> This results on 18 bits (and probably 16 bits display as well) in colors
> being displayed poorly, because the MSB are obviously the most important
> bits for each color definition.
> 
> The default controller behaviour when using an interface width smaller
> than the color depth is to drop the LSBs of each color, which makes more
> sense because you lose the least important part of the color definition.
> 
> So, to fix the colors display, just get back to the default controller
> behaviour.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/video/mxsfb.c |    5 -----
>  1 file changed, 5 deletions(-)
> 
> diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c
> index 755556c..2cfaf8b 100644
> --- a/drivers/video/mxsfb.c
> +++ b/drivers/video/mxsfb.c
> @@ -424,11 +424,6 @@ static int mxsfb_set_par(struct fb_info *fb_info)
>  			return -EINVAL;
>  		case STMLCDIF_16BIT:
>  		case STMLCDIF_18BIT:
> -			/* 24 bit to 18 bit mapping */
> -			ctrl |= CTRL_DF24; /* ignore the upper 2 bits in
> -					    *  each colour component
> -					    */
> -			break;
>  		case STMLCDIF_24BIT:
>  			/* real 24 bit */
>  			break;
> -- 
> 1.7.10.4
> 


^ permalink raw reply

* Re: [PATCH] video: mxsfb: Fix colors display on lower color depth
From: Marek Vasut @ 2013-04-18 14:00 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366277032-671-1-git-send-email-maxime.ripard@free-electrons.com>

Dear Maxime Ripard,

> The current code always registers as a 32 bits display, and uses the
> hardware to drop the MSB of each color to abjust to the interface width
> used by the panel.
> 
> This results on 18 bits (and probably 16 bits display as well) in colors
> being displayed poorly, because the MSB are obviously the most important
> bits for each color definition.
> 
> The default controller behaviour when using an interface width smaller
> than the color depth is to drop the LSBs of each color, which makes more
> sense because you lose the least important part of the color definition.
> 
> So, to fix the colors display, just get back to the default controller
> behaviour.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Isn't this what M28EVK is doing already? This was last tested on 3.9-rc3 I 
think:

bits-per-pixel = <16>;
bus-width = <18>;

But it's also true we have all 24 data lines routed to the LCD.

Best regards,
Marek Vasut

^ permalink raw reply

* [PATCH v4 2/2] video: imxfb: Add DT support
From: Markus Pargmann @ 2013-04-18 13:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366290183-367-1-git-send-email-mpa@pengutronix.de>

Add devicetree support for imx framebuffer driver. It uses the generic
display bindings and helper functions.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
Cc: Fabio Estevam <festevam@gmail.com>
Cc: Mark Rutland <mark.rutland@arm.com>
---

Notes:
    Changes in v4:
    - Remove eukrea specific dmacr property.
    - Add optional dmacr property. If not present, the dmacr reset value is not
      changed.
    
    Changes in v3:
    - Fix returncodes of of_read_mode function and print error messages
    - Introduce a lower bound check for bits per pixel
    - Calculate correct bytes per pixel value before using it for the calculation of
    	memory size
    - Change DT property names
    
    Changes in v2:
    - Removed pwmr register property
    - Cleanup of devicetree binding documentation
    - Use default values for pwmr and lscr1

 .../devicetree/bindings/video/fsl,imx-fb.txt       |  51 ++++++
 drivers/video/imxfb.c                              | 194 +++++++++++++++++----
 2 files changed, 210 insertions(+), 35 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/video/fsl,imx-fb.txt

diff --git a/Documentation/devicetree/bindings/video/fsl,imx-fb.txt b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt
new file mode 100644
index 0000000..aff16a4
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt
@@ -0,0 +1,51 @@
+Freescale imx21 Framebuffer
+
+This framebuffer driver supports devices imx1, imx21, imx25, and imx27.
+
+Required properties:
+- compatible : "fsl,<chip>-fb", chip should be imx1 or imx21
+- reg : Should contain 1 register ranges(address and length)
+- interrupts : One interrupt of the fb dev
+
+Required nodes:
+- display: Phandle to a display node as described in
+	Documentation/devicetree/bindings/video/display-timing.txt
+	Additional, the display node has to define properties:
+	- fsl,bpp: Bits per pixel
+	- fsl,pcr: LCDC PCR value
+
+Optional properties:
+- fsl,dmacr: DMA Control Register value. This is optional. By default, the
+	register is not modified as recommended by the datasheet.
+- fsl,lscr1: LCDC Sharp Configuration Register value.
+
+Example:
+
+	imxfb: fb@10021000 {
+		compatible = "fsl,imx27-fb", "fsl,imx21-fb";
+		interrupts = <61>;
+		reg = <0x10021000 0x1000>;
+		display = <&display0>;
+	};
+
+	...
+
+	display0: display0 {
+		model = "Primeview-PD050VL1";
+		native-mode = <&timing_disp0>;
+		fsl,bpp = <16>;		/* non-standard but required */
+		fsl,pcr = <0xf0c88080>;	/* non-standard but required */
+		display-timings {
+			timing_disp0: 640x480 {
+				hactive = <640>;
+				vactive = <480>;
+				hback-porch = <112>;
+				hfront-porch = <36>;
+				hsync-len = <32>;
+				vback-porch = <33>;
+				vfront-porch = <33>;
+				vsync-len = <2>;
+				clock-frequency = <25000000>;
+			};
+		};
+	};
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
index ef2b587..e0230f8 100644
--- a/drivers/video/imxfb.c
+++ b/drivers/video/imxfb.c
@@ -32,6 +32,12 @@
 #include <linux/io.h>
 #include <linux/math64.h>
 #include <linux/uaccess.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
 
 #include <linux/platform_data/video-imxfb.h>
 
@@ -117,10 +123,11 @@
 #define LGWCR_GWAV(alpha)	(((alpha) & 0xff) << 24)
 #define LGWCR_GWE	(1 << 22)
 
+#define IMXFB_LSCR1_DEFAULT 0x00120300
+
 /* Used fb-mode. Can be set on kernel command line, therefore file-static. */
 static const char *fb_mode;
 
-
 /*
  * These are the bitfields for each
  * display depth that we support.
@@ -192,6 +199,19 @@ static struct platform_device_id imxfb_devtype[] = {
 };
 MODULE_DEVICE_TABLE(platform, imxfb_devtype);
 
+static struct of_device_id imxfb_of_dev_id[] = {
+	{
+		.compatible = "fsl,imx1-fb",
+		.data = &imxfb_devtype[IMX1_FB],
+	}, {
+		.compatible = "fsl,imx21-fb",
+		.data = &imxfb_devtype[IMX21_FB],
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, imxfb_of_dev_id);
+
 static inline int is_imx1_fb(struct imxfb_info *fbi)
 {
 	return fbi->devtype = IMX1_FB;
@@ -324,6 +344,9 @@ static const struct imx_fb_videomode *imxfb_find_mode(struct imxfb_info *fbi)
 	struct imx_fb_videomode *m;
 	int i;
 
+	if (!fb_mode)
+		return &fbi->mode[0];
+
 	for (i = 0, m = &fbi->mode[0]; i < fbi->num_modes; i++, m++) {
 		if (!strcmp(m->mode.name, fb_mode))
 			return m;
@@ -479,6 +502,9 @@ static int imxfb_bl_update_status(struct backlight_device *bl)
 	struct imxfb_info *fbi = bl_get_data(bl);
 	int brightness = bl->props.brightness;
 
+	if (!fbi->pwmr)
+		return 0;
+
 	if (bl->props.power != FB_BLANK_UNBLANK)
 		brightness = 0;
 	if (bl->props.fb_blank != FB_BLANK_UNBLANK)
@@ -719,10 +745,14 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
 
 	writel(fbi->pcr, fbi->regs + LCDC_PCR);
 #ifndef PWMR_BACKLIGHT_AVAILABLE
-	writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+	if (fbi->pwmr)
+		writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
 #endif
 	writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
-	writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
+
+	/* dmacr = 0 is no valid value, as we need DMA control marks. */
+	if (fbi->dmacr)
+		writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
 
 	return 0;
 }
@@ -758,13 +788,12 @@ static int imxfb_resume(struct platform_device *dev)
 #define imxfb_resume	NULL
 #endif
 
-static int __init imxfb_init_fbinfo(struct platform_device *pdev)
+static int imxfb_init_fbinfo(struct platform_device *pdev)
 {
 	struct imx_fb_platform_data *pdata = pdev->dev.platform_data;
 	struct fb_info *info = dev_get_drvdata(&pdev->dev);
 	struct imxfb_info *fbi = info->par;
-	struct imx_fb_videomode *m;
-	int i;
+	struct device_node *np;
 
 	pr_debug("%s\n",__func__);
 
@@ -795,41 +824,95 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev)
 	info->fbops			= &imxfb_ops;
 	info->flags			= FBINFO_FLAG_DEFAULT |
 					  FBINFO_READS_FAST;
-	info->var.grayscale		= pdata->cmap_greyscale;
-	fbi->cmap_inverse		= pdata->cmap_inverse;
-	fbi->cmap_static		= pdata->cmap_static;
-	fbi->lscr1			= pdata->lscr1;
-	fbi->dmacr			= pdata->dmacr;
-	fbi->pwmr			= pdata->pwmr;
-	fbi->lcd_power			= pdata->lcd_power;
-	fbi->backlight_power		= pdata->backlight_power;
-
-	for (i = 0, m = &pdata->mode[0]; i < pdata->num_modes; i++, m++)
-		info->fix.smem_len = max_t(size_t, info->fix.smem_len,
-				m->mode.xres * m->mode.yres * m->bpp / 8);
+	if (pdata) {
+		info->var.grayscale		= pdata->cmap_greyscale;
+		fbi->cmap_inverse		= pdata->cmap_inverse;
+		fbi->cmap_static		= pdata->cmap_static;
+		fbi->lscr1			= pdata->lscr1;
+		fbi->dmacr			= pdata->dmacr;
+		fbi->pwmr			= pdata->pwmr;
+		fbi->lcd_power			= pdata->lcd_power;
+		fbi->backlight_power		= pdata->backlight_power;
+	} else {
+		np = pdev->dev.of_node;
+		info->var.grayscale = of_property_read_bool(np,
+						"cmap-greyscale");
+		fbi->cmap_inverse = of_property_read_bool(np, "cmap-inverse");
+		fbi->cmap_static = of_property_read_bool(np, "cmap-static");
+
+		fbi->lscr1 = IMXFB_LSCR1_DEFAULT;
+		of_property_read_u32(np, "fsl,lscr1", &fbi->lscr1);
+
+		of_property_read_u32(np, "fsl,dmacr", &fbi->dmacr);
+
+		/* These two function pointers could be used by some specific
+		 * platforms. */
+		fbi->lcd_power = NULL;
+		fbi->backlight_power = NULL;
+	}
+
+	return 0;
+}
+
+static int imxfb_of_read_mode(struct device *dev, struct device_node *np,
+		struct imx_fb_videomode *imxfb_mode)
+{
+	int ret;
+	struct fb_videomode *of_mode = &imxfb_mode->mode;
+	u32 bpp;
+	u32 pcr;
+
+	ret = of_property_read_string(np, "model", &of_mode->name);
+	if (ret)
+		of_mode->name = NULL;
+
+	ret = of_get_fb_videomode(np, of_mode, OF_USE_NATIVE_MODE);
+	if (ret) {
+		dev_err(dev, "Failed to get videomode from DT\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "fsl,bpp", &bpp);
+	ret |= of_property_read_u32(np, "fsl,pcr", &pcr);
+
+	if (ret) {
+		dev_err(dev, "Failed to read bpp and pcr from DT\n");
+		return -EINVAL;
+	}
+
+	if (bpp < 1 || bpp > 255) {
+		dev_err(dev, "Bits per pixel have to be between 1 and 255\n");
+		return -EINVAL;
+	}
+
+	imxfb_mode->bpp = bpp;
+	imxfb_mode->pcr = pcr;
 
 	return 0;
 }
 
-static int __init imxfb_probe(struct platform_device *pdev)
+static int imxfb_probe(struct platform_device *pdev)
 {
 	struct imxfb_info *fbi;
 	struct fb_info *info;
 	struct imx_fb_platform_data *pdata;
 	struct resource *res;
+	struct imx_fb_videomode *m;
+	const struct of_device_id *of_id;
 	int ret, i;
+	int bytes_per_pixel;
 
 	dev_info(&pdev->dev, "i.MX Framebuffer driver\n");
 
+	of_id = of_match_device(imxfb_of_dev_id, &pdev->dev);
+	if (of_id)
+		pdev->id_entry = of_id->data;
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res)
 		return -ENODEV;
 
 	pdata = pdev->dev.platform_data;
-	if (!pdata) {
-		dev_err(&pdev->dev,"No platform_data available\n");
-		return -ENOMEM;
-	}
 
 	info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev);
 	if (!info)
@@ -837,15 +920,55 @@ static int __init imxfb_probe(struct platform_device *pdev)
 
 	fbi = info->par;
 
-	if (!fb_mode)
-		fb_mode = pdata->mode[0].mode.name;
-
 	platform_set_drvdata(pdev, info);
 
 	ret = imxfb_init_fbinfo(pdev);
 	if (ret < 0)
 		goto failed_init;
 
+	if (pdata) {
+		if (!fb_mode)
+			fb_mode = pdata->mode[0].mode.name;
+
+		fbi->mode = pdata->mode;
+		fbi->num_modes = pdata->num_modes;
+	} else {
+		struct device_node *display_np;
+		fb_mode = NULL;
+
+		display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);
+		if (!display_np) {
+			dev_err(&pdev->dev, "No display defined in devicetree\n");
+			ret = -EINVAL;
+			goto failed_of_parse;
+		}
+
+		/*
+		 * imxfb does not support more modes, we choose only the native
+		 * mode.
+		 */
+		fbi->num_modes = 1;
+
+		fbi->mode = devm_kzalloc(&pdev->dev,
+				sizeof(struct imx_fb_videomode), GFP_KERNEL);
+		if (!fbi->mode) {
+			ret = -ENOMEM;
+			goto failed_of_parse;
+		}
+
+		ret = imxfb_of_read_mode(&pdev->dev, display_np, fbi->mode);
+		if (ret)
+			goto failed_of_parse;
+	}
+
+	/* Calculate maximum bytes used per pixel. In most cases this should
+	 * be the same as m->bpp/8 */
+	m = &fbi->mode[0];
+	bytes_per_pixel = (m->bpp + 7) / 8;
+	for (i = 0; i < fbi->num_modes; i++, m++)
+		info->fix.smem_len = max_t(size_t, info->fix.smem_len,
+				m->mode.xres * m->mode.yres * bytes_per_pixel);
+
 	res = request_mem_region(res->start, resource_size(res),
 				DRIVER_NAME);
 	if (!res) {
@@ -878,7 +1001,8 @@ static int __init imxfb_probe(struct platform_device *pdev)
 		goto failed_ioremap;
 	}
 
-	if (!pdata->fixed_screen_cpu) {
+	/* Seems not being used by anyone, so no support for oftree */
+	if (!pdata || !pdata->fixed_screen_cpu) {
 		fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
 		fbi->map_cpu = dma_alloc_writecombine(&pdev->dev,
 				fbi->map_size, &fbi->map_dma, GFP_KERNEL);
@@ -903,18 +1027,16 @@ static int __init imxfb_probe(struct platform_device *pdev)
 		info->fix.smem_start = fbi->screen_dma;
 	}
 
-	if (pdata->init) {
+	if (pdata && pdata->init) {
 		ret = pdata->init(fbi->pdev);
 		if (ret)
 			goto failed_platform_init;
 	}
 
-	fbi->mode = pdata->mode;
-	fbi->num_modes = pdata->num_modes;
 
 	INIT_LIST_HEAD(&info->modelist);
-	for (i = 0; i < pdata->num_modes; i++)
-		fb_add_videomode(&pdata->mode[i].mode, &info->modelist);
+	for (i = 0; i < fbi->num_modes; i++)
+		fb_add_videomode(&fbi->mode[i].mode, &info->modelist);
 
 	/*
 	 * This makes sure that our colour bitfield
@@ -944,10 +1066,10 @@ static int __init imxfb_probe(struct platform_device *pdev)
 failed_register:
 	fb_dealloc_cmap(&info->cmap);
 failed_cmap:
-	if (pdata->exit)
+	if (pdata && pdata->exit)
 		pdata->exit(fbi->pdev);
 failed_platform_init:
-	if (!pdata->fixed_screen_cpu)
+	if (pdata && !pdata->fixed_screen_cpu)
 		dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu,
 			fbi->map_dma);
 failed_map:
@@ -956,6 +1078,7 @@ failed_ioremap:
 failed_getclock:
 	release_mem_region(res->start, resource_size(res));
 failed_req:
+failed_of_parse:
 	kfree(info->pseudo_palette);
 failed_init:
 	platform_set_drvdata(pdev, NULL);
@@ -980,7 +1103,7 @@ static int imxfb_remove(struct platform_device *pdev)
 	unregister_framebuffer(info);
 
 	pdata = pdev->dev.platform_data;
-	if (pdata->exit)
+	if (pdata && pdata->exit)
 		pdata->exit(fbi->pdev);
 
 	fb_dealloc_cmap(&info->cmap);
@@ -1009,6 +1132,7 @@ static struct platform_driver imxfb_driver = {
 	.shutdown	= imxfb_shutdown,
 	.driver		= {
 		.name	= DRIVER_NAME,
+		.of_match_table = imxfb_of_dev_id,
 	},
 	.id_table	= imxfb_devtype,
 };
-- 
1.8.1.5


^ permalink raw reply related

* [PATCH v4 1/2] imxfb: Set alpha value of the framebuffer
From: Markus Pargmann @ 2013-04-18 13:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366290183-367-1-git-send-email-mpa@pengutronix.de>

From: Christian Hemp <c.hemp@phytec.de>

Based on Sascha Hauer's patch i.MX27 clock: Do not disable lcd clocks during
startup.
This patch gives a interface to chance the alphavalue of the framebuffer.

Signed-off-by: Christian Hemp <c.hemp@phytec.de>

rebased to 3.7
Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
---
 drivers/video/imxfb.c                     | 35 +++++++++++++++++++++++++++++++
 include/linux/platform_data/video-imxfb.h |  3 +++
 2 files changed, 38 insertions(+)

diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
index 0abf2bf..ef2b587 100644
--- a/drivers/video/imxfb.c
+++ b/drivers/video/imxfb.c
@@ -31,6 +31,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
 #include <linux/math64.h>
+#include <linux/uaccess.h>
 
 #include <linux/platform_data/video-imxfb.h>
 
@@ -112,6 +113,10 @@
 #define LCDISR_EOF	(1<<1)
 #define LCDISR_BOF	(1<<0)
 
+#define LCDC_LGWCR	0x64
+#define LGWCR_GWAV(alpha)	(((alpha) & 0xff) << 24)
+#define LGWCR_GWE	(1 << 22)
+
 /* Used fb-mode. Can be set on kernel command line, therefore file-static. */
 static const char *fb_mode;
 
@@ -610,6 +615,35 @@ static int imxfb_blank(int blank, struct fb_info *info)
 	return 0;
 }
 
+static int imxfb_ioctl(struct fb_info *info, unsigned int cmd,
+			unsigned long arg)
+{
+	struct imxfb_info *fbi = info->par;
+	int alpha, ret = 0;
+	unsigned int tmp;
+
+	switch (cmd) {
+	case IMXFB_ALPHA:
+		if (get_user(alpha, (int __user *)arg)) {
+			ret = -EFAULT;
+		} else {
+			tmp = readl(fbi->regs + LCDC_LGWCR);
+			tmp &= ~LGWCR_GWAV(0xff);
+			tmp |= LGWCR_GWAV(alpha);
+			if (!alpha)
+				tmp &= ~LGWCR_GWE;
+			else
+				tmp |= LGWCR_GWE;
+			writel(tmp , fbi->regs + LCDC_LGWCR);
+		}
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
 static struct fb_ops imxfb_ops = {
 	.owner		= THIS_MODULE,
 	.fb_check_var	= imxfb_check_var,
@@ -619,6 +653,7 @@ static struct fb_ops imxfb_ops = {
 	.fb_copyarea	= cfb_copyarea,
 	.fb_imageblit	= cfb_imageblit,
 	.fb_blank	= imxfb_blank,
+	.fb_ioctl	= imxfb_ioctl,
 };
 
 /*
diff --git a/include/linux/platform_data/video-imxfb.h b/include/linux/platform_data/video-imxfb.h
index 9de8f06..ce3875f 100644
--- a/include/linux/platform_data/video-imxfb.h
+++ b/include/linux/platform_data/video-imxfb.h
@@ -51,6 +51,9 @@
 #define DMACR_HM(x)	(((x) & 0xf) << 16)
 #define DMACR_TM(x)	((x) & 0xf)
 
+#define IMXFB_IOW(num, dtype)	_IOW('I', num, dtype)
+#define IMXFB_ALPHA		IMXFB_IOW(31, int)
+
 struct imx_fb_videomode {
 	struct fb_videomode mode;
 	u32 pcr;
-- 
1.8.1.5


^ permalink raw reply related

* [PATCH v4 0/2] video: imxfb DT support
From: Markus Pargmann @ 2013-04-18 13:03 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

This series adds DT support for imxfb. Changes are listed in the notes section
of each patch.

Regards,

Markus


Christian Hemp (1):
      imxfb: Set alpha value of the framebuffer

Markus Pargmann (1):
      video: imxfb: Add DT support

 Documentation/devicetree/bindings/video/fsl,imx-fb.txt |  51 ++++++++++++++++++
 drivers/video/imxfb.c                                  | 229 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
 include/linux/platform_data/video-imxfb.h              |   3 ++
 3 files changed, 248 insertions(+), 35 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/video/fsl,imx-fb.txt


^ permalink raw reply

* Re: [RFC 06/10] video: ARM CLCD: Add DT & CDF support
From: Russell King - ARM Linux @ 2013-04-18 10:24 UTC (permalink / raw)
  To: Pawel Moll
  Cc: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel, Laurent Pinchart, Linus Walleij
In-Reply-To: <1366211842-21497-7-git-send-email-pawel.moll@arm.com>

On Wed, Apr 17, 2013 at 04:17:18PM +0100, Pawel Moll wrote:
> +#if defined(CONFIG_OF)
> +static int clcdfb_of_get_tft_parallel_panel(struct clcd_panel *panel,
> +		struct display_entity_interface_params *params)
> +{
> +	int r = params->p.tft_parallel.r_bits;
> +	int g = params->p.tft_parallel.g_bits;
> +	int b = params->p.tft_parallel.b_bits;
> +
> +	/* Bypass pixel clock divider, data output on the falling edge */
> +	panel->tim2 = TIM2_BCD | TIM2_IPC;
> +
> +	/* TFT display, vert. comp. interrupt at the start of the back porch */
> +	panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1);
> +
> +	if (params->p.tft_parallel.r_b_swapped)
> +		panel->cntl |= CNTL_BGR;

NAK.  Do not set this explicitly.  Note the driver talks about this being
"the old way" - this should not be used with the panel capabilities - and
in fact it will be overwritten.

Instead, you need to encode this into the capabilities by masking the
below with CLCD_CAP_RGB or CLCD_CAP_BGR depending on the ordering.

^ permalink raw reply

* [PATCH] video: mxsfb: Fix colors display on lower color depth
From: Maxime Ripard @ 2013-04-18  9:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <516EBCA4.1020609@free-electrons.com>

The current code always registers as a 32 bits display, and uses the
hardware to drop the MSB of each color to abjust to the interface width
used by the panel.

This results on 18 bits (and probably 16 bits display as well) in colors
being displayed poorly, because the MSB are obviously the most important
bits for each color definition.

The default controller behaviour when using an interface width smaller
than the color depth is to drop the LSBs of each color, which makes more
sense because you lose the least important part of the color definition.

So, to fix the colors display, just get back to the default controller
behaviour.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/video/mxsfb.c |    5 -----
 1 file changed, 5 deletions(-)

diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c
index 755556c..2cfaf8b 100644
--- a/drivers/video/mxsfb.c
+++ b/drivers/video/mxsfb.c
@@ -424,11 +424,6 @@ static int mxsfb_set_par(struct fb_info *fb_info)
 			return -EINVAL;
 		case STMLCDIF_16BIT:
 		case STMLCDIF_18BIT:
-			/* 24 bit to 18 bit mapping */
-			ctrl |= CTRL_DF24; /* ignore the upper 2 bits in
-					    *  each colour component
-					    */
-			break;
 		case STMLCDIF_24BIT:
 			/* real 24 bit */
 			break;
-- 
1.7.10.4


^ permalink raw reply related

* Re: [PATCH 00/33] OMAPDSS: platform_enable/disable callback removal from panel drivers
From: Tomi Valkeinen @ 2013-04-18  3:40 UTC (permalink / raw)
  To: Tony Lindgren; +Cc: Archit Taneja, linux-omap, linux-fbdev
In-Reply-To: <20130418003419.GB10155@atomide.com>

[-- Attachment #1: Type: text/plain, Size: 641 bytes --]

On 2013-04-18 03:34, Tony Lindgren wrote:

> Just one request: Let's do branches like this early on before -rc6,
> not what might be five days before the merge window potentially
> opens..

Agreed, it got a bit late. But the branch itself has been stable and in
linux-next for some time.

Perhaps it would've been better to merge it to l-o earlier, instead of
me pushing it to linux-next via my tree. Any possible found problems
(there weren't any this time) could've been fixed with a new fixes branch.

> I've pulled them into omap-for-v3.10/dss and will send a pull
> request for Arnd and Olof.

Ok, thanks!

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]

^ permalink raw reply

* Re: [PATCH 00/33] OMAPDSS: platform_enable/disable callback removal from panel drivers
From: Tony Lindgren @ 2013-04-18  0:34 UTC (permalink / raw)
  To: Tomi Valkeinen; +Cc: Archit Taneja, linux-omap, linux-fbdev
In-Reply-To: <516CD17A.5050403@ti.com>

* Tomi Valkeinen <tomi.valkeinen@ti.com> [130415 21:25]:
> On 2013-04-16 00:20, Tony Lindgren wrote:
> 
> >> So, to recap, the common header changes are located in:
> >>
> >> git://gitorious.org/linux-omap-dss2/linux.git 3.10/0-dss-headers
> >>
> >> And the branch for linux-omap is:
> >>
> >> git://gitorious.org/linux-omap-dss2/linux.git 3.10-lo/board-cleanup
> >>
> >> After merging those, some displays won't start anymore until the omapdss
> >> changes are in, but things should still compile.
> > 
> > Sounds like it's best that you merge those branches via your
> > tree as the conflicts have been already resolved in linux next.
> 
> The dss changes are going through drm tree this time, as there are some
> drm dependencies also, and I've already sent the pull request for those.
> And when I asked Dave Airlie if he can merge the dss changes, I didn't
> talk about a big chunk of arch file changes getting included.

OK sorry I did not know that and was assuming you will be sending
a pull request to Linus.
 
> Also, the whole division to two independent branches was done only to
> make it possible for the arch changes to go through l-o tree.

Yup. That probably caused you to fix up few other things while doing
it though ;)
 
> There will probably be more these kind of changes in the future, so I
> think we should agree how to handle those and stick to the plan.
> Dividing the arch file changes to a separate branch is often quite
> laborious, and I'd rather not do that for nothing.

Thanks for doing all that. And yes, let's plan on always separating
driver changes from arch/arm changes to cut away the dependencies.

Just one request: Let's do branches like this early on before -rc6,
not what might be five days before the merge window potentially
opens..

I've pulled them into omap-for-v3.10/dss and will send a pull
request for Arnd and Olof.

Regards,

Tony

^ permalink raw reply

* [RFC 10/10] ARM: vexpress: Add CLCD Device Tree properties
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
  To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel
  Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
	Pawel Moll
In-Reply-To: <1366211842-21497-1-git-send-email-pawel.moll@arm.com>

Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
 arch/arm/boot/dts/vexpress-v2m-rs1.dtsi |   17 +++++++++++++----
 arch/arm/boot/dts/vexpress-v2m.dtsi     |   17 +++++++++++++----
 arch/arm/boot/dts/vexpress-v2p-ca9.dts  |    5 +++++
 3 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi
index ac870fb..aac9459 100644
--- a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi
+++ b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi
@@ -41,7 +41,7 @@
 			bank-width = <4>;
 		};
 
-		vram@2,00000000 {
+		v2m_vram: vram@2,00000000 {
 			compatible = "arm,vexpress-vram";
 			reg = <2 0x00000000 0x00800000>;
 		};
@@ -233,6 +233,12 @@
 				interrupts = <14>;
 				clocks = <&v2m_oscclk1>, <&smbclk>;
 				clock-names = "clcdclk", "apb_pclk";
+				label = "IOFPGA CLCD";
+				video-ram = <&v2m_vram>;
+				display = <&v2m_muxfpga 0>;
+				max-hactive = <640>;
+				max-vactive = <480>;
+				max-bpp = <16>;
 			};
 		};
 
@@ -282,7 +288,7 @@
 				/* CLCD clock */
 				compatible = "arm,vexpress-osc";
 				arm,vexpress-sysreg,func = <1 1>;
-				freq-range = <23750000 63500000>;
+				freq-range = <23750000 65000000>;
 				#clock-cells = <0>;
 				clock-output-names = "v2m:oscclk1";
 			};
@@ -317,9 +323,11 @@
 				arm,vexpress-sysreg,func = <5 0>;
 			};
 
-			muxfpga@0 {
+			v2m_muxfpga: muxfpga@0 {
 				compatible = "arm,vexpress-muxfpga";
 				arm,vexpress-sysreg,func = <7 0>;
+				#display-cells = <1>;
+				display = <&v2m_dvimode>;
 			};
 
 			shutdown@0 {
@@ -332,9 +340,10 @@
 				arm,vexpress-sysreg,func = <9 0>;
 			};
 
-			dvimode@0 {
+			v2m_dvimode: dvimode@0 {
 				compatible = "arm,vexpress-dvimode";
 				arm,vexpress-sysreg,func = <11 0>;
+				#display-cells = <0>;
 			};
 		};
 	};
diff --git a/arch/arm/boot/dts/vexpress-v2m.dtsi b/arch/arm/boot/dts/vexpress-v2m.dtsi
index f142036..4d080d0 100644
--- a/arch/arm/boot/dts/vexpress-v2m.dtsi
+++ b/arch/arm/boot/dts/vexpress-v2m.dtsi
@@ -40,7 +40,7 @@
 			bank-width = <4>;
 		};
 
-		vram@3,00000000 {
+		v2m_vram: vram@3,00000000 {
 			compatible = "arm,vexpress-vram";
 			reg = <3 0x00000000 0x00800000>;
 		};
@@ -232,6 +232,12 @@
 				interrupts = <14>;
 				clocks = <&v2m_oscclk1>, <&smbclk>;
 				clock-names = "clcdclk", "apb_pclk";
+				label = "IOFPGA CLCD";
+				video-ram = <&v2m_vram>;
+				display = <&v2m_muxfpga 0>;
+				max-hactive = <640>;
+				max-vactive = <480>;
+				max-bpp = <16>;
 			};
 		};
 
@@ -281,7 +287,7 @@
 				/* CLCD clock */
 				compatible = "arm,vexpress-osc";
 				arm,vexpress-sysreg,func = <1 1>;
-				freq-range = <23750000 63500000>;
+				freq-range = <23750000 65000000>;
 				#clock-cells = <0>;
 				clock-output-names = "v2m:oscclk1";
 			};
@@ -316,9 +322,11 @@
 				arm,vexpress-sysreg,func = <5 0>;
 			};
 
-			muxfpga@0 {
+			v2m_muxfpga: muxfpga@0 {
 				compatible = "arm,vexpress-muxfpga";
 				arm,vexpress-sysreg,func = <7 0>;
+				#display-cells = <1>;
+				display = <&v2m_dvimode>;
 			};
 
 			shutdown@0 {
@@ -331,9 +339,10 @@
 				arm,vexpress-sysreg,func = <9 0>;
 			};
 
-			dvimode@0 {
+			v2m_dvimode: dvimode@0 {
 				compatible = "arm,vexpress-dvimode";
 				arm,vexpress-sysreg,func = <11 0>;
+				#display-cells = <0>;
 			};
 		};
 	};
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca9.dts b/arch/arm/boot/dts/vexpress-v2p-ca9.dts
index 1420bb1..2a63510 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca9.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca9.dts
@@ -73,6 +73,11 @@
 		interrupts = <0 44 4>;
 		clocks = <&oscclk1>, <&oscclk2>;
 		clock-names = "clcdclk", "apb_pclk";
+		label = "V2P-CA9 CLCD";
+		display = <&v2m_muxfpga 0xf>;
+		max-hactive = <1024>;
+		max-vactive = <768>;
+		max-bpp = <16>;
 	};
 
 	memory-controller@100e0000 {
-- 
1.7.10.4



^ permalink raw reply related

* [RFC 09/10] video: Versatile Express DVI mode driver
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
  To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel
  Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
	Pawel Moll
In-Reply-To: <1366211842-21497-1-git-send-email-pawel.moll@arm.com>

Versatile Express DVI output is driven by a Sii9022 chip. It can be
controller to a limited extend by the Motherboard Config Controller,
and that's what the driver is doing now. It is a temporary measure
till there's a full I2C driver for the chip.

Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
 drivers/video/Makefile           |    1 +
 drivers/video/vexpress-dvimode.c |  158 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 159 insertions(+)
 create mode 100644 drivers/video/vexpress-dvimode.c

diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 84c6083..9347e00 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -179,3 +179,4 @@ obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o
 
 # platform specific output drivers
 obj-$(CONFIG_VEXPRESS_CONFIG)	  += vexpress-muxfpga.o
+obj-$(CONFIG_VEXPRESS_CONFIG)	  += vexpress-dvimode.o
diff --git a/drivers/video/vexpress-dvimode.c b/drivers/video/vexpress-dvimode.c
new file mode 100644
index 0000000..85d5608
--- /dev/null
+++ b/drivers/video/vexpress-dvimode.c
@@ -0,0 +1,158 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2013 ARM Limited
+ */
+
+#define pr_fmt(fmt) "vexpress-dvimode: " fmt
+
+#include <linux/fb.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/vexpress.h>
+#include <video/display.h>
+#include <video/videomode.h>
+
+
+static struct vexpress_config_func *vexpress_dvimode_func;
+
+
+static int vexpress_dvimode_display_update(struct display_entity *display,
+		const struct videomode *mode)
+{
+	static const struct {
+		u32 hactive, vactive, dvimode;
+	} dvimodes[] = {
+		{ 640, 480, 0 }, /* VGA */
+		{ 800, 600, 1 }, /* SVGA */
+		{ 1024, 768, 2 }, /* XGA */
+		{ 1280, 1024, 3 }, /* SXGA */
+		{ 1600, 1200, 4 }, /* UXGA */
+		{ 1920, 1080, 5 }, /* HD1080 */
+	};
+	int err = -ENOENT;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dvimodes); i++) {
+		if (dvimodes[i].hactive == mode->hactive &&
+				dvimodes[i].vactive == mode->vactive) {
+			pr_debug("mode: %ux%u = %d\n", mode->hactive,
+					mode->vactive, dvimodes[i].dvimode);
+			err = vexpress_config_write(vexpress_dvimode_func, 0,
+					dvimodes[i].dvimode);
+			break;
+		}
+	}
+
+	if (err)
+		pr_warn("Failed to set %ux%u mode! (%d)\n", mode->hactive,
+				mode->vactive, err);
+
+	return err;
+}
+
+static int vexpress_dvimode_display_get_modes(struct display_entity *display,
+		const struct videomode **modes)
+{
+	static const struct videomode m[] = {
+		{
+			/* VGA */
+			.pixelclock	= 25175000,
+			.hactive	= 640,
+			.hback_porch	= 40,
+			.hfront_porch	= 24,
+			.vfront_porch	= 11,
+			.hsync_len	= 96,
+			.vactive	= 480,
+			.vback_porch	= 32,
+			.vsync_len	= 2,
+		}, {
+			/* XGA */
+			.pixelclock	= 63500127,
+			.hactive	= 1024,
+			.hback_porch	= 152,
+			.hfront_porch	= 48,
+			.hsync_len	= 104,
+			.vactive	= 768,
+			.vback_porch	= 23,
+			.vfront_porch	= 3,
+			.vsync_len	= 4,
+		}, {
+			/* SXGA */
+			.pixelclock	= 108000000,
+			.hactive	= 1280,
+			.hback_porch	= 248,
+			.hfront_porch	= 48,
+			.hsync_len	= 112,
+			.vactive	= 1024,
+			.vback_porch	= 38,
+			.vfront_porch	= 1,
+			.vsync_len	= 3,
+		},
+	};
+
+	*modes = m;
+
+	return ARRAY_SIZE(m);
+}
+
+static int vexpress_dvimode_display_get_params(struct display_entity *display,
+		struct display_entity_interface_params *params)
+{
+	params->type = DISPLAY_ENTITY_INTERFACE_TFT_PARALLEL;
+	params->p.tft_parallel.r_bits = 8;
+	params->p.tft_parallel.g_bits = 8;
+	params->p.tft_parallel.b_bits = 8;
+	params->p.tft_parallel.r_b_swapped = 0;
+
+	return 0;
+}
+
+static const struct display_entity_control_ops vexpress_dvimode_display_ops = {
+	.update = vexpress_dvimode_display_update,
+	.get_modes = vexpress_dvimode_display_get_modes,
+	.get_params = vexpress_dvimode_display_get_params,
+};
+
+static struct display_entity vexpress_dvimode_display = {
+	.ops.ctrl = &vexpress_dvimode_display_ops,
+};
+
+static struct of_device_id vexpress_dvimode_of_match[] = {
+	{ .compatible = "arm,vexpress-dvimode", },
+	{}
+};
+
+static int vexpress_dvimode_probe(struct platform_device *pdev)
+{
+	vexpress_dvimode_func = vexpress_config_func_get_by_dev(&pdev->dev);
+
+	vexpress_dvimode_display.dev = &pdev->dev;
+	display_entity_register(&vexpress_dvimode_display);
+	of_display_entity_add_provider(pdev->dev.of_node,
+			of_display_entity_provider_simple_get,
+			&vexpress_dvimode_display);
+
+	return 0;
+}
+
+static struct platform_driver vexpress_dvimode_driver = {
+	.probe = vexpress_dvimode_probe,
+	.driver = {
+		.name = "vexpress-dvimode",
+		.of_match_table = vexpress_dvimode_of_match,
+	},
+};
+
+static int __init vexpress_dvimode_init(void)
+{
+	return platform_driver_register(&vexpress_dvimode_driver);
+}
+device_initcall(vexpress_dvimode_init);
-- 
1.7.10.4



^ permalink raw reply related

* [RFC 08/10] video: Versatile Express MUXFPGA driver
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
  To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel
  Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
	Pawel Moll
In-Reply-To: <1366211842-21497-1-git-send-email-pawel.moll@arm.com>

Versatile Express' DVI video output can be connected to one the three
sources - motherboard's CLCD controller or a video signal generated
by one of the daughterboards.

This driver provides a Common Display Framework driver for the
muxer FPGA, which acts as a switch selecting one of the video data
sources. The default source is selected basing on the priority
list (which itself can be modified via module paramter), but
the user can make his own decision about it using the device's
sysfs "source" attribute.

Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
 .../testing/sysfs-driver-video-vexpress-muxfpga    |    5 +
 drivers/video/Makefile                             |    3 +
 drivers/video/vexpress-muxfpga.c                   |  228 ++++++++++++++++++++
 3 files changed, 236 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-driver-video-vexpress-muxfpga
 create mode 100644 drivers/video/vexpress-muxfpga.c

diff --git a/Documentation/ABI/testing/sysfs-driver-video-vexpress-muxfpga b/Documentation/ABI/testing/sysfs-driver-video-vexpress-muxfpga
new file mode 100644
index 0000000..bfd568d
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-video-vexpress-muxfpga
@@ -0,0 +1,5 @@
+What:		/sys/bus/platform/drivers/vexpress-muxfpga/<muxfpga device>/source
+Date:		April 2013
+Contant:	Pawel Moll <pawel.moll@arm.com>
+Description:	This file stores the id of the video signal source
+		supposed to be routed to the board's DVI output.
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index b989e8e..84c6083 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -176,3 +176,6 @@ obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o
 obj-$(CONFIG_OF_DISPLAY_TIMING) += of_display_timing.o
 obj-$(CONFIG_VIDEOMODE) += videomode.o
 obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o
+
+# platform specific output drivers
+obj-$(CONFIG_VEXPRESS_CONFIG)	  += vexpress-muxfpga.o
diff --git a/drivers/video/vexpress-muxfpga.c b/drivers/video/vexpress-muxfpga.c
new file mode 100644
index 0000000..1731ad0
--- /dev/null
+++ b/drivers/video/vexpress-muxfpga.c
@@ -0,0 +1,228 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2013 ARM Limited
+ */
+
+#define pr_fmt(fmt) "vexpress-muxfpga: " fmt
+
+#include <linux/fb.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/vexpress.h>
+#include <video/display.h>
+#include <video/videomode.h>
+
+
+static struct vexpress_config_func *vexpress_muxfpga_func;
+static struct display_entity *vexpress_muxfpga_output;
+
+
+static struct vexpress_muxfpga_source {
+	struct display_entity display;
+	struct videomode mode;
+	bool updated;
+} vexpress_muxfpga_sources[__VEXPRESS_SITE_LAST];
+static u32 vexpress_muxfpga_source_site = VEXPRESS_SITE_MB;
+static bool vexpress_muxfpga_source_stored;
+
+
+static int vexpress_muxfpga_set_site(u32 site)
+{
+	int err;
+
+	if (site >= ARRAY_SIZE(vexpress_muxfpga_sources))
+		return -EINVAL;
+
+	err = vexpress_config_write(vexpress_muxfpga_func, 0, site);
+	if (!err) {
+		pr_debug("Selected site %d as source\n", site);
+		vexpress_muxfpga_source_site = site;
+	} else {
+		pr_warn("Failed to select site %d as source! (%d)\n",
+				site, err);
+	}
+
+	return err;
+}
+
+static unsigned int vexpress_muxfpga_preferred_sites[] = {
+	VEXPRESS_SITE_MASTER,
+	VEXPRESS_SITE_DB1,
+	VEXPRESS_SITE_DB2,
+	VEXPRESS_SITE_MB,
+};
+static unsigned int vexpress_muxfpga_preferred_sites_num =
+		ARRAY_SIZE(vexpress_muxfpga_preferred_sites);
+module_param_array_named(preferred_sites, vexpress_muxfpga_preferred_sites,
+		uint, &vexpress_muxfpga_preferred_sites_num, S_IRUGO);
+MODULE_PARM_DESC(preferred_sites, "Preferred order of MUXFPGA (DVI output) "
+		"sources; values can be a daughterboard site ID (1-2), the "
+		"motherboard ID (0) or a value describing the master site "
+		"(0xf).");
+
+static int vexpress_muxfpga_get_priority(u32 site)
+{
+	int i;
+
+	for (i = 0; i < vexpress_muxfpga_preferred_sites_num; i++) {
+		u32 preference = vexpress_muxfpga_preferred_sites[i];
+
+		if (site == vexpress_get_site(preference))
+			return i;
+	}
+
+	return INT_MAX;
+}
+
+static void vexpress_muxfpga_set_preffered_site(u32 site)
+{
+	int current_priority = vexpress_muxfpga_get_priority(
+			vexpress_muxfpga_source_site);
+	int new_priority = vexpress_muxfpga_get_priority(site);
+
+	if (new_priority <= current_priority)
+		vexpress_muxfpga_set_site(site);
+}
+
+
+static int vexpress_muxfpga_display_update(struct display_entity *display,
+		const struct videomode *mode)
+{
+	int err = display_entity_update(vexpress_muxfpga_output, mode);
+
+	if (!err) {
+		struct vexpress_muxfpga_source *source = container_of(display,
+				struct vexpress_muxfpga_source, display);
+
+		source->updated = true;
+		source->mode = *mode;
+	}
+
+	return err;
+}
+
+static int vexpress_muxfpga_display_get_modes(struct display_entity *display,
+		const struct videomode **modes)
+{
+	return display_entity_get_modes(vexpress_muxfpga_output, modes);
+}
+
+static int vexpress_muxfpga_display_get_params(struct display_entity *display,
+		struct display_entity_interface_params *params)
+{
+	return display_entity_get_params(vexpress_muxfpga_output, params);
+}
+
+static const struct display_entity_control_ops vexpress_muxfpga_display_ops = {
+	.update = vexpress_muxfpga_display_update,
+	.get_modes = vexpress_muxfpga_display_get_modes,
+	.get_params = vexpress_muxfpga_display_get_params,
+};
+
+
+static ssize_t vexpress_muxfpga_show_source(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+
+	return sprintf(buf, "%u\n", vexpress_muxfpga_source_site);
+}
+
+static ssize_t vexpress_muxfpga_store_source(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	u32 site;
+	int err = kstrtou32(buf, 0, &site);
+
+	if (!err) {
+		site = vexpress_get_site(site);
+		err = vexpress_muxfpga_set_site(site);
+	}
+
+	if (!err)
+		vexpress_muxfpga_source_stored = true;
+
+	if (!err && vexpress_muxfpga_sources[site].updated)
+		vexpress_muxfpga_display_update(
+				&vexpress_muxfpga_sources[site].display,
+				&vexpress_muxfpga_sources[site].mode);
+
+	return err ? err : count;
+}
+
+DEVICE_ATTR(source, S_IRUGO | S_IWUSR, vexpress_muxfpga_show_source,
+		vexpress_muxfpga_store_source);
+
+static struct display_entity *vexpress_muxfpga_display_get(
+		struct of_phandle_args *spec, void *data)
+{
+	u32 site = vexpress_get_site(spec->args[0]);
+
+	if (WARN_ON(spec->args_count != 1 ||
+			site >= ARRAY_SIZE(vexpress_muxfpga_sources)))
+		return NULL;
+
+	/* Skip source selection if the user made his choice */
+	if (!vexpress_muxfpga_source_stored)
+		vexpress_muxfpga_set_preffered_site(site);
+
+	return &vexpress_muxfpga_sources[site].display;
+}
+
+
+static struct of_device_id vexpress_muxfpga_of_match[] = {
+	{ .compatible = "arm,vexpress-muxfpga", },
+	{}
+};
+
+static int vexpress_muxfpga_probe(struct platform_device *pdev)
+{
+	struct display_entity_interface_params params;
+	int i;
+
+	vexpress_muxfpga_output = of_display_entity_get(pdev->dev.of_node, 0);
+	if (!vexpress_muxfpga_output)
+		return -EPROBE_DEFER;
+
+	if (display_entity_get_params(vexpress_muxfpga_output, &params) != 0 ||
+			params.type != DISPLAY_ENTITY_INTERFACE_TFT_PARALLEL)
+		return -EINVAL;
+
+	vexpress_muxfpga_func = vexpress_config_func_get_by_dev(&pdev->dev);
+
+	for (i = 0; i < ARRAY_SIZE(vexpress_muxfpga_sources); i++) {
+		struct vexpress_muxfpga_source *source =
+			&vexpress_muxfpga_sources[i];
+
+		source->display.dev = &pdev->dev;
+		source->display.ops.ctrl = &vexpress_muxfpga_display_ops;
+		WARN_ON(display_entity_register(&source->display));
+		of_display_entity_add_provider(pdev->dev.of_node,
+				vexpress_muxfpga_display_get, NULL);
+	}
+
+	device_create_file(&pdev->dev, &dev_attr_source);
+
+	return 0;
+}
+
+static struct platform_driver vexpress_muxfpga_driver = {
+	.probe = vexpress_muxfpga_probe,
+	.driver = {
+		.name = "vexpress-muxfpga",
+		.of_match_table = vexpress_muxfpga_of_match,
+	},
+};
+
+static int __init vexpress_muxfpga_init(void)
+{
+	return platform_driver_register(&vexpress_muxfpga_driver);
+}
+device_initcall(vexpress_muxfpga_init);
-- 
1.7.10.4



^ permalink raw reply related

* [RFC 07/10] mfd: vexpress: Allow external drivers to parse site ids
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
  To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel
  Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
	Pawel Moll
In-Reply-To: <1366211842-21497-1-git-send-email-pawel.moll@arm.com>

... by providing a function translating the MASTER
value into the currently valid site number and
a _LAST constant providing all possible site id values.

Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
 drivers/mfd/vexpress-sysreg.c |    5 +++++
 include/linux/vexpress.h      |    2 ++
 2 files changed, 7 insertions(+)

diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c
index bf75e96..4158e26 100644
--- a/drivers/mfd/vexpress-sysreg.c
+++ b/drivers/mfd/vexpress-sysreg.c
@@ -81,6 +81,11 @@ void vexpress_flags_set(u32 data)
 	writel(data, vexpress_sysreg_base + SYS_FLAGSSET);
 }
 
+u32 vexpress_get_site(int site)
+{
+	return site == VEXPRESS_SITE_MASTER ? vexpress_master_site : site;
+}
+
 u32 vexpress_get_procid(int site)
 {
 	if (site == VEXPRESS_SITE_MASTER)
diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h
index 7581874..1ebbcf5 100644
--- a/include/linux/vexpress.h
+++ b/include/linux/vexpress.h
@@ -19,6 +19,7 @@
 #define VEXPRESS_SITE_MB		0
 #define VEXPRESS_SITE_DB1		1
 #define VEXPRESS_SITE_DB2		2
+#define __VEXPRESS_SITE_LAST		3
 #define VEXPRESS_SITE_MASTER		0xf
 
 #define VEXPRESS_CONFIG_STATUS_DONE	0
@@ -103,6 +104,7 @@ int vexpress_config_write(struct vexpress_config_func *func, int offset,
 
 /* Platform control */
 
+u32 vexpress_get_site(int site);
 u32 vexpress_get_procid(int site);
 u32 vexpress_get_hbi(int site);
 void *vexpress_get_24mhz_clock_base(void);
-- 
1.7.10.4



^ permalink raw reply related

* [RFC 06/10] video: ARM CLCD: Add DT & CDF support
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
  To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel
  Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
	Pawel Moll
In-Reply-To: <1366211842-21497-1-git-send-email-pawel.moll@arm.com>

This patch adds basic DT bindings for the PL11x CLCD cells
and make their fbdev driver use them, together with the
Common Display Framework.

The DT provides information about the hardware configuration
and limitations (eg. the largest supported resolution)
but the video modes come exclusively from the Common
Display Framework drivers, referenced to by the standard CDF
binding.

Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
 .../devicetree/bindings/video/arm,pl11x.txt        |   35 +++
 drivers/video/Kconfig                              |    1 +
 drivers/video/amba-clcd.c                          |  244 ++++++++++++++++++++
 include/linux/amba/clcd.h                          |    2 +
 4 files changed, 282 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/arm,pl11x.txt

diff --git a/Documentation/devicetree/bindings/video/arm,pl11x.txt b/Documentation/devicetree/bindings/video/arm,pl11x.txt
new file mode 100644
index 0000000..ee9534a
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/arm,pl11x.txt
@@ -0,0 +1,35 @@
+* ARM PrimeCell Color LCD Controller (CLCD) PL110/PL111
+
+Required properties:
+
+- compatible : must be one of:
+			"arm,pl110", "arm,primecell"
+			"arm,pl111", "arm,primecell"
+- reg : base address and size of the control registers block
+- interrupts : the combined interrupt
+- clocks : phandles to the CLCD (pixel) clock and the APB clocks
+- clock-names : "clcdclk", "apb_pclk"
+- display : phandle to the display entity connected to the controller
+
+Optional properties:
+
+- label : string describing the controller location and/or usage
+- video-ram : phandle to DT node of the specialized video RAM to be used
+- max-hactive : maximum frame buffer width in pixels
+- max-vactive : maximum frame buffer height in pixels
+- max-bpp : maximum number of bits per pixel
+- big-endian-pixels : defining this property makes the pixel bytes being
+			accessed in Big Endian organization
+
+Example:
+
+			clcd@1f0000 {
+				compatible = "arm,pl111", "arm,primecell";
+				reg = <0x1f0000 0x1000>;
+				interrupts = <14>;
+				clocks = <&v2m_oscclk1>, <&smbclk>;
+				clock-names = "clcdclk", "apb_pclk";
+				label = "IOFPGA CLCD";
+				video-ram = <&v2m_vram>;
+				display = <&v2m_muxfpga>;
+			};
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 281e548..bad8166 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -340,6 +340,7 @@ config FB_ARMCLCD
 	select FB_CFB_FILLRECT
 	select FB_CFB_COPYAREA
 	select FB_CFB_IMAGEBLIT
+	select FB_MODE_HELPERS if OF
 	help
 	  This framebuffer device driver is for the ARM PrimeCell PL110
 	  Colour LCD controller.  ARM PrimeCells provide the building
diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c
index 0a2cce7..778dc03 100644
--- a/drivers/video/amba-clcd.c
+++ b/drivers/video/amba-clcd.c
@@ -25,6 +25,11 @@
 #include <linux/amba/clcd.h>
 #include <linux/clk.h>
 #include <linux/hardirq.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <video/display.h>
+#include <video/videomode.h>
 
 #include <asm/sizes.h>
 
@@ -62,6 +67,10 @@ static void clcdfb_disable(struct clcd_fb *fb)
 {
 	u32 val;
 
+	if (fb->panel->display)
+		display_entity_set_state(fb->panel->display,
+				DISPLAY_ENTITY_STATE_OFF);
+
 	if (fb->board->disable)
 		fb->board->disable(fb);
 
@@ -115,6 +124,11 @@ static void clcdfb_enable(struct clcd_fb *fb, u32 cntl)
 	 */
 	if (fb->board->enable)
 		fb->board->enable(fb);
+
+	if (fb->panel->display)
+		display_entity_set_state(fb->panel->display,
+				DISPLAY_ENTITY_STATE_ON);
+
 }
 
 static int
@@ -304,6 +318,13 @@ static int clcdfb_set_par(struct fb_info *info)
 
 	clcdfb_enable(fb, regs.cntl);
 
+	if (fb->panel->display) {
+		struct videomode mode;
+
+		videomode_from_fb_var_screeninfo(&fb->fb.var, &mode);
+		display_entity_update(fb->panel->display, &mode);
+	}
+
 #ifdef DEBUG
 	printk(KERN_INFO
 	       "CLCD: Registers set to\n"
@@ -542,6 +563,226 @@ static int clcdfb_register(struct clcd_fb *fb)
 	return ret;
 }
 
+#if defined(CONFIG_OF)
+static int clcdfb_of_get_tft_parallel_panel(struct clcd_panel *panel,
+		struct display_entity_interface_params *params)
+{
+	int r = params->p.tft_parallel.r_bits;
+	int g = params->p.tft_parallel.g_bits;
+	int b = params->p.tft_parallel.b_bits;
+
+	/* Bypass pixel clock divider, data output on the falling edge */
+	panel->tim2 = TIM2_BCD | TIM2_IPC;
+
+	/* TFT display, vert. comp. interrupt at the start of the back porch */
+	panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1);
+
+	if (params->p.tft_parallel.r_b_swapped)
+		panel->cntl |= CNTL_BGR;
+
+	if (r >= 4 && g >= 4 && b >= 4)
+		panel->caps |= CLCD_CAP_444;
+	if (r >= 5 && g >= 5 && b >= 5)
+		panel->caps |= CLCD_CAP_5551;
+	if (r >= 5 && g >= 6 && b >= 5)
+		panel->caps |= CLCD_CAP_565;
+	if (r >= 8 && g >= 8 && b >= 8)
+		panel->caps |= CLCD_CAP_888;
+
+	return 0;
+}
+
+static int clcdfb_of_init_display(struct clcd_fb *fb)
+{
+	struct device_node *node = fb->dev->dev.of_node;
+	struct display_entity_interface_params params;
+	const struct videomode *modes;
+	int modes_num;
+	int best_mode = -1;
+	u32 max_bpp = 32;
+	u32 max_hactive = (u32)~0UL;
+	u32 max_vactive = (u32)~0UL;
+	unsigned int width, height;
+	char *mode_name;
+	int i, err;
+
+	fb->panel = devm_kzalloc(&fb->dev->dev, sizeof(*fb->panel), GFP_KERNEL);
+	if (!fb->panel)
+		return -ENOMEM;
+
+	fb->panel->display = of_display_entity_get(node, 0);
+	if (!fb->panel->display)
+		return -EPROBE_DEFER;
+
+	modes_num = display_entity_get_modes(fb->panel->display, &modes);
+	if (modes_num < 0)
+		return modes_num;
+
+	/* Pick the "best" (the widest, then the highest) mode from the list */
+	of_property_read_u32(node, "max-hactive", &max_hactive);
+	of_property_read_u32(node, "max-vactive", &max_vactive);
+	for (i = 0; i < modes_num; i++) {
+		if (modes[i].hactive > max_hactive ||
+				modes[i].vactive > max_vactive)
+			continue;
+		if (best_mode < 0 ||
+				(modes[i].hactive >= modes[best_mode].hactive &&
+				modes[i].vactive > modes[best_mode].vactive))
+			best_mode = i;
+	}
+	if (best_mode < 0)
+		return -ENODEV;
+
+	err = fb_videomode_from_videomode(&modes[best_mode], &fb->panel->mode);
+	if (err)
+		return err;
+
+	i = snprintf(NULL, 0, "%ux%u@%u", fb->panel->mode.xres,
+			fb->panel->mode.yres, fb->panel->mode.refresh);
+	mode_name = devm_kzalloc(&fb->dev->dev, i + 1, GFP_KERNEL);
+	snprintf(mode_name, i + 1, "%ux%u@%u", fb->panel->mode.xres,
+			fb->panel->mode.yres, fb->panel->mode.refresh);
+	fb->panel->mode.name = mode_name;
+
+	of_property_read_u32(node, "max-bpp", &max_bpp);
+	fb->panel->bpp = max_bpp;
+
+	if (of_property_read_bool(node, "big-endian-pixels"))
+		fb->panel->cntl |= CNTL_BEBO;
+
+	if (display_entity_get_size(fb->panel->display, &width, &height) != 0)
+		width = height = -1;
+	fb->panel->width = width;
+	fb->panel->height = height;
+
+	err = display_entity_get_params(fb->panel->display, &params);
+	if (err)
+		return err;
+
+	switch (params.type) {
+	case DISPLAY_ENTITY_INTERFACE_TFT_PARALLEL:
+		return clcdfb_of_get_tft_parallel_panel(fb->panel, &params);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int clcdfb_of_vram_setup(struct clcd_fb *fb)
+{
+	const __be32 *prop = of_get_property(fb->dev->dev.of_node, "video-ram",
+			NULL);
+	struct device_node *node = of_find_node_by_phandle(be32_to_cpup(prop));
+	u64 size;
+	int err;
+
+	if (!node)
+		return -ENODEV;
+
+	err = clcdfb_of_init_display(fb);
+	if (err)
+		return err;
+
+	fb->fb.screen_base = of_iomap(node, 0);
+	if (!fb->fb.screen_base)
+		return -ENOMEM;
+
+	fb->fb.fix.smem_start = of_translate_address(node,
+			of_get_address(node, 0, &size, NULL));
+	fb->fb.fix.smem_len = size;
+
+	return 0;
+}
+
+static int clcdfb_of_vram_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+	unsigned long off, user_size, kernel_size;
+
+	off = vma->vm_pgoff << PAGE_SHIFT;
+	user_size = vma->vm_end - vma->vm_start;
+	kernel_size = fb->fb.fix.smem_len;
+
+	if (off >= kernel_size || user_size > (kernel_size - off))
+		return -ENXIO;
+
+	return remap_pfn_range(vma, vma->vm_start,
+			__phys_to_pfn(fb->fb.fix.smem_start) + vma->vm_pgoff,
+			user_size,
+			pgprot_writecombine(vma->vm_page_prot));
+}
+
+static void clcdfb_of_vram_remove(struct clcd_fb *fb)
+{
+	iounmap(fb->fb.screen_base);
+}
+
+static int clcdfb_of_dma_setup(struct clcd_fb *fb)
+{
+	unsigned long framesize;
+	dma_addr_t dma;
+	int err;
+
+	err = clcdfb_of_init_display(fb);
+	if (err)
+		return err;
+
+	framesize = fb->panel->mode.xres * fb->panel->mode.yres *
+			fb->panel->bpp / 8;
+	fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev, framesize,
+			&dma, GFP_KERNEL);
+	if (!fb->fb.screen_base)
+		return -ENOMEM;
+
+	fb->fb.fix.smem_start = dma;
+	fb->fb.fix.smem_len = framesize;
+
+	return 0;
+}
+
+static int clcdfb_of_dma_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+	return dma_mmap_writecombine(&fb->dev->dev, vma, fb->fb.screen_base,
+			fb->fb.fix.smem_start, fb->fb.fix.smem_len);
+}
+
+static void clcdfb_of_dma_remove(struct clcd_fb *fb)
+{
+	dma_free_writecombine(&fb->dev->dev, fb->fb.fix.smem_len,
+			fb->fb.screen_base, fb->fb.fix.smem_start);
+}
+
+static struct clcd_board *clcdfb_of_get_board(struct amba_device *dev)
+{
+	struct clcd_board *board = devm_kzalloc(&dev->dev, sizeof(*board),
+			GFP_KERNEL);
+	struct device_node *node = dev->dev.of_node;
+
+	if (!board)
+		return NULL;
+
+	board->name = of_get_property(node, "label", NULL);
+	if (!board->name)
+		board->name = of_node_full_name(node);
+	board->check = clcdfb_check;
+	board->decode = clcdfb_decode;
+	if (of_find_property(node, "video-ram", NULL)) {
+		board->setup = clcdfb_of_vram_setup;
+		board->mmap = clcdfb_of_vram_mmap;
+		board->remove = clcdfb_of_vram_remove;
+	} else {
+		board->setup = clcdfb_of_dma_setup;
+		board->mmap = clcdfb_of_dma_mmap;
+		board->remove = clcdfb_of_dma_remove;
+	}
+
+	return board;
+}
+#else
+static struct clcd_board *clcdfb_of_get_board(struct amba_dev *dev)
+{
+	return NULL;
+}
+#endif
+
 static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
 {
 	struct clcd_board *board = dev->dev.platform_data;
@@ -549,6 +790,9 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
 	int ret;
 
 	if (!board)
+		board = clcdfb_of_get_board(dev);
+
+	if (!board)
 		return -EINVAL;
 
 	ret = amba_request_regions(dev, NULL);
diff --git a/include/linux/amba/clcd.h b/include/linux/amba/clcd.h
index e82e3ee..73b199b 100644
--- a/include/linux/amba/clcd.h
+++ b/include/linux/amba/clcd.h
@@ -10,6 +10,7 @@
  * for more details.
  */
 #include <linux/fb.h>
+#include <video/display.h>
 
 /*
  * CLCD Controller Internal Register addresses
@@ -105,6 +106,7 @@ struct clcd_panel {
 				fixedtimings:1,
 				grayscale:1;
 	unsigned int		connector;
+	struct display_entity	*display;
 };
 
 struct clcd_regs {
-- 
1.7.10.4



^ permalink raw reply related

* [RFC 05/10] fbmon: Add extra video helper
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
  To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel
  Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
	Pawel Moll
In-Reply-To: <1366211842-21497-1-git-send-email-pawel.moll@arm.com>

This function converts the fb_var_screeninfo to the videomode
structure, to be used in fbdev drivers working with the
Common Display Framework.

Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
 drivers/video/fbmon.c |   29 +++++++++++++++++++++++++++++
 include/linux/fb.h    |    3 +++
 2 files changed, 32 insertions(+)

diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
index 7f67099..f0ff2bf 100644
--- a/drivers/video/fbmon.c
+++ b/drivers/video/fbmon.c
@@ -1424,6 +1424,35 @@ int fb_videomode_from_videomode(const struct videomode *vm,
 	return 0;
 }
 EXPORT_SYMBOL_GPL(fb_videomode_from_videomode);
+
+void videomode_from_fb_var_screeninfo(const struct fb_var_screeninfo *var,
+				      struct videomode *vm)
+{
+	vm->pixelclock = PICOS2KHZ(var->pixclock) * 1000;
+
+	vm->hactive = var->xres;
+	vm->hfront_porch = var->right_margin;
+	vm->hback_porch = var->left_margin;
+	vm->hsync_len = var->hsync_len;
+
+	vm->vactive = var->yres;
+	vm->vfront_porch = var->lower_margin;
+	vm->vback_porch = var->upper_margin;
+	vm->vsync_len = var->vsync_len;
+
+	vm->dmt_flags = 0;
+	if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+		vm->dmt_flags |= VESA_DMT_HSYNC_HIGH;
+	if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+		vm->dmt_flags |= VESA_DMT_VSYNC_HIGH;
+
+	vm->data_flags = 0;
+	if (var->vmode & FB_VMODE_INTERLACED)
+		vm->data_flags |= DISPLAY_FLAGS_INTERLACED;
+	if (var->vmode & FB_VMODE_DOUBLE)
+		vm->data_flags |= DISPLAY_FLAGS_DOUBLESCAN;
+}
+EXPORT_SYMBOL_GPL(videomode_from_fb_var_screeninfo);
 #endif
 
 #if IS_ENABLED(CONFIG_OF_VIDEOMODE)
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 58b9860..aae2ed3 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -721,6 +721,9 @@ extern int of_get_fb_videomode(struct device_node *np,
 			       int index);
 extern int fb_videomode_from_videomode(const struct videomode *vm,
 				       struct fb_videomode *fbmode);
+extern void videomode_from_fb_var_screeninfo(
+				const struct fb_var_screeninfo *var,
+				struct videomode *vm);
 
 /* drivers/video/modedb.c */
 #define VESA_MODEDB_SIZE 34
-- 
1.7.10.4



^ permalink raw reply related

* [RFC 04/10] video: display: Add generic TFT display type
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
  To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel
  Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
	Pawel Moll
In-Reply-To: <1366211842-21497-1-git-send-email-pawel.moll@arm.com>

TFT panels may be interfaced via a simple parallel interface
carrying RGB data, pixel clock and synchronisation signals.
From the video generator point of view the width of the data
channels (number of bits per R/G/B components) may be an
important factor in setting up the display model.

Above information is based on the presentations by Dave Anders
available here: http://elinux.org/Elc-lcd

This patch adds the parallel TFT display type and basic parameters
structure. Maybe it should be split into a separate header, eg.
include/video/tft.h? Or maybe it's just the INTERFACE_DPI I'm
talking about?

Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
 include/video/display.h |    9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/include/video/display.h b/include/video/display.h
index 7fe8b2f..875e230 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -69,10 +69,19 @@ enum display_entity_stream_state {
 
 enum display_entity_interface_type {
 	DISPLAY_ENTITY_INTERFACE_DPI,
+	DISPLAY_ENTITY_INTERFACE_TFT_PARALLEL,
+};
+
+struct tft_parallel_interface_params {
+	int r_bits, g_bits, b_bits;
+	int r_b_swapped;
 };
 
 struct display_entity_interface_params {
 	enum display_entity_interface_type type;
+	union {
+		struct tft_parallel_interface_params tft_parallel;
+	} p;
 };
 
 struct display_entity_control_ops {
-- 
1.7.10.4



^ permalink raw reply related

* [RFC 03/10] video: display: Add Device Tree bindings
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
  To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel
  Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
	Pawel Moll
In-Reply-To: <1366211842-21497-1-git-send-email-pawel.moll@arm.com>

Modelled after the common clock solution, the bindings
are based on the idea of display entity "providers" and
"consumers".

Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
 .../devicetree/bindings/video/display-bindings.txt |   75 +++++++++++++++++
 drivers/video/display/display-core.c               |   84 ++++++++++++++++++++
 include/video/display.h                            |   11 +++
 3 files changed, 170 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/display-bindings.txt

diff --git a/Documentation/devicetree/bindings/video/display-bindings.txt b/Documentation/devicetree/bindings/video/display-bindings.txt
new file mode 100644
index 0000000..6d8b888
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/display-bindings.txt
@@ -0,0 +1,75 @@
+[this is an RFC]
+
+Common Display Framework define a display entity (eg. LCD panel),
+being a sink for video data generated by a video signal generator
+(eg.  LCD controller/driver).
+
+This set of bindings allow to represent connections between them
+in the Device Tree.
+
+Devices nodes representing display sinks are called "display
+providers" and nodes representing display sources are called
+"display consumers".
+
+Notice that in both cases a device represented by a node can
+provide or consume more than one display entity. For example
+a LCD controller can be able to driver more than one LCD
+panel at the same time, while a panel (or a special signal
+multiplexer) may have more than one input (sink) and switch
+between them.
+
+== Display provider ==
+
+Required properties:
+
+* #clock-cells:	Number of cells in the display specifier. Typically
+		0 for nodes providing single display entity and 1
+		for nodes providing multiple displays.
+
+Example:
+			dvi-output: dvi-output@0 {
+				#display-cells = <0>;
+			};
+
+== Display consumer ==
+
+Required properties:
+
+* display:	List of phandle and clock specifier pairs, one pair
+		for each display (sink). Note: if the display provider
+		specifies '0' for #display-cells, then only the phandle
+		portion of the pair will appear.
+
+Example:
+
+			display-driver {
+				display = <&dvi-output>;
+			};
+
+== Larger example ==
+
+			clcd@10020000 {
+				compatible = "arm,pl111", "arm,primecell";
+				reg = <0x10020000 0x1000>;
+				interrupts = <0 44 4>;
+				clocks = <&oscclk1>, <&oscclk2>;
+				clock-names = "clcdclk", "apb_pclk";
+				label = "V2P-CA9 CLCD";
+				display = <&v2m_muxfpga 0xf>;
+				max-hactive = <1024>;
+				max-vactive = <768>;
+				max-bpp = <16>;
+			};
+
+			v2m_muxfpga: muxfpga@0 {
+				compatible = "arm,vexpress-muxfpga";
+				arm,vexpress-sysreg,func = <7 0>;
+				#display-cells = <1>;
+				display = <&v2m_dvimode>;
+			};
+
+			v2m_dvimode: dvimode@0 {
+				compatible = "arm,vexpress-dvimode";
+				arm,vexpress-sysreg,func = <11 0>;
+				#display-cells = <0>;
+			};
diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
index 4b8e45a..9827a5d 100644
--- a/drivers/video/display/display-core.c
+++ b/drivers/video/display/display-core.c
@@ -15,6 +15,7 @@
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/slab.h>
 
 #include <video/display.h>
 #include <video/videomode.h>
@@ -230,6 +231,89 @@ void display_entity_put(struct display_entity *entity)
 }
 EXPORT_SYMBOL_GPL(display_entity_put);
 
+#if defined(CONFIG_OF)
+struct of_display_entity_provider {
+	struct list_head list;
+	struct device_node *node;
+	struct display_entity *(*get)(struct of_phandle_args *spec, void *data);
+	void *data;
+};
+
+static LIST_HEAD(of_display_entity_providers);
+static DEFINE_MUTEX(of_display_entity_providers_lock);
+
+int of_display_entity_add_provider(struct device_node *node,
+		struct display_entity *(*get)(struct of_phandle_args *spec,
+		void *data), void *data)
+{
+	struct of_display_entity_provider *provider =
+			kzalloc(sizeof(*provider), GFP_KERNEL);
+
+	if (!provider)
+		return -ENOMEM;
+
+	provider->node = of_node_get(node);
+	provider->get = get;
+	provider->data = data;
+
+	mutex_lock(&of_display_entity_providers_lock);
+	list_add(&provider->list, &of_display_entity_providers);
+	mutex_unlock(&of_display_entity_providers_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_display_entity_add_provider);
+
+struct display_entity *of_display_entity_provider_simple_get(
+		struct of_phandle_args *spec, void *data)
+{
+	return data;
+}
+EXPORT_SYMBOL_GPL(of_display_entity_provider_simple_get);
+
+void of_display_entity_remove_provider(struct device_node *node)
+{
+	struct of_display_entity_provider *provider, *p;
+
+	mutex_lock(&of_display_entity_providers_lock);
+	list_for_each_entry_safe(provider, p, &of_display_entity_providers,
+			list) {
+		if (provider->node == node) {
+			list_del(&provider->list);
+			of_node_put(provider->node);
+			kfree(node);
+			break;
+		}
+	}
+	mutex_unlock(&of_display_entity_providers_lock);
+}
+EXPORT_SYMBOL_GPL(of_display_entity_remove_provider);
+
+struct display_entity *of_display_entity_get(struct device_node *node,
+		int index)
+{
+	struct of_phandle_args spec;
+	struct of_display_entity_provider *provider;
+	struct display_entity *entity = NULL;
+
+	if (of_parse_phandle_with_args(node, "display", "#display-cells",
+			index, &spec) != 0)
+		return NULL;
+
+	mutex_lock(&of_display_entity_providers_lock);
+	list_for_each_entry(provider, &of_display_entity_providers, list) {
+		if (provider->node == spec.np) {
+			entity = provider->get(&spec, provider->data);
+			break;
+		}
+	}
+	mutex_unlock(&of_display_entity_providers_lock);
+
+	return entity;
+}
+EXPORT_SYMBOL_GPL(of_display_entity_get);
+#endif
+
 static int display_entity_notifier_match(struct display_entity *entity,
 				struct display_entity_notifier *notifier)
 {
diff --git a/include/video/display.h b/include/video/display.h
index 64f84d5..7fe8b2f 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -16,6 +16,7 @@
 #include <linux/kref.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <linux/of.h>
 
 /* -----------------------------------------------------------------------------
  * Display Entity
@@ -149,4 +150,14 @@ void display_entity_unregister_notifier(struct display_entity_notifier *notifier
 #define display_entity_register(display_entity) \
 	__display_entity_register(display_entity, THIS_MODULE)
 
+struct display_entity *of_display_entity_get(struct device_node *node,
+		int index);
+
+int of_display_entity_add_provider(struct device_node *node,
+		struct display_entity *(*get)(struct of_phandle_args *spec,
+		void *data), void *data);
+void of_display_entity_remove_provider(struct device_node *node);
+struct display_entity *of_display_entity_provider_simple_get(
+		struct of_phandle_args *spec, void *data);
+
 #endif /* __DISPLAY_H__ */
-- 
1.7.10.4



^ permalink raw reply related

* [RFC 02/10] video: display: Update the display with the video mode data
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
  To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel
  Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
	Pawel Moll
In-Reply-To: <1366211842-21497-1-git-send-email-pawel.moll@arm.com>

The display entity (sink) may need to know about the mode being
changed, eg. to update timings.

Alternatively there could be a separate set_mode() operation...

Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
 drivers/video/display/display-core.c |    5 +++--
 include/video/display.h              |    6 ++++--
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
index d2daa15..4b8e45a 100644
--- a/drivers/video/display/display-core.c
+++ b/drivers/video/display/display-core.c
@@ -69,12 +69,13 @@ EXPORT_SYMBOL_GPL(display_entity_set_state);
  *
  * Return 0 on success or a negative error code otherwise.
  */
-int display_entity_update(struct display_entity *entity)
+int display_entity_update(struct display_entity *entity,
+			     const struct videomode *mode)
 {
 	if (!entity->ops.ctrl || !entity->ops.ctrl->update)
 		return 0;
 
-	return entity->ops.ctrl->update(entity);
+	return entity->ops.ctrl->update(entity, mode);
 }
 EXPORT_SYMBOL_GPL(display_entity_update);
 
diff --git a/include/video/display.h b/include/video/display.h
index 90d18ca..64f84d5 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -77,7 +77,8 @@ struct display_entity_interface_params {
 struct display_entity_control_ops {
 	int (*set_state)(struct display_entity *ent,
 			 enum display_entity_state state);
-	int (*update)(struct display_entity *ent);
+	int (*update)(struct display_entity *ent,
+			 const struct videomode *mode);
 	int (*get_modes)(struct display_entity *ent,
 			 const struct videomode **modes);
 	int (*get_params)(struct display_entity *ent,
@@ -111,7 +112,8 @@ struct display_entity {
 
 int display_entity_set_state(struct display_entity *entity,
 			     enum display_entity_state state);
-int display_entity_update(struct display_entity *entity);
+int display_entity_update(struct display_entity *entity,
+			     const struct videomode *mode);
 int display_entity_get_modes(struct display_entity *entity,
 			     const struct videomode **modes);
 int display_entity_get_params(struct display_entity *entity,
-- 
1.7.10.4



^ permalink raw reply related

* [RFC 01/10] video: Add generic display entity core
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
  To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel
  Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
	Laurent Pinchart
In-Reply-To: <1366211842-21497-1-git-send-email-pawel.moll@arm.com>

From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/Kconfig                |    1 +
 drivers/video/Makefile               |    1 +
 drivers/video/display/Kconfig        |    4 +
 drivers/video/display/Makefile       |    1 +
 drivers/video/display/display-core.c |  362 ++++++++++++++++++++++++++++++++++
 include/video/display.h              |  150 ++++++++++++++
 6 files changed, 519 insertions(+)
 create mode 100644 drivers/video/display/Kconfig
 create mode 100644 drivers/video/display/Makefile
 create mode 100644 drivers/video/display/display-core.c
 create mode 100644 include/video/display.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 4c1546f..281e548 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2456,6 +2456,7 @@ source "drivers/video/omap2/Kconfig"
 source "drivers/video/exynos/Kconfig"
 source "drivers/video/mmp/Kconfig"
 source "drivers/video/backlight/Kconfig"
+source "drivers/video/display/Kconfig"
 
 if VT
 	source "drivers/video/console/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 9df3873..b989e8e 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -15,6 +15,7 @@ fb-objs                           := $(fb-y)
 obj-$(CONFIG_VT)		  += console/
 obj-$(CONFIG_LOGO)		  += logo/
 obj-y				  += backlight/
+obj-y				  += display/
 
 obj-$(CONFIG_EXYNOS_VIDEO)     += exynos/
 
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
new file mode 100644
index 0000000..1d533e7
--- /dev/null
+++ b/drivers/video/display/Kconfig
@@ -0,0 +1,4 @@
+menuconfig DISPLAY_CORE
+	tristate "Display Core"
+	---help---
+	  Support common display framework for graphics devices.
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
new file mode 100644
index 0000000..bd93496
--- /dev/null
+++ b/drivers/video/display/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DISPLAY_CORE) += display-core.o
diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
new file mode 100644
index 0000000..d2daa15
--- /dev/null
+++ b/drivers/video/display/display-core.c
@@ -0,0 +1,362 @@
+/*
+ * Display Core
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+#include <video/display.h>
+#include <video/videomode.h>
+
+static LIST_HEAD(display_entity_list);
+static LIST_HEAD(display_entity_notifiers);
+static DEFINE_MUTEX(display_entity_mutex);
+
+/* -----------------------------------------------------------------------------
+ * Control operations
+ */
+
+/**
+ * display_entity_set_state - Set the display entity operation state
+ * @entity: The display entity
+ * @state: Display entity operation state
+ *
+ * See &enum display_entity_state for information regarding the entity states.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_set_state(struct display_entity *entity,
+			     enum display_entity_state state)
+{
+	int ret;
+
+	if (entity->state == state)
+		return 0;
+
+	if (!entity->ops.ctrl || !entity->ops.ctrl->set_state)
+		return 0;
+
+	ret = entity->ops.ctrl->set_state(entity, state);
+	if (ret < 0)
+		return ret;
+
+	entity->state = state;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_set_state);
+
+/**
+ * display_entity_update - Update the display
+ * @entity: The display entity
+ *
+ * Make the display entity ready to receive pixel data and start frame transfer.
+ * This operation can only be called if the display entity is in STANDBY or ON
+ * state.
+ *
+ * The display entity will call the upstream entity in the video chain to start
+ * the video stream.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_update(struct display_entity *entity)
+{
+	if (!entity->ops.ctrl || !entity->ops.ctrl->update)
+		return 0;
+
+	return entity->ops.ctrl->update(entity);
+}
+EXPORT_SYMBOL_GPL(display_entity_update);
+
+/**
+ * display_entity_get_modes - Get video modes supported by the display entity
+ * @entity The display entity
+ * @modes: Pointer to an array of modes
+ *
+ * Fill the modes argument with a pointer to an array of video modes. The array
+ * is owned by the display entity.
+ *
+ * Return the number of supported modes on success (including 0 if no mode is
+ * supported) or a negative error code otherwise.
+ */
+int display_entity_get_modes(struct display_entity *entity,
+			     const struct videomode **modes)
+{
+	if (!entity->ops.ctrl || !entity->ops.ctrl->get_modes)
+		return 0;
+
+	return entity->ops.ctrl->get_modes(entity, modes);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_modes);
+
+/**
+ * display_entity_get_size - Get display entity physical size
+ * @entity: The display entity
+ * @width: Physical width in millimeters
+ * @height: Physical height in millimeters
+ *
+ * When applicable, for instance for display panels, retrieve the display
+ * physical size in millimeters.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_get_size(struct display_entity *entity,
+			    unsigned int *width, unsigned int *height)
+{
+	if (!entity->ops.ctrl || !entity->ops.ctrl->get_size)
+		return -EOPNOTSUPP;
+
+	return entity->ops.ctrl->get_size(entity, width, height);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_size);
+
+/**
+ * display_entity_get_params - Get display entity interface parameters
+ * @entity: The display entity
+ * @params: Pointer to interface parameters
+ *
+ * Fill the parameters structure pointed to by the params argument with display
+ * entity interface parameters.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_get_params(struct display_entity *entity,
+			      struct display_entity_interface_params *params)
+{
+	if (!entity->ops.ctrl || !entity->ops.ctrl->get_modes)
+		return -EOPNOTSUPP;
+
+	return entity->ops.ctrl->get_params(entity, params);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_params);
+
+/* -----------------------------------------------------------------------------
+ * Video operations
+ */
+
+/**
+ * display_entity_set_stream - Control the video stream state
+ * @entity: The display entity
+ * @state: Display video stream state
+ *
+ * Control the video stream state at the entity video output.
+ *
+ * See &enum display_entity_stream_state for information regarding the stream
+ * states.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_set_stream(struct display_entity *entity,
+			      enum display_entity_stream_state state)
+{
+	if (!entity->ops.video || !entity->ops.video->set_stream)
+		return 0;
+
+	return entity->ops.video->set_stream(entity, state);
+}
+EXPORT_SYMBOL_GPL(display_entity_set_stream);
+
+/* -----------------------------------------------------------------------------
+ * Connections
+ */
+
+/**
+ * display_entity_connect - Connect two entities through a video stream
+ * @source: The video stream source
+ * @sink: The video stream sink
+ *
+ * Set the sink entity source field to the source entity.
+ */
+
+/**
+ * display_entity_disconnect - Disconnect two previously connected entities
+ * @source: The video stream source
+ * @sink: The video stream sink
+ *
+ * Break a connection between two previously connected entities. The source
+ * entity source field is reset to NULL.
+ */
+
+/* -----------------------------------------------------------------------------
+ * Registration and notification
+ */
+
+static void display_entity_release(struct kref *ref)
+{
+	struct display_entity *entity =
+		container_of(ref, struct display_entity, ref);
+
+	if (entity->release)
+		entity->release(entity);
+}
+
+/**
+ * display_entity_get - get a reference to a display entity
+ * @display_entity: the display entity
+ *
+ * Return the display entity pointer.
+ */
+struct display_entity *display_entity_get(struct display_entity *entity)
+{
+	if (entity == NULL)
+		return NULL;
+
+	kref_get(&entity->ref);
+	return entity;
+}
+EXPORT_SYMBOL_GPL(display_entity_get);
+
+/**
+ * display_entity_put - release a reference to a display entity
+ * @display_entity: the display entity
+ *
+ * Releasing the last reference to a display entity releases the display entity
+ * itself.
+ */
+void display_entity_put(struct display_entity *entity)
+{
+	kref_put(&entity->ref, display_entity_release);
+}
+EXPORT_SYMBOL_GPL(display_entity_put);
+
+static int display_entity_notifier_match(struct display_entity *entity,
+				struct display_entity_notifier *notifier)
+{
+	return notifier->dev == NULL || notifier->dev == entity->dev;
+}
+
+/**
+ * display_entity_register_notifier - register a display entity notifier
+ * @notifier: display entity notifier structure we want to register
+ *
+ * Display entity notifiers are called to notify drivers of display
+ * entity-related events for matching display_entitys.
+ *
+ * Notifiers and display_entitys are matched through the device they correspond
+ * to. If the notifier dev field is equal to the display entity dev field the
+ * notifier will be called when an event is reported. Notifiers with a NULL dev
+ * field act as catch-all and will be called for all display_entitys.
+ *
+ * Supported events are
+ *
+ * - DISPLAY_ENTITY_NOTIFIER_CONNECT reports display entity connection and is
+ *   sent at display entity or notifier registration time
+ * - DISPLAY_ENTITY_NOTIFIER_DISCONNECT reports display entity disconnection and
+ *   is sent at display entity unregistration time
+ *
+ * Registering a notifier sends DISPLAY_ENTITY_NOTIFIER_CONNECT events for all
+ * previously registered display_entitys that match the notifiers.
+ *
+ * Return 0 on success.
+ */
+int display_entity_register_notifier(struct display_entity_notifier *notifier)
+{
+	struct display_entity *entity;
+
+	mutex_lock(&display_entity_mutex);
+	list_add_tail(&notifier->list, &display_entity_notifiers);
+
+	list_for_each_entry(entity, &display_entity_list, list) {
+		if (!display_entity_notifier_match(entity, notifier))
+			continue;
+
+		if (notifier->notify(notifier, entity,
+				     DISPLAY_ENTITY_NOTIFIER_CONNECT))
+			break;
+	}
+	mutex_unlock(&display_entity_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_register_notifier);
+
+/**
+ * display_entity_unregister_notifier - unregister a display entity notifier
+ * @notifier: display entity notifier to be unregistered
+ *
+ * Unregistration guarantees that the notifier will never be called upon return
+ * of this function.
+ */
+void display_entity_unregister_notifier(struct display_entity_notifier *notifier)
+{
+	mutex_lock(&display_entity_mutex);
+	list_del(&notifier->list);
+	mutex_unlock(&display_entity_mutex);
+}
+EXPORT_SYMBOL_GPL(display_entity_unregister_notifier);
+
+/**
+ * display_entity_register - register a display entity
+ * @display_entity: display entity to be registered
+ *
+ * Register the display entity and send the DISPLAY_ENTITY_NOTIFIER_CONNECT
+ * event to all matching registered notifiers.
+ *
+ * Return 0 on success.
+ */
+int __must_check __display_entity_register(struct display_entity *entity,
+					   struct module *owner)
+{
+	struct display_entity_notifier *notifier;
+
+	kref_init(&entity->ref);
+	entity->owner = owner;
+	entity->state = DISPLAY_ENTITY_STATE_OFF;
+
+	mutex_lock(&display_entity_mutex);
+	list_add(&entity->list, &display_entity_list);
+
+	list_for_each_entry(notifier, &display_entity_notifiers, list) {
+		if (!display_entity_notifier_match(entity, notifier))
+			continue;
+
+		if (notifier->notify(notifier, entity,
+				     DISPLAY_ENTITY_NOTIFIER_CONNECT))
+			break;
+	}
+	mutex_unlock(&display_entity_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(__display_entity_register);
+
+/**
+ * display_entity_unregister - unregister a display entity
+ * @display_entity: display entity to be unregistered
+ *
+ * Unregister the display entity and send the DISPLAY_ENTITY_NOTIFIER_DISCONNECT
+ * event to all matching registered notifiers.
+ */
+void display_entity_unregister(struct display_entity *entity)
+{
+	struct display_entity_notifier *notifier;
+
+	mutex_lock(&display_entity_mutex);
+	list_for_each_entry(notifier, &display_entity_notifiers, list) {
+		if (!display_entity_notifier_match(entity, notifier))
+			continue;
+
+		notifier->notify(notifier, entity,
+				 DISPLAY_ENTITY_NOTIFIER_DISCONNECT);
+	}
+
+	list_del(&entity->list);
+	mutex_unlock(&display_entity_mutex);
+
+	display_entity_put(entity);
+}
+EXPORT_SYMBOL_GPL(display_entity_unregister);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Display Core");
+MODULE_LICENSE("GPL");
diff --git a/include/video/display.h b/include/video/display.h
new file mode 100644
index 0000000..90d18ca
--- /dev/null
+++ b/include/video/display.h
@@ -0,0 +1,150 @@
+/*
+ * Display Core
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __DISPLAY_H__
+#define __DISPLAY_H__
+
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+/* -----------------------------------------------------------------------------
+ * Display Entity
+ */
+
+struct display_entity;
+struct videomode;
+
+#define DISPLAY_ENTITY_NOTIFIER_CONNECT		1
+#define DISPLAY_ENTITY_NOTIFIER_DISCONNECT	2
+
+struct display_entity_notifier {
+	int (*notify)(struct display_entity_notifier *, struct display_entity *,
+		      int);
+	struct device *dev;
+	struct list_head list;
+};
+
+/**
+ * enum display_entity_state - State of a display entity
+ * @DISPLAY_ENTITY_STATE_OFF: The entity is turned off completely, possibly
+ *	including its power supplies. Communication with a display entity in
+ *	that state is not possible.
+ * @DISPLAY_ENTITY_STATE_STANDBY: The entity is in a low-power state. Full
+ *	communication with the display entity is supported, including pixel data
+ *	transfer, but the output is kept blanked.
+ * @DISPLAY_ENTITY_STATE_ON: The entity is fully operational.
+ */
+enum display_entity_state {
+	DISPLAY_ENTITY_STATE_OFF,
+	DISPLAY_ENTITY_STATE_STANDBY,
+	DISPLAY_ENTITY_STATE_ON,
+};
+
+/**
+ * enum display_entity_stream_state - State of a video stream
+ * @DISPLAY_ENTITY_STREAM_STOPPED: The video stream is stopped, no frames are
+ *	transferred.
+ * @DISPLAY_ENTITY_STREAM_SINGLE_SHOT: The video stream has been started for
+ *      single shot operation. The source entity will transfer a single frame
+ *      and then stop.
+ * @DISPLAY_ENTITY_STREAM_CONTINUOUS: The video stream is running, frames are
+ *	transferred continuously by the source entity.
+ */
+enum display_entity_stream_state {
+	DISPLAY_ENTITY_STREAM_STOPPED,
+	DISPLAY_ENTITY_STREAM_SINGLE_SHOT,
+	DISPLAY_ENTITY_STREAM_CONTINUOUS,
+};
+
+enum display_entity_interface_type {
+	DISPLAY_ENTITY_INTERFACE_DPI,
+};
+
+struct display_entity_interface_params {
+	enum display_entity_interface_type type;
+};
+
+struct display_entity_control_ops {
+	int (*set_state)(struct display_entity *ent,
+			 enum display_entity_state state);
+	int (*update)(struct display_entity *ent);
+	int (*get_modes)(struct display_entity *ent,
+			 const struct videomode **modes);
+	int (*get_params)(struct display_entity *ent,
+			  struct display_entity_interface_params *params);
+	int (*get_size)(struct display_entity *ent,
+			unsigned int *width, unsigned int *height);
+};
+
+struct display_entity_video_ops {
+	int (*set_stream)(struct display_entity *ent,
+			  enum display_entity_stream_state state);
+};
+
+struct display_entity {
+	struct list_head list;
+	struct device *dev;
+	struct module *owner;
+	struct kref ref;
+
+	struct display_entity *source;
+
+	struct {
+		const struct display_entity_control_ops *ctrl;
+		const struct display_entity_video_ops *video;
+	} ops;
+
+	void(*release)(struct display_entity *ent);
+
+	enum display_entity_state state;
+};
+
+int display_entity_set_state(struct display_entity *entity,
+			     enum display_entity_state state);
+int display_entity_update(struct display_entity *entity);
+int display_entity_get_modes(struct display_entity *entity,
+			     const struct videomode **modes);
+int display_entity_get_params(struct display_entity *entity,
+			      struct display_entity_interface_params *params);
+int display_entity_get_size(struct display_entity *entity,
+			    unsigned int *width, unsigned int *height);
+
+int display_entity_set_stream(struct display_entity *entity,
+			      enum display_entity_stream_state state);
+
+static inline void display_entity_connect(struct display_entity *source,
+					  struct display_entity *sink)
+{
+	sink->source = source;
+}
+
+static inline void display_entity_disconnect(struct display_entity *source,
+					     struct display_entity *sink)
+{
+	sink->source = NULL;
+}
+
+struct display_entity *display_entity_get(struct display_entity *entity);
+void display_entity_put(struct display_entity *entity);
+
+int __must_check __display_entity_register(struct display_entity *entity,
+					   struct module *owner);
+void display_entity_unregister(struct display_entity *entity);
+
+int display_entity_register_notifier(struct display_entity_notifier *notifier);
+void display_entity_unregister_notifier(struct display_entity_notifier *notifier);
+
+#define display_entity_register(display_entity) \
+	__display_entity_register(display_entity, THIS_MODULE)
+
+#endif /* __DISPLAY_H__ */
-- 
1.7.10.4



^ permalink raw reply related

* [RFC 00/10] Versatile Express CLCD DVI output support
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
  To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel
  Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
	Pawel Moll

Hello All,

This series implements support for the Versatile Express
video output pipeline, which is not the simplest one available...

It is meant as a RFC only and I'm hoping to attract all possible
feedback (*including* naming ;-).

The VE's "MultiMedia Bus" [1] comprises three video signal sources
(motherboard's CLCD cell and a implementation-specific driver
on each of the daugtherboards) and a FPGA multiplexer routing
data from one of the sources to DVI/HDMI formatter chip (Sii9022).

+-----+
| DB1 |>--+                         DVI connector
+-----+   |   +------+                   +--+
          +-->|      |                   |oo|
+-----+       | mux  |    +---------+    |oo|
| DB2 |>----->|      |>-->| sii9022 |>-->|oo|
+-----+       | FPGA |    +---------+    |oo|
          +-->|      |                   |oo|
+-----+   |   +------+                   +--+
| MB  |>--+
+-----+

The series is based on Laurent Pinchart's Common Display Framework
patch (not in mainline yet, v2 discussed here: [2]) and extends it
by adding DT bindings and basic support for TFT panels.

The CLCD driver has been adapted to work with the framework and
the Device Tree information.

Also a set of drivers for the VE-specific components is included
(note that the sii9022 is now driven via the moterboard firmware;
this is intended to be replaced by a proper I2C driver for the
chip).

It is worth mentioning that the CDF caters for both fbdev and DRM
so the solution should be suitable for all potential DRM-driven
display controllers.

[1] http://infocenter.arm.com/help/topic/com.arm.doc.dui0447h/CHDEHEAA.html#CACGIGGC
[2] http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/57298


Laurent Pinchart (1):
  video: Add generic display entity core

Pawel Moll (9):
  video: display: Update the display with the video mode data
  video: display: Add Device Tree bindings
  video: display: Add generic TFT display type
  fbmon: Add extra video helper
  video: ARM CLCD: Add DT & CDF support
  mfd: vexpress: Allow external drivers to parse site ids
  video: Versatile Express MUXFPGA driver
  video: Versatile Express DVI mode driver
  ARM: vexpress: Add CLCD Device Tree properties

 .../testing/sysfs-driver-video-vexpress-muxfpga    |    5 +
 .../devicetree/bindings/video/arm,pl11x.txt        |   35 ++
 .../devicetree/bindings/video/display-bindings.txt |   75 ++++
 arch/arm/boot/dts/vexpress-v2m-rs1.dtsi            |   17 +-
 arch/arm/boot/dts/vexpress-v2m.dtsi                |   17 +-
 arch/arm/boot/dts/vexpress-v2p-ca9.dts             |    5 +
 drivers/mfd/vexpress-sysreg.c                      |    5 +
 drivers/video/Kconfig                              |    2 +
 drivers/video/Makefile                             |    5 +
 drivers/video/amba-clcd.c                          |  244 +++++++++++
 drivers/video/display/Kconfig                      |    4 +
 drivers/video/display/Makefile                     |    1 +
 drivers/video/display/display-core.c               |  447 ++++++++++++++++++++
 drivers/video/fbmon.c                              |   29 ++
 drivers/video/vexpress-dvimode.c                   |  158 +++++++
 drivers/video/vexpress-muxfpga.c                   |  228 ++++++++++
 include/linux/amba/clcd.h                          |    2 +
 include/linux/fb.h                                 |    3 +
 include/linux/vexpress.h                           |    2 +
 include/video/display.h                            |  172 ++++++++
 20 files changed, 1448 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-driver-video-vexpress-muxfpga
 create mode 100644 Documentation/devicetree/bindings/video/arm,pl11x.txt
 create mode 100644 Documentation/devicetree/bindings/video/display-bindings.txt
 create mode 100644 drivers/video/display/Kconfig
 create mode 100644 drivers/video/display/Makefile
 create mode 100644 drivers/video/display/display-core.c
 create mode 100644 drivers/video/vexpress-dvimode.c
 create mode 100644 drivers/video/vexpress-muxfpga.c
 create mode 100644 include/video/display.h

-- 
1.7.10.4



^ permalink raw reply

* Re: [PATCH 4/8] video: atmel_lcdfb: add device tree suport
From: Nicolas Ferre @ 2013-04-16 15:43 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130416134404.GA4998@game.jcrosoft.org>

On 04/16/2013 03:44 PM, Jean-Christophe PLAGNIOL-VILLARD :
> On 15:42 Tue 16 Apr     , Nicolas Ferre wrote:
>> On 04/11/2013 05:00 PM, Jean-Christophe PLAGNIOL-VILLARD :
>>> get display timings from device tree
>>> Use videomode helpers to get display timings and configurations from
>>> device tree
>>
>> 2 sentences? Simply elaborate the 2nd one and it will be good.
>>
>>> Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
>>> Cc: linux-fbdev@vger.kernel.org
>>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>>> Cc: Andrew Morton <akpm@linux-foundation.org>
>>> ---
>>>  .../devicetree/bindings/video/atmel,lcdc.txt       |   75 ++++++
>>>  drivers/video/Kconfig                              |    2 +
>>>  drivers/video/atmel_lcdfb.c                        |  244 +++++++++++++++++---
>>>  3 files changed, 289 insertions(+), 32 deletions(-)
>>>  create mode 100644 Documentation/devicetree/bindings/video/atmel,lcdc.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/video/atmel,lcdc.txt b/Documentation/devicetree/bindings/video/atmel,lcdc.txt
>>> new file mode 100644
>>> index 0000000..1ec175e
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/video/atmel,lcdc.txt
>>
>> Why lcdc? I would have preferred atmel,lcdfb, like the driver's name: it
>> is even more self-explanatory...
> we do not describe drivers but IP

fine, but in
static const struct platform_device_id atmel_lcdfb_devtypes, we use
"xxx-lcdfb" type...

>>
>>> @@ -0,0 +1,75 @@
>>> +Atmel LCDC Framebuffer
>>> +-----------------------------------------------------
>>> +
>>> +Required properties:
>>> +- compatible :
>>> +	"atmel,at91sam9261-lcdc" , 
>>> +	"atmel,at91sam9263-lcdc" ,
>>> +	"atmel,at91sam9g10-lcdc" ,
>>> +	"atmel,at91sam9g45-lcdc" ,
>>> +	"atmel,at91sam9g45es-lcdc" ,
>>> +	"atmel,at91sam9rl-lcdc" ,
>>> +	"atmel,at32ap-lcdc"
>>> +- reg : Should contain 1 register ranges(address and length)
>>> +- interrupts : framebuffer controller interrupt
>>> +- display: a phandle pointing to the display node
>>> +
>>> +Required nodes:
>>> +- display: a display node is required to initialize the lcd panel
>>> +	This should be in the board dts.
>>> +- default-mode: a videomode within the display with timing parameters
>>> +	as specified below.
>>> +
>>> +Example:
>>> +
>>> +	fb0: fb@0x00500000 {
>>> +		compatible = "atmel,at91sam9g45-lcdc";
>>> +		reg = <0x00500000 0x1000>;
>>> +		interrupts = <23 3 0>;
>>> +		pinctrl-names = "default";
>>> +		pinctrl-0 = <&pinctrl_fb>;
>>> +		display = <&display0>;
>>> +		status = "okay";
>>> +		#address-cells = <1>;
>>> +		#size-cells = <1>;
>>> +
>>> +	};
>>> +
>>> +Atmel LCDC Display
>>> +-----------------------------------------------------
>>> +Required properties (as per of_videomode_helper):
>>
>> Can you please point somewhere to the documentation:
>> Documentation/devicetree/bindings/video/display-timing.txt
>>
>>> + - atmel,dmacon: dma controler configuration
>>
>> Typo: controller.
>>
>>> + - atmel,lcdcon2: lcd controler configuration
>>
>> Ditto
>>
>>> + - atmel,guard-time: lcd guard time (Delay in frame periods)
>>
>> periods -> period, no?
> no it's periods even in the datasheet
>>
>>> + - bits-per-pixel: lcd panel bit-depth.
>>> +
>>> +Optional properties (as per of_videomode_helper):
>>> + - atmel,lcdcon-backlight: enable backlight
>>> + - atmel,lcd-wiring-mode: lcd wiring mode "RGB" or "BRG"
>>
>> Is it a sting, or a number (as seen below)? If it is a number, please
>> tell how to choose the index.
> String

Okay: so tell it in the description and correct the example below.


>>> + - atmel,power-control-gpio: gpio to power on or off the LCD (as many as needed)
>>> +
>>> +Example:
>>> +	display0: display {
>>> +		bits-per-pixel = <32>;
>>> +		atmel,lcdcon-backlight;
>>> +		atmel,dmacon = <0x1>;
>>> +		atmel,lcdcon2 = <0x80008002>;
>>> +		atmel,guard-time = <9>;
>>> +		atmel,lcd-wiring-mode = <1>;

Here ----------------------------------^^^^

>>> +
>>> +		display-timings {
>>> +			native-mode = <&timing0>;
>>> +			timing0: timing0 {
>>> +				clock-frequency = <9000000>;
>>> +				hactive = <480>;
>>> +				vactive = <272>;
>>> +				hback-porch = <1>;
>>> +				hfront-porch = <1>;
>>> +				vback-porch = <40>;
>>> +				vfront-porch = <1>;
>>> +				hsync-len = <45>;
>>> +				vsync-len = <1>;
>>> +			};
>>> +		};
>>> +	};
>>> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
>>> index 4c1546f..0687482 100644
>>> --- a/drivers/video/Kconfig
>>> +++ b/drivers/video/Kconfig
>>> @@ -1018,6 +1018,8 @@ config FB_ATMEL
>>>  	select FB_CFB_FILLRECT
>>>  	select FB_CFB_COPYAREA
>>>  	select FB_CFB_IMAGEBLIT
>>> +	select FB_MODE_HELPERS
>>> +	select OF_VIDEOMODE
>>>  	help
>>>  	  This enables support for the AT91/AT32 LCD Controller.
>>>  
>>> diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
>>> index f67e226..4a31570 100644
>>> --- a/drivers/video/atmel_lcdfb.c
>>> +++ b/drivers/video/atmel_lcdfb.c
>>> @@ -20,7 +20,11 @@
>>>  #include <linux/gfp.h>
>>>  #include <linux/module.h>
>>>  #include <linux/platform_data/atmel.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/of_gpio.h>
>>>  #include <video/of_display_timing.h>
>>
>> As said in patch 1/8, this one belongs to this 4/8 patch.
>>
>>> +#include <video/videomode.h>
>>>  
>>>  #include <mach/cpu.h>
>>>  #include <asm/gpio.h>
>>> @@ -59,6 +63,13 @@ struct atmel_lcdfb_info {
>>>  	struct atmel_lcdfb_config *config;
>>>  };
>>>  
>>> +struct atmel_lcdfb_power_ctrl_gpio {
>>> +	int gpio;
>>> +	int active_low;
>>> +
>>> +	struct list_head list;
>>> +};
>>> +
>>>  #define lcdc_readl(sinfo, reg)		__raw_readl((sinfo)->mmio+(reg))
>>>  #define lcdc_writel(sinfo, reg, val)	__raw_writel((val), (sinfo)->mmio+(reg))
>>>  
>>> @@ -945,16 +956,187 @@ static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo)
>>>  	clk_disable(sinfo->lcdc_clk);
>>>  }
>>>  
>>> +#ifdef CONFIG_OF
>>> +static const struct of_device_id atmel_lcdfb_dt_ids[] = {
>>> +	{ .compatible = "atmel,at91sam9261-lcdc" , .data = &at91sam9261_config, },
>>> +	{ .compatible = "atmel,at91sam9263-lcdc" , .data = &at91sam9263_config, },
>>> +	{ .compatible = "atmel,at91sam9g10-lcdc" , .data = &at91sam9g10_config, },
>>> +	{ .compatible = "atmel,at91sam9g45-lcdc" , .data = &at91sam9g45_config, },
>>> +	{ .compatible = "atmel,at91sam9g45es-lcdc" , .data = &at91sam9g45es_config, },
>>> +	{ .compatible = "atmel,at91sam9rl-lcdc" , .data = &at91sam9rl_config, },
>>> +	{ .compatible = "atmel,at32ap-lcdc" , .data = &at32ap_config, },
>>> +	{ /* sentinel */ }
>>> +};
>>> +
>>> +MODULE_DEVICE_TABLE(of, atmel_lcdfb_dt_ids);
>>> +
>>> +static const char *atmel_lcdfb_wiring_modes[] = {
>>> +	[ATMEL_LCDC_WIRING_BGR]	= "BRG",
>>> +	[ATMEL_LCDC_WIRING_RGB]	= "RGB",
>>> +};
>>> +
>>> +const int atmel_lcdfb_get_of_wiring_modes(struct device_node *np)
>>> +{
>>> +	const char *mode;
>>> +	int err, i;
>>> +
>>> +	err = of_property_read_string(np, "atmel,lcd-wiring-mode", &mode);
>>> +	if (err < 0)
>>> +		return ATMEL_LCDC_WIRING_BGR;
>>> +
>>> +	for (i = 0; i < ARRAY_SIZE(atmel_lcdfb_wiring_modes); i++)
>>> +		if (!strcasecmp(mode, atmel_lcdfb_wiring_modes[i]))
>>> +			return i;
>>> +
>>> +	return -ENODEV;
>>> +}
>>> +
>>> +static void atmel_lcdfb_power_control_gpio(struct atmel_lcdfb_pdata *pdata, int on)
>>> +{
>>> +	struct atmel_lcdfb_power_ctrl_gpio *og;
>>> +
>>> +	list_for_each_entry(og, &pdata->pwr_gpios, list)
>>> +		gpio_set_value(og->gpio, on);
>>> +}
>>> +
>>> +static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo)
>>> +{
>>> +	struct fb_info *info = sinfo->info;
>>> +	struct atmel_lcdfb_pdata *pdata = &sinfo->pdata;
>>> +	struct fb_var_screeninfo *var = &info->var;
>>> +	struct device *dev = &sinfo->pdev->dev;
>>> +	struct device_node *np Þv->of_node;
>>> +	struct device_node *display_np;
>>> +	struct device_node *timings_np;
>>> +	struct display_timings *timings;
>>> +	enum of_gpio_flags flags;
>>> +	struct atmel_lcdfb_power_ctrl_gpio *og;
>>> +	bool is_gpio_power = false;
>>> +	int ret = -ENOENT;
>>> +	int i, gpio;
>>> +
>>> +	sinfo->config = (struct atmel_lcdfb_config*)
>>> +		of_match_device(atmel_lcdfb_dt_ids, dev)->data;
>>
>> Please split it in 2 steps, otherwise the day that the drivers doesn't
>> find the device in dt_ids table, it hangs here with an Oops.
> no as you will never end here in this case

Ok, I see.

>>
>>> +
>>> +	display_np = of_parse_phandle(np, "display", 0);
>>> +	if (!display_np) {
>>> +		dev_err(dev, "failed to find display phandle\n");
>>> +		return -ENOENT;
>>> +	}
>>> +
>>> +	ret = of_property_read_u32(display_np, "bits-per-pixel", &var->bits_per_pixel);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "failed to get property bits-per-pixel\n");
>>> +		goto put_display_node;
>>> +	}
>>> +
>>> +	ret = of_property_read_u32(display_np, "atmel,guard-time", &pdata->guard_time);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "failed to get property atmel,guard-time\n");
>>> +		goto put_display_node;
>>> +	}
>>> +
>>> +	ret = of_property_read_u32(display_np, "atmel,lcdcon2", &pdata->default_lcdcon2);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "failed to get property atmel,lcdcon2\n");
>>> +		goto put_display_node;
>>> +	}
>>> +
>>> +	ret = of_property_read_u32(display_np, "atmel,dmacon", &pdata->default_dmacon);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "failed to get property bits-per-pixel\n");
>>
>> No, wrong error message.
>>
>>> +		goto put_display_node;
>>> +	}
>>> +
>>> +	ret = -ENOMEM;
>>> +	for (i = 0; i < of_gpio_named_count(display_np, "atmel,power-control-gpio"); i++) {
>>> +		gpio = of_get_named_gpio_flags(display_np, "atmel,power-control-gpio",
>>> +					       i, &flags);
>>> +		if (gpio < 0)
>>> +			continue;
>>> +
>>> +		og = devm_kzalloc(dev, sizeof(*og), GFP_KERNEL);
>>> +		if (!og)
>>> +			goto put_display_node;
>>> +
>>> +		og->gpio = gpio;
>>> +		og->active_low = flags & OF_GPIO_ACTIVE_LOW;
>>> +		is_gpio_power = true;
>>> +		ret = devm_gpio_request(dev, gpio, "lcd-power-control-gpio");
>>> +		if (ret) {
>>> +			dev_err(dev, "request gpio %d failed\n", gpio);
>>> +			goto put_display_node;
>>> +		}
>>> +
>>> +		ret = gpio_direction_output(gpio, og->active_low);
>>> +		if (ret) {
>>> +			dev_err(dev, "set direction output gpio %d failed\n", gpio);
>>> +			goto put_display_node;
>>> +		}
>>> +	}
>>> +
>>> +	if (is_gpio_power)
>>> +		pdata->atmel_lcdfb_power_control = atmel_lcdfb_power_control_gpio;
>>> +
>>> +	ret = atmel_lcdfb_get_of_wiring_modes(display_np);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "invalid atmel,lcd-wiring-mode\n");
>>> +		goto put_display_node;
>>> +	}
>>> +	pdata->lcd_wiring_mode = ret;
>>> +
>>> +	pdata->lcdcon_is_backlight = of_property_read_bool(display_np, "atmel,lcdcon-backlight");
>>> +
>>> +	timings = of_get_display_timings(display_np);
>>> +	if (!timings) {
>>> +		dev_err(dev, "failed to get display timings\n");
>>> +		goto put_display_node;
>>> +	}
>>> +
>>> +	timings_np = of_find_node_by_name(display_np, "display-timings");
>>> +	if (!timings_np) {
>>> +		dev_err(dev, "failed to find display-timings node\n");
>>> +		goto put_display_node;
>>> +	}
>>> +
>>> +	for (i = 0; i < of_get_child_count(timings_np); i++) {
>>> +		struct videomode vm;
>>> +		struct fb_videomode fb_vm;
>>> +
>>> +		ret = videomode_from_timing(timings, &vm, i);
>>> +		if (ret < 0)
>>> +			goto put_timings_node;
>>> +		ret = fb_videomode_from_videomode(&vm, &fb_vm);
>>> +		if (ret < 0)
>>> +			goto put_timings_node;
>>> +
>>> +		fb_add_videomode(&fb_vm, &info->modelist);
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +put_timings_node:
>>> +	of_node_put(timings_np);
>>> +put_display_node:
>>> +	of_node_put(display_np);
>>> +	return ret;
>>> +}
>>> +#else
>>> +static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo)
>>> +{
>>> +	return 0;
>>> +}
>>> +#endif
>>>  
>>>  static int __init atmel_lcdfb_probe(struct platform_device *pdev)
>>>  {
>>>  	struct device *dev = &pdev->dev;
>>>  	struct fb_info *info;
>>>  	struct atmel_lcdfb_info *sinfo;
>>> -	struct atmel_lcdfb_pdata *pdata;
>>> -	struct fb_videomode fbmode;
>>> +	struct atmel_lcdfb_pdata *pdata = NULL;
>>>  	struct resource *regs = NULL;
>>>  	struct resource *map = NULL;
>>> +	struct fb_modelist *modelist;
>>>  	int ret;
>>>  
>>>  	dev_dbg(dev, "%s BEGIN\n", __func__);
>>> @@ -967,17 +1149,35 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
>>>  	}
>>>  
>>>  	sinfo = info->par;
>>> +	sinfo->pdev = pdev;
>>> +	sinfo->info = info;
>>> +
>>> +	INIT_LIST_HEAD(&info->modelist);
>>>  
>>> -	if (dev->platform_data) {
>>> -		pdata = (struct atmel_lcdfb_pdata *)dev->platform_data;
>>> +	if (pdev->dev.of_node) {
>>> +		ret = atmel_lcdfb_of_init(sinfo);
>>> +		if (ret)
>>> +			goto free_info;
>>> +	} else if (dev->platform_data) {
>>> +		struct fb_monspecs *monspecs;
>>> +		int i;
>>> +
>>> +		pdata = dev->platform_data;
>>> +		monspecs = pdata->default_monspecs;
>>>  		sinfo->pdata = *pdata;
>>> +
>>> +		for (i = 0; i < monspecs->modedb_len; i++)
>>> +			fb_add_videomode(&monspecs->modedb[i], &info->modelist);
>>> +
>>> +		sinfo->config = atmel_lcdfb_get_config(pdev);
>>> +
>>> +		info->var.bits_per_pixel = pdata->default_bpp ? pdata->default_bpp : 16;
>>> +		memcpy(&info->monspecs, pdata->default_monspecs, sizeof(info->monspecs));
>>>  	} else {
>>>  		dev_err(dev, "cannot get default configuration\n");
>>>  		goto free_info;
>>>  	}
>>> -	sinfo->info = info;
>>> -	sinfo->pdev = pdev;
>>> -	sinfo->config = atmel_lcdfb_get_config(pdev);
>>> +
>>>  	if (!sinfo->config)
>>>  		goto free_info;
>>>  
>>> @@ -986,7 +1186,6 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
>>>  	info->pseudo_palette = sinfo->pseudo_palette;
>>>  	info->fbops = &atmel_lcdfb_ops;
>>>  
>>> -	memcpy(&info->monspecs, pdata->default_monspecs, sizeof(info->monspecs));
>>>  	info->fix = atmel_lcdfb_fix;
>>>  
>>>  	/* Enable LCDC Clocks */
>>> @@ -1002,14 +1201,11 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
>>>  	}
>>>  	atmel_lcdfb_start_clock(sinfo);
>>>  
>>> -	ret = fb_find_mode(&info->var, info, NULL, info->monspecs.modedb,
>>> -			info->monspecs.modedb_len, info->monspecs.modedb,
>>> -			pdata->default_bpp);
>>> -	if (!ret) {
>>> -		dev_err(dev, "no suitable video mode found\n");
>>> -		goto stop_clk;
>>> -	}
>>> +	modelist = list_first_entry(&info->modelist,
>>> +			struct fb_modelist, list);
>>> +	fb_videomode_to_var(&info->var, &modelist->mode);
>>>  
>>> +	atmel_lcdfb_check_var(&info->var, info);
>>>  
>>>  	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>  	if (!regs) {
>>> @@ -1093,18 +1289,6 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
>>>  		goto unregister_irqs;
>>>  	}
>>>  
>>> -	/*
>>> -	 * This makes sure that our colour bitfield
>>> -	 * descriptors are correctly initialised.
>>> -	 */
>>> -	atmel_lcdfb_check_var(&info->var, info);
>>> -
>>> -	ret = fb_set_var(info, &info->var);
>>> -	if (ret) {
>>> -		dev_warn(dev, "unable to set display parameters\n");
>>> -		goto free_cmap;
>>> -	}
>>> -
>>>  	dev_set_drvdata(dev, info);
>>>  
>>>  	/*
>>> @@ -1116,10 +1300,6 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
>>>  		goto reset_drvdata;
>>>  	}
>>>  
>>> -	/* add selected videomode to modelist */
>>> -	fb_var_to_videomode(&fbmode, &info->var);
>>> -	fb_add_videomode(&fbmode, &info->modelist);
>>> -
>>>  	/* Power up the LCDC screen */
>>>  	atmel_lcdfb_power_control(sinfo, 1);
>>>  
>>> @@ -1130,7 +1310,6 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
>>>  
>>>  reset_drvdata:
>>>  	dev_set_drvdata(dev, NULL);
>>> -free_cmap:
>>>  	fb_dealloc_cmap(&info->cmap);
>>>  unregister_irqs:
>>>  	cancel_work_sync(&sinfo->task);
>>> @@ -1249,6 +1428,7 @@ static struct platform_driver atmel_lcdfb_driver = {
>>>  	.driver		= {
>>>  		.name	= "atmel_lcdfb",
>>>  		.owner	= THIS_MODULE,
>>> +		.of_match_table	= of_match_ptr(atmel_lcdfb_dt_ids),
>>>  	},
>>>  };
>>>  
>>>
>>
>>
>> -- 
>> Nicolas Ferre
> 
> 


-- 
Nicolas Ferre

^ permalink raw reply

* Re: [PATCH v3 2/2] video: imxfb: Add DT support
From: Markus Pargmann @ 2013-04-16 14:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130415132322.GC15139@game.jcrosoft.org>

On Mon, Apr 15, 2013 at 03:23:22PM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote:
> On 15:13 Mon 15 Apr     , Eric Bénard wrote:
> > Hi,
> > 
> > Le Mon, 15 Apr 2013 14:43:21 +0200,
> > Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> a écrit :
> > 
> > > On 16:31 Sun 14 Apr     , Markus Pargmann wrote:
> > > > On Mon, Apr 08, 2013 at 09:57:42AM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote:
> > > > > On 12:31 Fri 05 Apr     , Markus Pargmann wrote:
> > > > > > Add devicetree support for imx framebuffer driver. It uses the generic
> > > > > > display bindings and helper functions.
> > > > > > 
> > > > > > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > > > > > Cc: Fabio Estevam <festevam@gmail.com>
> > > > > > Cc: Mark Rutland <mark.rutland@arm.com>
> > > > > > ---
> > > > > >  .../devicetree/bindings/video/fsl,imx-fb.txt       |  49 ++++++
> > > > > >  drivers/video/imxfb.c                              | 192 +++++++++++++++++----
> > > > > >  2 files changed, 207 insertions(+), 34 deletions(-)
> > > > > >  create mode 100644 Documentation/devicetree/bindings/video/fsl,imx-fb.txt
> > > > > > 
> > > > > > diff --git a/Documentation/devicetree/bindings/video/fsl,imx-fb.txt b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt
> > > > > > new file mode 100644
> > > > > > index 0000000..bde9c77
> > > > > > --- /dev/null
> > > > > > +++ b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt
> > > > > > @@ -0,0 +1,49 @@
> > > > > > +Freescale imx21 Framebuffer
> > > > > > +
> > > > > > +This framebuffer driver supports devices imx1, imx21, imx25, and imx27.
> > > > > > +
> > > > > > +Required properties:
> > > > > > +- compatible : "fsl,<chip>-fb", chip should be imx1 or imx21
> > > > > > +- reg : Should contain 1 register ranges(address and length)
> > > > > > +- interrupts : One interrupt of the fb dev
> > > > > > +
> > > > > > +Required nodes:
> > > > > > +- display: Phandle to a display node as described in
> > > > > > +	Documentation/devicetree/bindings/video/display-timing.txt
> > > > > > +	Additional, the display node has to define properties:
> > > > > > +	- fsl,bpp: Bits per pixel
> > > > > > +	- fsl,pcr: LCDC PCR value
> > > > > > +
> > > > > > +Optional properties:
> > > > > > +- dmacr-eukrea: Should be set for eukrea boards.
> > > > > why ?
> > > > 
> > > > Because eukrea boards have a different dmacr then all other boards using
> > > > imxfb. The dmacr address is hardcoded as defaults in the code. I could
> > > > also search for the board name in the DT, but there are no eukrea boards
> > > > with DT at the moment, so I thought a bool property may be better for
> > > > the moment.
> > > so no if an other board come will have an other property no
> > > 
> > > add an optionnal option to pass the dmacr
> > 
> > I didn't follow the thread but dmacr is not specific to our boards :
> > $ grep -rn dmacr arch/arm/mach-imx/
> > arch/arm/mach-imx/mach-mx27_3ds.c:446:	.dmacr		= 0x00020010,
> > arch/arm/mach-imx/mach-mx21ads.c:231:	.dmacr		= 0x00020008,
> > arch/arm/mach-imx/mach-mxt_td60.c:210:	.dmacr		= 0x00020010,
> > arch/arm/mach-imx/eukrea_mbimx27-baseboard.c:192:	.dmacr		= 0x00040060,
> > arch/arm/mach-imx/eukrea_mbimxsd25-baseboard.c:165:	.dmacr		= 0x00040060,
> > arch/arm/mach-imx/mach-mx27ads.c:241:	.dmacr		= 0x00020010,
> > arch/arm/mach-imx/mach-pca100.c:351:	.dmacr		= 0x00020010,
> > arch/arm/mach-imx/pcm970-baseboard.c:185:	.dmacr		= 0x00020010,
> > arch/arm/mach-imx/mach-mx25_3ds.c:173:	.dmacr		= 0x00020010,
> 
> with this it even more clear nack on the dmacr-eukrea property
> 
> use an optional property where we can specify the value to use
> 
> Best Regsrds,
> J.
> 

v4 will contain a optional fsl,dmacr property now. By default the reset
value of dmacr register is not changed.

Regards,

Markus

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* Re: [PATCH 4/8] video: atmel_lcdfb: add device tree suport
From: Jean-Christophe PLAGNIOL-VILLARD @ 2013-04-16 13:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <516D554C.1090909@atmel.com>

On 15:42 Tue 16 Apr     , Nicolas Ferre wrote:
> On 04/11/2013 05:00 PM, Jean-Christophe PLAGNIOL-VILLARD :
> > get display timings from device tree
> > Use videomode helpers to get display timings and configurations from
> > device tree
> 
> 2 sentences? Simply elaborate the 2nd one and it will be good.
> 
> > Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> > Cc: linux-fbdev@vger.kernel.org
> > Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> > Cc: Andrew Morton <akpm@linux-foundation.org>
> > ---
> >  .../devicetree/bindings/video/atmel,lcdc.txt       |   75 ++++++
> >  drivers/video/Kconfig                              |    2 +
> >  drivers/video/atmel_lcdfb.c                        |  244 +++++++++++++++++---
> >  3 files changed, 289 insertions(+), 32 deletions(-)
> >  create mode 100644 Documentation/devicetree/bindings/video/atmel,lcdc.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/video/atmel,lcdc.txt b/Documentation/devicetree/bindings/video/atmel,lcdc.txt
> > new file mode 100644
> > index 0000000..1ec175e
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/video/atmel,lcdc.txt
> 
> Why lcdc? I would have preferred atmel,lcdfb, like the driver's name: it
> is even more self-explanatory...
we do not describe drivers but IP
> 
> > @@ -0,0 +1,75 @@
> > +Atmel LCDC Framebuffer
> > +-----------------------------------------------------
> > +
> > +Required properties:
> > +- compatible :
> > +	"atmel,at91sam9261-lcdc" , 
> > +	"atmel,at91sam9263-lcdc" ,
> > +	"atmel,at91sam9g10-lcdc" ,
> > +	"atmel,at91sam9g45-lcdc" ,
> > +	"atmel,at91sam9g45es-lcdc" ,
> > +	"atmel,at91sam9rl-lcdc" ,
> > +	"atmel,at32ap-lcdc"
> > +- reg : Should contain 1 register ranges(address and length)
> > +- interrupts : framebuffer controller interrupt
> > +- display: a phandle pointing to the display node
> > +
> > +Required nodes:
> > +- display: a display node is required to initialize the lcd panel
> > +	This should be in the board dts.
> > +- default-mode: a videomode within the display with timing parameters
> > +	as specified below.
> > +
> > +Example:
> > +
> > +	fb0: fb@0x00500000 {
> > +		compatible = "atmel,at91sam9g45-lcdc";
> > +		reg = <0x00500000 0x1000>;
> > +		interrupts = <23 3 0>;
> > +		pinctrl-names = "default";
> > +		pinctrl-0 = <&pinctrl_fb>;
> > +		display = <&display0>;
> > +		status = "okay";
> > +		#address-cells = <1>;
> > +		#size-cells = <1>;
> > +
> > +	};
> > +
> > +Atmel LCDC Display
> > +-----------------------------------------------------
> > +Required properties (as per of_videomode_helper):
> 
> Can you please point somewhere to the documentation:
> Documentation/devicetree/bindings/video/display-timing.txt
> 
> > + - atmel,dmacon: dma controler configuration
> 
> Typo: controller.
> 
> > + - atmel,lcdcon2: lcd controler configuration
> 
> Ditto
> 
> > + - atmel,guard-time: lcd guard time (Delay in frame periods)
> 
> periods -> period, no?
no it's periods even in the datasheet
> 
> > + - bits-per-pixel: lcd panel bit-depth.
> > +
> > +Optional properties (as per of_videomode_helper):
> > + - atmel,lcdcon-backlight: enable backlight
> > + - atmel,lcd-wiring-mode: lcd wiring mode "RGB" or "BRG"
> 
> Is it a sting, or a number (as seen below)? If it is a number, please
> tell how to choose the index.
String
> 
> 
> > + - atmel,power-control-gpio: gpio to power on or off the LCD (as many as needed)
> > +
> > +Example:
> > +	display0: display {
> > +		bits-per-pixel = <32>;
> > +		atmel,lcdcon-backlight;
> > +		atmel,dmacon = <0x1>;
> > +		atmel,lcdcon2 = <0x80008002>;
> > +		atmel,guard-time = <9>;
> > +		atmel,lcd-wiring-mode = <1>;
> > +
> > +		display-timings {
> > +			native-mode = <&timing0>;
> > +			timing0: timing0 {
> > +				clock-frequency = <9000000>;
> > +				hactive = <480>;
> > +				vactive = <272>;
> > +				hback-porch = <1>;
> > +				hfront-porch = <1>;
> > +				vback-porch = <40>;
> > +				vfront-porch = <1>;
> > +				hsync-len = <45>;
> > +				vsync-len = <1>;
> > +			};
> > +		};
> > +	};
> > diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> > index 4c1546f..0687482 100644
> > --- a/drivers/video/Kconfig
> > +++ b/drivers/video/Kconfig
> > @@ -1018,6 +1018,8 @@ config FB_ATMEL
> >  	select FB_CFB_FILLRECT
> >  	select FB_CFB_COPYAREA
> >  	select FB_CFB_IMAGEBLIT
> > +	select FB_MODE_HELPERS
> > +	select OF_VIDEOMODE
> >  	help
> >  	  This enables support for the AT91/AT32 LCD Controller.
> >  
> > diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
> > index f67e226..4a31570 100644
> > --- a/drivers/video/atmel_lcdfb.c
> > +++ b/drivers/video/atmel_lcdfb.c
> > @@ -20,7 +20,11 @@
> >  #include <linux/gfp.h>
> >  #include <linux/module.h>
> >  #include <linux/platform_data/atmel.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_gpio.h>
> >  #include <video/of_display_timing.h>
> 
> As said in patch 1/8, this one belongs to this 4/8 patch.
> 
> > +#include <video/videomode.h>
> >  
> >  #include <mach/cpu.h>
> >  #include <asm/gpio.h>
> > @@ -59,6 +63,13 @@ struct atmel_lcdfb_info {
> >  	struct atmel_lcdfb_config *config;
> >  };
> >  
> > +struct atmel_lcdfb_power_ctrl_gpio {
> > +	int gpio;
> > +	int active_low;
> > +
> > +	struct list_head list;
> > +};
> > +
> >  #define lcdc_readl(sinfo, reg)		__raw_readl((sinfo)->mmio+(reg))
> >  #define lcdc_writel(sinfo, reg, val)	__raw_writel((val), (sinfo)->mmio+(reg))
> >  
> > @@ -945,16 +956,187 @@ static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo)
> >  	clk_disable(sinfo->lcdc_clk);
> >  }
> >  
> > +#ifdef CONFIG_OF
> > +static const struct of_device_id atmel_lcdfb_dt_ids[] = {
> > +	{ .compatible = "atmel,at91sam9261-lcdc" , .data = &at91sam9261_config, },
> > +	{ .compatible = "atmel,at91sam9263-lcdc" , .data = &at91sam9263_config, },
> > +	{ .compatible = "atmel,at91sam9g10-lcdc" , .data = &at91sam9g10_config, },
> > +	{ .compatible = "atmel,at91sam9g45-lcdc" , .data = &at91sam9g45_config, },
> > +	{ .compatible = "atmel,at91sam9g45es-lcdc" , .data = &at91sam9g45es_config, },
> > +	{ .compatible = "atmel,at91sam9rl-lcdc" , .data = &at91sam9rl_config, },
> > +	{ .compatible = "atmel,at32ap-lcdc" , .data = &at32ap_config, },
> > +	{ /* sentinel */ }
> > +};
> > +
> > +MODULE_DEVICE_TABLE(of, atmel_lcdfb_dt_ids);
> > +
> > +static const char *atmel_lcdfb_wiring_modes[] = {
> > +	[ATMEL_LCDC_WIRING_BGR]	= "BRG",
> > +	[ATMEL_LCDC_WIRING_RGB]	= "RGB",
> > +};
> > +
> > +const int atmel_lcdfb_get_of_wiring_modes(struct device_node *np)
> > +{
> > +	const char *mode;
> > +	int err, i;
> > +
> > +	err = of_property_read_string(np, "atmel,lcd-wiring-mode", &mode);
> > +	if (err < 0)
> > +		return ATMEL_LCDC_WIRING_BGR;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(atmel_lcdfb_wiring_modes); i++)
> > +		if (!strcasecmp(mode, atmel_lcdfb_wiring_modes[i]))
> > +			return i;
> > +
> > +	return -ENODEV;
> > +}
> > +
> > +static void atmel_lcdfb_power_control_gpio(struct atmel_lcdfb_pdata *pdata, int on)
> > +{
> > +	struct atmel_lcdfb_power_ctrl_gpio *og;
> > +
> > +	list_for_each_entry(og, &pdata->pwr_gpios, list)
> > +		gpio_set_value(og->gpio, on);
> > +}
> > +
> > +static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo)
> > +{
> > +	struct fb_info *info = sinfo->info;
> > +	struct atmel_lcdfb_pdata *pdata = &sinfo->pdata;
> > +	struct fb_var_screeninfo *var = &info->var;
> > +	struct device *dev = &sinfo->pdev->dev;
> > +	struct device_node *np Þv->of_node;
> > +	struct device_node *display_np;
> > +	struct device_node *timings_np;
> > +	struct display_timings *timings;
> > +	enum of_gpio_flags flags;
> > +	struct atmel_lcdfb_power_ctrl_gpio *og;
> > +	bool is_gpio_power = false;
> > +	int ret = -ENOENT;
> > +	int i, gpio;
> > +
> > +	sinfo->config = (struct atmel_lcdfb_config*)
> > +		of_match_device(atmel_lcdfb_dt_ids, dev)->data;
> 
> Please split it in 2 steps, otherwise the day that the drivers doesn't
> find the device in dt_ids table, it hangs here with an Oops.
no as you will never end here in this case
> 
> > +
> > +	display_np = of_parse_phandle(np, "display", 0);
> > +	if (!display_np) {
> > +		dev_err(dev, "failed to find display phandle\n");
> > +		return -ENOENT;
> > +	}
> > +
> > +	ret = of_property_read_u32(display_np, "bits-per-pixel", &var->bits_per_pixel);
> > +	if (ret < 0) {
> > +		dev_err(dev, "failed to get property bits-per-pixel\n");
> > +		goto put_display_node;
> > +	}
> > +
> > +	ret = of_property_read_u32(display_np, "atmel,guard-time", &pdata->guard_time);
> > +	if (ret < 0) {
> > +		dev_err(dev, "failed to get property atmel,guard-time\n");
> > +		goto put_display_node;
> > +	}
> > +
> > +	ret = of_property_read_u32(display_np, "atmel,lcdcon2", &pdata->default_lcdcon2);
> > +	if (ret < 0) {
> > +		dev_err(dev, "failed to get property atmel,lcdcon2\n");
> > +		goto put_display_node;
> > +	}
> > +
> > +	ret = of_property_read_u32(display_np, "atmel,dmacon", &pdata->default_dmacon);
> > +	if (ret < 0) {
> > +		dev_err(dev, "failed to get property bits-per-pixel\n");
> 
> No, wrong error message.
> 
> > +		goto put_display_node;
> > +	}
> > +
> > +	ret = -ENOMEM;
> > +	for (i = 0; i < of_gpio_named_count(display_np, "atmel,power-control-gpio"); i++) {
> > +		gpio = of_get_named_gpio_flags(display_np, "atmel,power-control-gpio",
> > +					       i, &flags);
> > +		if (gpio < 0)
> > +			continue;
> > +
> > +		og = devm_kzalloc(dev, sizeof(*og), GFP_KERNEL);
> > +		if (!og)
> > +			goto put_display_node;
> > +
> > +		og->gpio = gpio;
> > +		og->active_low = flags & OF_GPIO_ACTIVE_LOW;
> > +		is_gpio_power = true;
> > +		ret = devm_gpio_request(dev, gpio, "lcd-power-control-gpio");
> > +		if (ret) {
> > +			dev_err(dev, "request gpio %d failed\n", gpio);
> > +			goto put_display_node;
> > +		}
> > +
> > +		ret = gpio_direction_output(gpio, og->active_low);
> > +		if (ret) {
> > +			dev_err(dev, "set direction output gpio %d failed\n", gpio);
> > +			goto put_display_node;
> > +		}
> > +	}
> > +
> > +	if (is_gpio_power)
> > +		pdata->atmel_lcdfb_power_control = atmel_lcdfb_power_control_gpio;
> > +
> > +	ret = atmel_lcdfb_get_of_wiring_modes(display_np);
> > +	if (ret < 0) {
> > +		dev_err(dev, "invalid atmel,lcd-wiring-mode\n");
> > +		goto put_display_node;
> > +	}
> > +	pdata->lcd_wiring_mode = ret;
> > +
> > +	pdata->lcdcon_is_backlight = of_property_read_bool(display_np, "atmel,lcdcon-backlight");
> > +
> > +	timings = of_get_display_timings(display_np);
> > +	if (!timings) {
> > +		dev_err(dev, "failed to get display timings\n");
> > +		goto put_display_node;
> > +	}
> > +
> > +	timings_np = of_find_node_by_name(display_np, "display-timings");
> > +	if (!timings_np) {
> > +		dev_err(dev, "failed to find display-timings node\n");
> > +		goto put_display_node;
> > +	}
> > +
> > +	for (i = 0; i < of_get_child_count(timings_np); i++) {
> > +		struct videomode vm;
> > +		struct fb_videomode fb_vm;
> > +
> > +		ret = videomode_from_timing(timings, &vm, i);
> > +		if (ret < 0)
> > +			goto put_timings_node;
> > +		ret = fb_videomode_from_videomode(&vm, &fb_vm);
> > +		if (ret < 0)
> > +			goto put_timings_node;
> > +
> > +		fb_add_videomode(&fb_vm, &info->modelist);
> > +	}
> > +
> > +	return 0;
> > +
> > +put_timings_node:
> > +	of_node_put(timings_np);
> > +put_display_node:
> > +	of_node_put(display_np);
> > +	return ret;
> > +}
> > +#else
> > +static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo)
> > +{
> > +	return 0;
> > +}
> > +#endif
> >  
> >  static int __init atmel_lcdfb_probe(struct platform_device *pdev)
> >  {
> >  	struct device *dev = &pdev->dev;
> >  	struct fb_info *info;
> >  	struct atmel_lcdfb_info *sinfo;
> > -	struct atmel_lcdfb_pdata *pdata;
> > -	struct fb_videomode fbmode;
> > +	struct atmel_lcdfb_pdata *pdata = NULL;
> >  	struct resource *regs = NULL;
> >  	struct resource *map = NULL;
> > +	struct fb_modelist *modelist;
> >  	int ret;
> >  
> >  	dev_dbg(dev, "%s BEGIN\n", __func__);
> > @@ -967,17 +1149,35 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
> >  	}
> >  
> >  	sinfo = info->par;
> > +	sinfo->pdev = pdev;
> > +	sinfo->info = info;
> > +
> > +	INIT_LIST_HEAD(&info->modelist);
> >  
> > -	if (dev->platform_data) {
> > -		pdata = (struct atmel_lcdfb_pdata *)dev->platform_data;
> > +	if (pdev->dev.of_node) {
> > +		ret = atmel_lcdfb_of_init(sinfo);
> > +		if (ret)
> > +			goto free_info;
> > +	} else if (dev->platform_data) {
> > +		struct fb_monspecs *monspecs;
> > +		int i;
> > +
> > +		pdata = dev->platform_data;
> > +		monspecs = pdata->default_monspecs;
> >  		sinfo->pdata = *pdata;
> > +
> > +		for (i = 0; i < monspecs->modedb_len; i++)
> > +			fb_add_videomode(&monspecs->modedb[i], &info->modelist);
> > +
> > +		sinfo->config = atmel_lcdfb_get_config(pdev);
> > +
> > +		info->var.bits_per_pixel = pdata->default_bpp ? pdata->default_bpp : 16;
> > +		memcpy(&info->monspecs, pdata->default_monspecs, sizeof(info->monspecs));
> >  	} else {
> >  		dev_err(dev, "cannot get default configuration\n");
> >  		goto free_info;
> >  	}
> > -	sinfo->info = info;
> > -	sinfo->pdev = pdev;
> > -	sinfo->config = atmel_lcdfb_get_config(pdev);
> > +
> >  	if (!sinfo->config)
> >  		goto free_info;
> >  
> > @@ -986,7 +1186,6 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
> >  	info->pseudo_palette = sinfo->pseudo_palette;
> >  	info->fbops = &atmel_lcdfb_ops;
> >  
> > -	memcpy(&info->monspecs, pdata->default_monspecs, sizeof(info->monspecs));
> >  	info->fix = atmel_lcdfb_fix;
> >  
> >  	/* Enable LCDC Clocks */
> > @@ -1002,14 +1201,11 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
> >  	}
> >  	atmel_lcdfb_start_clock(sinfo);
> >  
> > -	ret = fb_find_mode(&info->var, info, NULL, info->monspecs.modedb,
> > -			info->monspecs.modedb_len, info->monspecs.modedb,
> > -			pdata->default_bpp);
> > -	if (!ret) {
> > -		dev_err(dev, "no suitable video mode found\n");
> > -		goto stop_clk;
> > -	}
> > +	modelist = list_first_entry(&info->modelist,
> > +			struct fb_modelist, list);
> > +	fb_videomode_to_var(&info->var, &modelist->mode);
> >  
> > +	atmel_lcdfb_check_var(&info->var, info);
> >  
> >  	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >  	if (!regs) {
> > @@ -1093,18 +1289,6 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
> >  		goto unregister_irqs;
> >  	}
> >  
> > -	/*
> > -	 * This makes sure that our colour bitfield
> > -	 * descriptors are correctly initialised.
> > -	 */
> > -	atmel_lcdfb_check_var(&info->var, info);
> > -
> > -	ret = fb_set_var(info, &info->var);
> > -	if (ret) {
> > -		dev_warn(dev, "unable to set display parameters\n");
> > -		goto free_cmap;
> > -	}
> > -
> >  	dev_set_drvdata(dev, info);
> >  
> >  	/*
> > @@ -1116,10 +1300,6 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
> >  		goto reset_drvdata;
> >  	}
> >  
> > -	/* add selected videomode to modelist */
> > -	fb_var_to_videomode(&fbmode, &info->var);
> > -	fb_add_videomode(&fbmode, &info->modelist);
> > -
> >  	/* Power up the LCDC screen */
> >  	atmel_lcdfb_power_control(sinfo, 1);
> >  
> > @@ -1130,7 +1310,6 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
> >  
> >  reset_drvdata:
> >  	dev_set_drvdata(dev, NULL);
> > -free_cmap:
> >  	fb_dealloc_cmap(&info->cmap);
> >  unregister_irqs:
> >  	cancel_work_sync(&sinfo->task);
> > @@ -1249,6 +1428,7 @@ static struct platform_driver atmel_lcdfb_driver = {
> >  	.driver		= {
> >  		.name	= "atmel_lcdfb",
> >  		.owner	= THIS_MODULE,
> > +		.of_match_table	= of_match_ptr(atmel_lcdfb_dt_ids),
> >  	},
> >  };
> >  
> > 
> 
> 
> -- 
> Nicolas Ferre

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox