Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* [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

* 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

* 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 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

* [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

* [PATCH] uvesafb: Correct/simplify warning message
From: Borislav Petkov @ 2013-04-19 14:05 UTC (permalink / raw)
  To: linux-fbdev

From: Borislav Petkov <bp@suse.de>

Streamline it a bit. No functional change.

Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: Wang YanQing <udknight@gmail.com>
Cc: Michal Januszewski <spock@gentoo.org>
Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Cc: linux-fbdev@vger.kernel.org
---
 drivers/video/uvesafb.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c
index d4284458377e..a309f9fabe8b 100644
--- a/drivers/video/uvesafb.c
+++ b/drivers/video/uvesafb.c
@@ -819,8 +819,8 @@ static int uvesafb_vbe_init(struct fb_info *info)
 	if (par->pmi_setpal || par->ypan) {
 		if (__supported_pte_mask & _PAGE_NX) {
 			par->pmi_setpal = par->ypan = 0;
-			printk(KERN_WARNING "uvesafb: NX protection is actively."
-				"We have better not to use the PMI.\n");
+			printk(KERN_WARNING "uvesafb: NX protection is active, "
+					    "better not use the PMI.\n");
 		} else {
 			uvesafb_vbe_getpmi(task, par);
 		}
-- 
1.8.2.135.g7b592fa


^ permalink raw reply related

* Darlehen anbieten
From: jordan.buechler @ 2013-04-19 21:59 UTC (permalink / raw)
  To: linux-fbdev

Sehr geehrter Kunde,

Sie benötigen Darlehen zu zahlen Sie Ihre Schulden oder begleichen Sie Ihre Rechnung? Sind Sie in irgendeiner finanziellen Schlamassel oder benötigen Sie Fonds zu starten Ihr eigenes Geschäft? NORTON FINANCE 
ist bereit, Ihnen Kredit zu 3% Zins. wenn interessiert füllen das Anmeldeformular unten für die dringende Bearbeitung Ihres Darlehens. Kontakt: nortonfinan@blumail.org


Vorname: ........................
Adresse: ...........................
Land: ...........................
Telefonnummer: ......................
Beruf: .............. .........
Monatliches Einkommen: ...................
Zweck des Darlehens: ...................
Benötigte Menge ....................
Dauer der Ausleihe: .....................
Alter: ...............................

Mit freundlichen Grüßen,
Sarah Bell-
Loan Officer
E-Mail: nortonfinan@blumail.org
Tells: Tell: +447035958575
Fax: +448447742385

^ permalink raw reply

* Re: [PATCH v4 2/2] video: imxfb: Add DT support
From: Markus Pargmann @ 2013-04-21  8:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130418160614.GF4998@game.jcrosoft.org>

On Thu, Apr 18, 2013 at 06:06:14PM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote:
> 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

Thanks, fixed.

> > +		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

I can't find a generic binding for that. There are only 2 other drivers
using a "bpp" property, but they both use it within different contexts.
tilcdc panel uses it within a "panel-info" property and via,vt8500-fb
within modes. So I think it would be better to use a clear distinction
because there is no generic binding. That was also suggested in comments
on version 2 of this patch:
https://patchwork.kernel.org/patch/2220511/

Regards,

Markus

> > +		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
> > 
> 

-- 
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

* [PATCH] MAINTAINERS: Add linux-samsung-soc list to all related entries
From: Tomasz Figa @ 2013-04-21 19:20 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-samsung-soc, linux-media, dri-devel, linux-fbdev,
	alsa-devel, linux-mmc, kyungmin.park, k.debski, s.nawrocki,
	jtp.park, t.stanislaws, inki.dae, jy0922.shim, sw0312.kim,
	jg1.han, dh09.lee, sbkim73, linux, Tomasz Figa

Several entries in MAINTAINERS file related to Samsung SoCs do not point
to linux-samsung-soc mailing list, which is supposed to collect all
Samsung SoC-related threads, to ease following of Samsung SoC-related
work. This leads to a problem with people skipping this mailing list in
their posts, even though they are related to Samsung SoCs.

This patch adds pointers to linux-samsung-soc mailing list to affected
entries of MAINTAINERS file.

Signed-off-by: Tomasz Figa <tomasz.figa@gmail.com>
---
 MAINTAINERS | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 6c0d68b..07cb8da 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1139,6 +1139,7 @@ F:	arch/arm/mach-exynos*/
 ARM/SAMSUNG MOBILE MACHINE SUPPORT
 M:	Kyungmin Park <kyungmin.park@samsung.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L:	linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
 S:	Maintained
 F:	arch/arm/mach-s5pv210/mach-aquila.c
 F:	arch/arm/mach-s5pv210/mach-goni.c
@@ -1150,6 +1151,7 @@ M:	Kyungmin Park <kyungmin.park@samsung.com>
 M:	Kamil Debski <k.debski@samsung.com>
 L:	linux-arm-kernel@lists.infradead.org
 L:	linux-media@vger.kernel.org
+L:	linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
 S:	Maintained
 F:	drivers/media/platform/s5p-g2d/
 
@@ -1158,6 +1160,7 @@ M:	Kyungmin Park <kyungmin.park@samsung.com>
 M:	Sylwester Nawrocki <s.nawrocki@samsung.com>
 L:	linux-arm-kernel@lists.infradead.org
 L:	linux-media@vger.kernel.org
+L:	linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
 S:	Maintained
 F:	arch/arm/plat-samsung/include/plat/*fimc*
 F:	drivers/media/platform/s5p-fimc/
@@ -1168,6 +1171,7 @@ M:	Kamil Debski <k.debski@samsung.com>
 M:	Jeongtae Park <jtp.park@samsung.com>
 L:	linux-arm-kernel@lists.infradead.org
 L:	linux-media@vger.kernel.org
+L:	linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
 S:	Maintained
 F:	arch/arm/plat-samsung/s5p-dev-mfc.c
 F:	drivers/media/platform/s5p-mfc/
@@ -1177,6 +1181,7 @@ M:	Kyungmin Park <kyungmin.park@samsung.com>
 M:	Tomasz Stanislawski <t.stanislaws@samsung.com>
 L:	linux-arm-kernel@lists.infradead.org
 L:	linux-media@vger.kernel.org
+L:	linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
 S:	Maintained
 F:	drivers/media/platform/s5p-tv/
 
@@ -2679,6 +2684,7 @@ M:	Joonyoung Shim <jy0922.shim@samsung.com>
 M:	Seung-Woo Kim <sw0312.kim@samsung.com>
 M:	Kyungmin Park <kyungmin.park@samsung.com>
 L:	dri-devel@lists.freedesktop.org
+L:	linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
 S:	Supported
 F:	drivers/gpu/drm/exynos
@@ -3142,6 +3148,7 @@ F:	Documentation/extcon/
 EXYNOS DP DRIVER
 M:	Jingoo Han <jg1.han@samsung.com>
 L:	linux-fbdev@vger.kernel.org
+L:	linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
 S:	Maintained
 F:	drivers/video/exynos/exynos_dp*
 F:	include/video/exynos_dp*
@@ -3151,6 +3158,7 @@ M:	Inki Dae <inki.dae@samsung.com>
 M:	Donghwa Lee <dh09.lee@samsung.com>
 M:	Kyungmin Park <kyungmin.park@samsung.com>
 L:	linux-fbdev@vger.kernel.org
+L:	linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
 S:	Maintained
 F:	drivers/video/exynos/exynos_mipi*
 F:	include/video/exynos_mipi*
@@ -6892,12 +6900,14 @@ F:	drivers/platform/x86/samsung-laptop.c
 SAMSUNG AUDIO (ASoC) DRIVERS
 M:	Sangbeom Kim <sbkim73@samsung.com>
 L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
+L:	linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
 S:	Supported
 F:	sound/soc/samsung
 
 SAMSUNG FRAMEBUFFER DRIVER
 M:	Jingoo Han <jg1.han@samsung.com>
 L:	linux-fbdev@vger.kernel.org
+L:	linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
 S:	Maintained
 F:	drivers/video/s3c-fb.c
 
@@ -7087,6 +7097,7 @@ F:	drivers/mmc/host/sdhci-pltfm.[ch]
 SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) SAMSUNG DRIVER
 M:	Ben Dooks <ben-linux@fluff.org>
 L:	linux-mmc@vger.kernel.org
+L:	linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
 S:	Maintained
 F:	drivers/mmc/host/sdhci-s3c.c
 
-- 
1.8.2.1


^ permalink raw reply related

* Re: [PATCH] uvesafb: Correct/simplify warning message
From: Wang YanQing @ 2013-04-22  1:19 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1366380325-2038-1-git-send-email-bp@alien8.de>

On Fri, Apr 19, 2013 at 04:05:25PM +0200, Borislav Petkov wrote:
> From: Borislav Petkov <bp@suse.de>
> 
> Streamline it a bit. No functional change.
> 
> Signed-off-by: Borislav Petkov <bp@suse.de>
> Cc: Wang YanQing <udknight@gmail.com>
> Cc: Michal Januszewski <spock@gentoo.org>
> Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
> Cc: linux-fbdev@vger.kernel.org
> ---
>  drivers/video/uvesafb.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c
> index d4284458377e..a309f9fabe8b 100644
> --- a/drivers/video/uvesafb.c
> +++ b/drivers/video/uvesafb.c
> @@ -819,8 +819,8 @@ static int uvesafb_vbe_init(struct fb_info *info)
>  	if (par->pmi_setpal || par->ypan) {
>  		if (__supported_pte_mask & _PAGE_NX) {
>  			par->pmi_setpal = par->ypan = 0;
> -			printk(KERN_WARNING "uvesafb: NX protection is actively."
> -				"We have better not to use the PMI.\n");
> +			printk(KERN_WARNING "uvesafb: NX protection is active, "
> +					    "better not use the PMI.\n");
>  		} else {
>  			uvesafb_vbe_getpmi(task, par);
>  		}
> -- 
> 1.8.2.135.g7b592fa

Acked-by: Wang YanQing <udknight@gmail.com>

Thanks.

^ permalink raw reply

* Re: [PATCH] video: mxsfb: Fix colors display on lower color depth
From: Maxime Ripard @ 2013-04-22  8:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130418144838.GD2222@S2101-09.ap.freescale.net>

Hi Shawn,

Le 18/04/2013 16:48, Shawn Guo a écrit :
> Copy Sascha.

Ah, yes, sorry, I somehow got confused and thought you were the one that
wrote the driver.

I'll resend the patch.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

^ permalink raw reply

* [PATCH RESEND] video: mxsfb: Fix colors display on lower color depth
From: Maxime Ripard @ 2013-04-22  8:11 UTC (permalink / raw)
  To: linux-arm-kernel

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>
---
  - Copied Sascha

 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 RESEND] video: mxsfb: Fix colors display on lower color depth
From: Juergen Beisert @ 2013-04-22  9:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366618310-7068-1-git-send-email-maxime.ripard@free-electrons.com>

Hi Maxime,

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.

But the "def_rgb666" bitfield description reports correctly to use bit 0..5 
for each colour component in this mode. Maybe your userland program has a bug 
and forgets to honor the bitfield description?

> 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.

If you do this change, you also must adapt the "def_rgb666" bitfield 
description.
This structure currently reports the lower 6 bits to be used for this mode:
  blue -> 0..5 (skip 6/7)
  green -> 8..13 (skip 14/15)
  red -> 16..21 (skip 22/23).

> [...]

jbe

-- 
Pengutronix e.K.                              | Juergen Beisert             |
Linux Solutions for Science and Industry      | http://www.pengutronix.de/  |

^ permalink raw reply

* Re: [PATCH RESEND] video: mxsfb: Fix colors display on lower color depth
From: Marek Vasut @ 2013-04-22  9:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366618310-7068-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>

Did you receive my latest email? Check M28EVK (imx28-m28evk.dts), it uses 18bit 
LCD and works without this patch I think.

Best regards,
Marek Vasut

^ permalink raw reply

* [PATCHv2 0/2] Add support for the Solomon SSD1306 OLED Controller
From: Maxime Ripard @ 2013-04-22  9:55 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

This patchset adds the needed bits to the ssd1307fb driver to support the
very similar Solomon SSD1306 controller.

Since the SSD1307 has been replaced by the SSD1306 in the latest CFA-10036
production samples, we also need to change the device tree accordingly.

Thanks,
Maxime

Changes from v1:
  - Fix style errors

Maxime Ripard (2):
  video: ssd1307fb: Add support for SSD1306 OLED controller
  ARM: dts: cfa10036: Change the OLED display to SSD1306

 .../devicetree/bindings/video/ssd1307fb.txt        |   10 +-
 arch/arm/boot/dts/imx28-cfa10036.dts               |   14 +-
 arch/arm/boot/dts/imx28-cfa10049.dts               |    4 +-
 drivers/video/ssd1307fb.c                          |  273 +++++++++++++++-----
 4 files changed, 216 insertions(+), 85 deletions(-)

-- 
1.7.10.4


^ permalink raw reply

* [PATCH 1/2] video: ssd1307fb: Add support for SSD1306 OLED controller
From: Maxime Ripard @ 2013-04-22  9:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366624555-5616-1-git-send-email-maxime.ripard@free-electrons.com>

The Solomon SSD1306 OLED controller is very similar to the SSD1307,
except for the fact that the power is given through an external PWM for
the 1307, and while the 1306 can generate its own power without any PWM.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 .../devicetree/bindings/video/ssd1307fb.txt        |   10 +-
 drivers/video/ssd1307fb.c                          |  273 +++++++++++++++-----
 2 files changed, 209 insertions(+), 74 deletions(-)

diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt
index 3d0060c..7a12542 100644
--- a/Documentation/devicetree/bindings/video/ssd1307fb.txt
+++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt
@@ -1,13 +1,17 @@
 * Solomon SSD1307 Framebuffer Driver
 
 Required properties:
-  - compatible: Should be "solomon,ssd1307fb-<bus>". The only supported bus for
-    now is i2c.
+  - compatible: Should be "solomon,<chip>fb-<bus>". The only supported bus for
+    now is i2c, and the supported chips are ssd1306 and ssd1307.
   - reg: Should contain address of the controller on the I2C bus. Most likely
          0x3c or 0x3d
   - pwm: Should contain the pwm to use according to the OF device tree PWM
-         specification [0]
+         specification [0]. Only required for the ssd1307.
   - reset-gpios: Should contain the GPIO used to reset the OLED display
+  - solomon,height: Height in pixel of the screen driven by the controller
+  - solomon,width: Width in pixel of the screen driven by the controller
+  - solomon,page-offset: Offset of pages (band of 8 pixels) that the screen is
+    mapped to.
 
 Optional properties:
   - reset-active-low: Is the reset gpio is active on physical low?
diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
index 395cb6a..5ab5281 100644
--- a/drivers/video/ssd1307fb.c
+++ b/drivers/video/ssd1307fb.c
@@ -16,24 +16,39 @@
 #include <linux/pwm.h>
 #include <linux/delay.h>
 
-#define SSD1307FB_WIDTH			96
-#define SSD1307FB_HEIGHT		16
-
 #define SSD1307FB_DATA			0x40
 #define SSD1307FB_COMMAND		0x80
 
 #define SSD1307FB_CONTRAST		0x81
+#define	SSD1307FB_CHARGE_PUMP		0x8d
 #define SSD1307FB_SEG_REMAP_ON		0xa1
 #define SSD1307FB_DISPLAY_OFF		0xae
+#define SSD1307FB_SET_MULTIPLEX_RATIO	0xa8
 #define SSD1307FB_DISPLAY_ON		0xaf
 #define SSD1307FB_START_PAGE_ADDRESS	0xb0
+#define SSD1307FB_SET_DISPLAY_OFFSET	0xd3
+#define	SSD1307FB_SET_CLOCK_FREQ	0xd5
+#define	SSD1307FB_SET_PRECHARGE_PERIOD	0xd9
+#define	SSD1307FB_SET_COM_PINS_CONFIG	0xda
+#define	SSD1307FB_SET_VCOMH		0xdb
+
+struct ssd1307fb_par;
+
+struct ssd1307fb_ops {
+	int (*init)(struct ssd1307fb_par *);
+	int (*remove)(struct ssd1307fb_par *);
+};
 
 struct ssd1307fb_par {
 	struct i2c_client *client;
+	u32 height;
 	struct fb_info *info;
+	struct ssd1307fb_ops *ops;
+	u32 page_offset;
 	struct pwm_device *pwm;
 	u32 pwm_period;
 	int reset;
+	u32 width;
 };
 
 static struct fb_fix_screeninfo ssd1307fb_fix = {
@@ -43,15 +58,10 @@ static struct fb_fix_screeninfo ssd1307fb_fix = {
 	.xpanstep	= 0,
 	.ypanstep	= 0,
 	.ywrapstep	= 0,
-	.line_length	= SSD1307FB_WIDTH / 8,
 	.accel		= FB_ACCEL_NONE,
 };
 
 static struct fb_var_screeninfo ssd1307fb_var = {
-	.xres		= SSD1307FB_WIDTH,
-	.yres		= SSD1307FB_HEIGHT,
-	.xres_virtual	= SSD1307FB_WIDTH,
-	.yres_virtual	= SSD1307FB_HEIGHT,
 	.bits_per_pixel	= 1,
 };
 
@@ -134,16 +144,17 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par)
 	 *  (5) A4 B4 C4 D4 E4 F4 G4 H4
 	 */
 
-	for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) {
-		ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1));
+	for (i = 0; i < (par->height / 8); i++) {
+		ssd1307fb_write_cmd(par->client,
+				    SSD1307FB_START_PAGE_ADDRESS + i + par->page_offset);
 		ssd1307fb_write_cmd(par->client, 0x00);
 		ssd1307fb_write_cmd(par->client, 0x10);
 
-		for (j = 0; j < SSD1307FB_WIDTH; j++) {
+		for (j = 0; j < par->width; j++) {
 			u8 buf = 0;
 			for (k = 0; k < 8; k++) {
-				u32 page_length = SSD1307FB_WIDTH * i;
-				u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8;
+				u32 page_length = par->width * i;
+				u32 index = page_length + (par->width * k + j) / 8;
 				u8 byte = *(vmem + index);
 				u8 bit = byte & (1 << (j % 8));
 				bit = bit >> (j % 8);
@@ -227,16 +238,147 @@ static struct fb_deferred_io ssd1307fb_defio = {
 	.deferred_io	= ssd1307fb_deferred_io,
 };
 
+static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par)
+{
+	int ret;
+
+	par->pwm = pwm_get(&par->client->dev, NULL);
+	if (IS_ERR(par->pwm)) {
+		dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
+		return PTR_ERR(par->pwm);
+	}
+
+	par->pwm_period = pwm_get_period(par->pwm);
+	/* Enable the PWM */
+	pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
+	pwm_enable(par->pwm);
+
+	dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
+		par->pwm->pwm, par->pwm_period);
+
+	/* Map column 127 of the OLED to segment 0 */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
+	if (ret < 0)
+		return ret;
+
+	/* Turn on the display */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par)
+{
+	pwm_disable(par->pwm);
+	pwm_put(par->pwm);
+	return 0;
+}
+
+static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = {
+	.init	= ssd1307fb_ssd1307_init,
+	.remove	= ssd1307fb_ssd1307_remove,
+};
+
+static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
+{
+	int ret;
+
+	/* Set initial contrast */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x7f);
+	if (ret < 0)
+		return ret;
+
+	/* Set COM direction */
+	ret = ssd1307fb_write_cmd(par->client, 0xc8);
+	if (ret < 0)
+		return ret;
+
+	/* Set segment re-map */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
+	if (ret < 0)
+		return ret;
+
+	/* Set multiplex ratio value */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO);
+	ret = ret & ssd1307fb_write_cmd(par->client, par->height - 1);
+	if (ret < 0)
+		return ret;
+
+	/* set display offset value */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET);
+	ret = ssd1307fb_write_cmd(par->client, 0x20);
+	if (ret < 0)
+		return ret;
+
+	/* Set clock frequency */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0xf0);
+	if (ret < 0)
+		return ret;
+
+	/* Set precharge period in number of ticks from the internal clock */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
+	if (ret < 0)
+		return ret;
+
+	/* Set COM pins configuration */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
+	if (ret < 0)
+		return ret;
+
+	/* Set VCOMH */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x49);
+	if (ret < 0)
+		return ret;
+
+	/* Turn on the DC-DC Charge Pump */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x14);
+	if (ret < 0)
+		return ret;
+
+	/* Turn on the display */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = {
+	.init	= ssd1307fb_ssd1306_init,
+};
+
+static const struct of_device_id ssd1307fb_of_match[] = {
+	{
+		.compatible = "solomon,ssd1306fb-i2c",
+		.data = (void *)&ssd1307fb_ssd1306_ops,
+	},
+	{
+		.compatible = "solomon,ssd1307fb-i2c",
+		.data = (void *)&ssd1307fb_ssd1307_ops,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
+
 static int ssd1307fb_probe(struct i2c_client *client,
 			   const struct i2c_device_id *id)
 {
 	struct fb_info *info;
-	u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8;
+	struct device_node *node = client->dev.of_node;
+	u32 vmem_size;
 	struct ssd1307fb_par *par;
 	u8 *vmem;
 	int ret;
 
-	if (!client->dev.of_node) {
+	if (!node) {
 		dev_err(&client->dev, "No device tree data found!\n");
 		return -EINVAL;
 	}
@@ -247,6 +389,31 @@ static int ssd1307fb_probe(struct i2c_client *client,
 		return -ENOMEM;
 	}
 
+	par = info->par;
+	par->info = info;
+	par->client = client;
+
+	par->ops = (struct ssd1307fb_ops *)of_match_device(ssd1307fb_of_match,
+							   &client->dev)->data;
+
+	par->reset = of_get_named_gpio(client->dev.of_node,
+					 "reset-gpios", 0);
+	if (!gpio_is_valid(par->reset)) {
+		ret = -EINVAL;
+		goto fb_alloc_error;
+	}
+
+	if (of_property_read_u32(node, "solomon,width", &par->width))
+		par->width = 96;
+
+	if (of_property_read_u32(node, "solomon,height", &par->height))
+		par->width = 16;
+
+	if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset))
+		par->page_offset = 1;
+
+	vmem_size = par->width * par->height / 8;
+
 	vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
 	if (!vmem) {
 		dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
@@ -256,9 +423,15 @@ static int ssd1307fb_probe(struct i2c_client *client,
 
 	info->fbops = &ssd1307fb_ops;
 	info->fix = ssd1307fb_fix;
+	info->fix.line_length = par->width / 8;
 	info->fbdefio = &ssd1307fb_defio;
 
 	info->var = ssd1307fb_var;
+	info->var.xres = par->width;
+	info->var.xres_virtual = par->width;
+	info->var.yres = par->height;
+	info->var.yres_virtual = par->height;
+
 	info->var.red.length = 1;
 	info->var.red.offset = 0;
 	info->var.green.length = 1;
@@ -272,17 +445,6 @@ static int ssd1307fb_probe(struct i2c_client *client,
 
 	fb_deferred_io_init(info);
 
-	par = info->par;
-	par->info = info;
-	par->client = client;
-
-	par->reset = of_get_named_gpio(client->dev.of_node,
-					 "reset-gpios", 0);
-	if (!gpio_is_valid(par->reset)) {
-		ret = -EINVAL;
-		goto reset_oled_error;
-	}
-
 	ret = devm_gpio_request_one(&client->dev, par->reset,
 				    GPIOF_OUT_INIT_HIGH,
 				    "oled-reset");
@@ -293,23 +455,6 @@ static int ssd1307fb_probe(struct i2c_client *client,
 		goto reset_oled_error;
 	}
 
-	par->pwm = pwm_get(&client->dev, NULL);
-	if (IS_ERR(par->pwm)) {
-		dev_err(&client->dev, "Could not get PWM from device tree!\n");
-		ret = PTR_ERR(par->pwm);
-		goto pwm_error;
-	}
-
-	par->pwm_period = pwm_get_period(par->pwm);
-
-	dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period);
-
-	ret = register_framebuffer(info);
-	if (ret) {
-		dev_err(&client->dev, "Couldn't register the framebuffer\n");
-		goto fbreg_error;
-	}
-
 	i2c_set_clientdata(client, info);
 
 	/* Reset the screen */
@@ -318,34 +463,25 @@ static int ssd1307fb_probe(struct i2c_client *client,
 	gpio_set_value(par->reset, 1);
 	udelay(4);
 
-	/* Enable the PWM */
-	pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
-	pwm_enable(par->pwm);
-
-	/* Map column 127 of the OLED to segment 0 */
-	ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON);
-	if (ret < 0) {
-		dev_err(&client->dev, "Couldn't remap the screen.\n");
-		goto remap_error;
+	if (par->ops->init) {
+		ret = par->ops->init(par);
+		if (ret)
+			goto reset_oled_error;
 	}
 
-	/* Turn on the display */
-	ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON);
-	if (ret < 0) {
-		dev_err(&client->dev, "Couldn't turn the display on.\n");
-		goto remap_error;
+	ret = register_framebuffer(info);
+	if (ret) {
+		dev_err(&client->dev, "Couldn't register the framebuffer\n");
+		goto panel_init_error;
 	}
 
 	dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
 
 	return 0;
 
-remap_error:
-	unregister_framebuffer(info);
-	pwm_disable(par->pwm);
-fbreg_error:
-	pwm_put(par->pwm);
-pwm_error:
+panel_init_error:
+	if (par->ops->remove)
+		par->ops->remove(par);
 reset_oled_error:
 	fb_deferred_io_cleanup(info);
 fb_alloc_error:
@@ -359,8 +495,8 @@ static int ssd1307fb_remove(struct i2c_client *client)
 	struct ssd1307fb_par *par = info->par;
 
 	unregister_framebuffer(info);
-	pwm_disable(par->pwm);
-	pwm_put(par->pwm);
+	if (par->ops->remove)
+		par->ops->remove(par);
 	fb_deferred_io_cleanup(info);
 	framebuffer_release(info);
 
@@ -368,17 +504,12 @@ static int ssd1307fb_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id ssd1307fb_i2c_id[] = {
+	{ "ssd1306fb", 0 },
 	{ "ssd1307fb", 0 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
 
-static const struct of_device_id ssd1307fb_of_match[] = {
-	{ .compatible = "solomon,ssd1307fb-i2c" },
-	{},
-};
-MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
-
 static struct i2c_driver ssd1307fb_driver = {
 	.probe = ssd1307fb_probe,
 	.remove = ssd1307fb_remove,
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 2/2] ARM: dts: cfa10036: Change the OLED display to SSD1306
From: Maxime Ripard @ 2013-04-22  9:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366624555-5616-1-git-send-email-maxime.ripard@free-electrons.com>

The SSD1307 was used in an early prototype that will never get
distributed. The final board now has a SSD1306 instead, that has its own
power generation unit and thus doesn't need any PWM. The panel attached
to it also changed.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/boot/dts/imx28-cfa10036.dts |   14 +++++---------
 arch/arm/boot/dts/imx28-cfa10049.dts |    4 ++--
 2 files changed, 7 insertions(+), 11 deletions(-)

diff --git a/arch/arm/boot/dts/imx28-cfa10036.dts b/arch/arm/boot/dts/imx28-cfa10036.dts
index 1594694..40488f5a8 100644
--- a/arch/arm/boot/dts/imx28-cfa10036.dts
+++ b/arch/arm/boot/dts/imx28-cfa10036.dts
@@ -58,12 +58,6 @@
 		};
 
 		apbx@80040000 {
-			pwm: pwm@80064000 {
-				pinctrl-names = "default";
-				pinctrl-0 = <&pwm4_pins_a>;
-				status = "okay";
-			};
-
 			duart: serial@80074000 {
 				pinctrl-names = "default";
 				pinctrl-0 = <&duart_pins_b>;
@@ -75,11 +69,13 @@
 				pinctrl-0 = <&i2c0_pins_b>;
 				status = "okay";
 
-				ssd1307: oled@3c {
-					compatible = "solomon,ssd1307fb-i2c";
+				ssd1306: oled@3c {
+					compatible = "solomon,ssd1306fb-i2c";
 					reg = <0x3c>;
-					pwms = <&pwm 4 3000>;
 					reset-gpios = <&gpio2 7 0>;
+					solomon,height = <32>;
+					solomon,width = <128>;
+					solomon,page-offset = <0>;
 				};
 			};
 		};
diff --git a/arch/arm/boot/dts/imx28-cfa10049.dts b/arch/arm/boot/dts/imx28-cfa10049.dts
index a0d3e9f..ded7784 100644
--- a/arch/arm/boot/dts/imx28-cfa10049.dts
+++ b/arch/arm/boot/dts/imx28-cfa10049.dts
@@ -132,8 +132,8 @@
 
 		apbx@80040000 {
 			pwm: pwm@80064000 {
-				pinctrl-names = "default", "default";
-				pinctrl-1 = <&pwm3_pins_b>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pwm3_pins_b>;
 				status = "okay";
 			};
 
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 0/4] drivers: video: Optimisations for the ssd1307fb driver
From: Maxime Ripard @ 2013-04-22 10:02 UTC (permalink / raw)
  To: linux-arm-kernel

Hi everyone,

This is a patchset to improve significantly (by an order of magnitude)
the refreshing time of the SSD1306 and SSD1307.

We do so by sending the pixels by batches, instead of 8 at a time,
combined with some additionnal features found on these controllers
that allows to send a whole screen content at once.

This obviously removes the tearing effect that was previously seen
with these controllers.

This patchset depends on the "Add support for the Solomon SSD1306 OLED
Controller" I sent previously.

Thanks,
Maxime

Maxime Ripard (4):
  ARM: cfa10036: dt: Change i2c0 clock frequency
  ssd1307fb: Rework the communication functions
  ssd1307fb: Speed up the communication with the controller
  ssd1307fb: Make use of horizontal addressing mode

 arch/arm/boot/dts/imx28-cfa10036.dts |    1 +
 drivers/video/ssd1307fb.c            |  123 ++++++++++++++++++++++++----------
 2 files changed, 89 insertions(+), 35 deletions(-)

-- 
1.7.10.4


^ permalink raw reply

* [PATCH 1/4] ARM: cfa10036: dt: Change i2c0 clock frequency
From: Maxime Ripard @ 2013-04-22 10:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366624945-5708-1-git-send-email-maxime.ripard@free-electrons.com>

The OLED display can work faster. Change the i2c controller clock
frequency to remove the tearing effect that can be seen on the display.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/boot/dts/imx28-cfa10036.dts |    1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/imx28-cfa10036.dts b/arch/arm/boot/dts/imx28-cfa10036.dts
index 40488f5a8..a524caf 100644
--- a/arch/arm/boot/dts/imx28-cfa10036.dts
+++ b/arch/arm/boot/dts/imx28-cfa10036.dts
@@ -67,6 +67,7 @@
 			i2c0: i2c@80058000 {
 				pinctrl-names = "default";
 				pinctrl-0 = <&i2c0_pins_b>;
+				clock-frequency = <400000>;
 				status = "okay";
 
 				ssd1306: oled@3c {
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 2/4] ssd1307fb: Rework the communication functions
From: Maxime Ripard @ 2013-04-22 10:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366624945-5708-1-git-send-email-maxime.ripard@free-electrons.com>

To efficiently send a whole page to the display, we need to be able to
manipulate more easily the data arrays that has to be sent to the OLED
controller. As such, this patch introduces a ssd1307fb_array structure
that handles both the small header to be sent over i2c, which contains
the type of information sent, and the raw bytes after that.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/video/ssd1307fb.c |   77 +++++++++++++++++++++++++++++----------------
 1 file changed, 50 insertions(+), 27 deletions(-)

diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
index 5ab5281..35f5348 100644
--- a/drivers/video/ssd1307fb.c
+++ b/drivers/video/ssd1307fb.c
@@ -51,6 +51,11 @@ struct ssd1307fb_par {
 	u32 width;
 };
 
+struct ssd1307fb_array {
+	u8	type;
+	u8	data[0];
+};
+
 static struct fb_fix_screeninfo ssd1307fb_fix = {
 	.id		= "Solomon SSD1307",
 	.type		= FB_TYPE_PACKED_PIXELS,
@@ -65,49 +70,67 @@ static struct fb_var_screeninfo ssd1307fb_var = {
 	.bits_per_pixel	= 1,
 };
 
-static int ssd1307fb_write_array(struct i2c_client *client, u8 type, u8 *cmd, u32 len)
+static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type)
 {
-	u8 *buf;
-	int ret = 0;
-
-	buf = kzalloc(len + 1, GFP_KERNEL);
-	if (!buf) {
-		dev_err(&client->dev, "Couldn't allocate sending buffer.\n");
-		return -ENOMEM;
-	}
+	struct ssd1307fb_array *array;
 
-	buf[0] = type;
-	memcpy(buf + 1, cmd, len);
+	array = kzalloc(sizeof(struct ssd1307fb_array) + len, GFP_KERNEL);
+	if (!array)
+		return NULL;
 
-	ret = i2c_master_send(client, buf, len + 1);
-	if (ret != len + 1) {
-		dev_err(&client->dev, "Couldn't send I2C command.\n");
-		goto error;
-	}
+	array->type = type;
 
-error:
-	kfree(buf);
-	return ret;
+	return array;
 }
 
-static inline int ssd1307fb_write_cmd_array(struct i2c_client *client, u8 *cmd, u32 len)
+static int ssd1307fb_write_array(struct i2c_client *client,
+				 struct ssd1307fb_array *array, u32 len)
 {
-	return ssd1307fb_write_array(client, SSD1307FB_COMMAND, cmd, len);
+	int ret;
+
+	len += sizeof(struct ssd1307fb_array);
+
+	ret = i2c_master_send(client, (u8 *)array, len);
+	if (ret != len) {
+		dev_err(&client->dev, "Couldn't send I2C command.\n");
+		return ret;
+	}
+
+	return 0;
 }
 
 static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
 {
-	return ssd1307fb_write_cmd_array(client, &cmd, 1);
-}
+	struct ssd1307fb_array *array;
+	int ret;
 
-static inline int ssd1307fb_write_data_array(struct i2c_client *client, u8 *cmd, u32 len)
-{
-	return ssd1307fb_write_array(client, SSD1307FB_DATA, cmd, len);
+	array = ssd1307fb_alloc_array(1, SSD1307FB_COMMAND);
+	if (!array)
+		return -ENOMEM;
+
+	array->data[0] = cmd;
+
+	ret = ssd1307fb_write_array(client, array, 1);
+	kfree(array);
+
+	return ret;
 }
 
 static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data)
 {
-	return ssd1307fb_write_data_array(client, &data, 1);
+	struct ssd1307fb_array *array;
+	int ret;
+
+	array = ssd1307fb_alloc_array(1, SSD1307FB_DATA);
+	if (!array)
+		return -ENOMEM;
+
+	array->data[0] = data;
+
+	ret = ssd1307fb_write_array(client, array, 1);
+	kfree(array);
+
+	return ret;
 }
 
 static void ssd1307fb_update_display(struct ssd1307fb_par *par)
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 3/4] ssd1307fb: Speed up the communication with the controller
From: Maxime Ripard @ 2013-04-22 10:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366624945-5708-1-git-send-email-maxime.ripard@free-electrons.com>

The code until now was sending only 1pixel-wide page segment at once,
and started a new transfer every time. It has proven very inefficient,
because for one byte to display on the screen, we had to actually send 3
bytes over I2C: the address, the type of data that was going to the
controller, and then the actual data.

This patches changes that by sending a whole page at once, avoiding most
of this expensive overhead.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/video/ssd1307fb.c |   11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
index 35f5348..cab5f7c 100644
--- a/drivers/video/ssd1307fb.c
+++ b/drivers/video/ssd1307fb.c
@@ -168,23 +168,28 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par)
 	 */
 
 	for (i = 0; i < (par->height / 8); i++) {
+		struct ssd1307fb_array *array;
 		ssd1307fb_write_cmd(par->client,
 				    SSD1307FB_START_PAGE_ADDRESS + i + par->page_offset);
 		ssd1307fb_write_cmd(par->client, 0x00);
 		ssd1307fb_write_cmd(par->client, 0x10);
 
+		array = ssd1307fb_alloc_array(par->width, SSD1307FB_DATA);
+
 		for (j = 0; j < par->width; j++) {
-			u8 buf = 0;
+			array->data[j] = 0;
 			for (k = 0; k < 8; k++) {
 				u32 page_length = par->width * i;
 				u32 index = page_length + (par->width * k + j) / 8;
 				u8 byte = *(vmem + index);
 				u8 bit = byte & (1 << (j % 8));
 				bit = bit >> (j % 8);
-				buf |= bit << k;
+				array->data[j] |= bit << k;
 			}
-			ssd1307fb_write_data(par->client, buf);
 		}
+
+		ssd1307fb_write_array(par->client, array, par->width);
+		kfree(array);
 	}
 }
 
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 4/4] ssd1307fb: Make use of horizontal addressing mode
From: Maxime Ripard @ 2013-04-22 10:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366624945-5708-1-git-send-email-maxime.ripard@free-electrons.com>

By default, the ssd1307 controller uses an addressing mode called page
addressing. This mode only increments the column cursor in memory when
writing data but will not increments the page cursor when we are at the
end of the page.

However, the controller supports another addressing mode, called
horizontal addressing, that will maintain both the page and column
cursors when writing data to the controller.

That means that we can just remove the code that increments the current
page address and reset the column cursor when reaching the end of the
line, allowing to have a lower data overhead, and a simpler driver.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/video/ssd1307fb.c |   51 +++++++++++++++++++++++++++++++++------------
 1 file changed, 38 insertions(+), 13 deletions(-)

diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
index cab5f7c..259c9ee 100644
--- a/drivers/video/ssd1307fb.c
+++ b/drivers/video/ssd1307fb.c
@@ -19,6 +19,12 @@
 #define SSD1307FB_DATA			0x40
 #define SSD1307FB_COMMAND		0x80
 
+#define SSD1307FB_SET_ADDRESS_MODE	0x20
+#define SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL	(0x00)
+#define SSD1307FB_SET_ADDRESS_MODE_VERTICAL	(0x01)
+#define SSD1307FB_SET_ADDRESS_MODE_PAGE		(0x02)
+#define SSD1307FB_SET_COL_RANGE		0x21
+#define SSD1307FB_SET_PAGE_RANGE	0x22
 #define SSD1307FB_CONTRAST		0x81
 #define	SSD1307FB_CHARGE_PUMP		0x8d
 #define SSD1307FB_SEG_REMAP_ON		0xa1
@@ -135,9 +141,15 @@ static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data)
 
 static void ssd1307fb_update_display(struct ssd1307fb_par *par)
 {
+	struct ssd1307fb_array *array;
 	u8 *vmem = par->info->screen_base;
 	int i, j, k;
 
+	array = ssd1307fb_alloc_array(par->width * par->height / 8,
+				      SSD1307FB_DATA);
+	if (!array)
+		return;
+
 	/*
 	 * The screen is divided in pages, each having a height of 8
 	 * pixels, and the width of the screen. When sending a byte of
@@ -168,29 +180,22 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par)
 	 */
 
 	for (i = 0; i < (par->height / 8); i++) {
-		struct ssd1307fb_array *array;
-		ssd1307fb_write_cmd(par->client,
-				    SSD1307FB_START_PAGE_ADDRESS + i + par->page_offset);
-		ssd1307fb_write_cmd(par->client, 0x00);
-		ssd1307fb_write_cmd(par->client, 0x10);
-
-		array = ssd1307fb_alloc_array(par->width, SSD1307FB_DATA);
-
 		for (j = 0; j < par->width; j++) {
-			array->data[j] = 0;
+			u32 array_idx = i * par->width + j;
+			array->data[array_idx] = 0;
 			for (k = 0; k < 8; k++) {
 				u32 page_length = par->width * i;
 				u32 index = page_length + (par->width * k + j) / 8;
 				u8 byte = *(vmem + index);
 				u8 bit = byte & (1 << (j % 8));
 				bit = bit >> (j % 8);
-				array->data[j] |= bit << k;
+				array->data[array_idx] |= bit << k;
 			}
 		}
-
-		ssd1307fb_write_array(par->client, array, par->width);
-		kfree(array);
 	}
+
+	ssd1307fb_write_array(par->client, array, par->width * par->height / 8);
+	kfree(array);
 }
 
 
@@ -371,6 +376,26 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
 	if (ret < 0)
 		return ret;
 
+	/* Switch to horizontal addressing mode */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE);
+	ret = ret & ssd1307fb_write_cmd(par->client,
+					SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL);
+	if (ret < 0)
+		return ret;
+
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
+	ret = ret & ssd1307fb_write_cmd(par->client, par->width - 1);
+	if (ret < 0)
+		return ret;
+
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
+	ret = ret & ssd1307fb_write_cmd(par->client,
+					par->page_offset + (par->height / 8) - 1);
+	if (ret < 0)
+		return ret;
+
 	/* Turn on the display */
 	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
 	if (ret < 0)
-- 
1.7.10.4


^ permalink raw reply related

* Re: [PATCH RESEND] video: mxsfb: Fix colors display on lower color depth
From: Maxime Ripard @ 2013-04-22 10:15 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <201304221108.17339.jbe@pengutronix.de>

Hi Juergen,

Le 22/04/2013 11:08, Juergen Beisert a écrit :
> Hi Maxime,
> 
> 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.
> 
> But the "def_rgb666" bitfield description reports correctly to use bit 0..5 
> for each colour component in this mode. Maybe your userland program has a bug 
> and forgets to honor the bitfield description?

Hmmm, that might be the case actually, yes, I'll check into this and let
you know. Thanks!

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

^ permalink raw reply

* Re: [PATCH RESEND] video: mxsfb: Fix colors display on lower color depth
From: Maxime Ripard @ 2013-04-22 10:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <201304221116.18080.marex@denx.de>

Hi Marek,

Le 22/04/2013 11:16, Marek Vasut a écrit :
> 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>
> 
> Did you receive my latest email? Check M28EVK (imx28-m28evk.dts), it uses 18bit 
> LCD and works without this patch I think.

Thanks for the pointer. You mentionned in your other mail that it was
wired on 24bits but that the screen is actually 18 bits. I went to
search for the schematics to look at the wirings to see what could
differ, but I couldn't find any for the M28EVK. Are they publicly available?

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

^ permalink raw reply

* Re: [RFC v2] video: ARM CLCD: Add DT & CDF support
From: Russell King - ARM Linux @ 2013-04-22 14:28 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: <1366306402-21651-1-git-send-email-pawel.moll@arm.com>

On Thu, Apr 18, 2013 at 06:33:21PM +0100, Pawel Moll wrote:
> 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>

Much better.

I will point out though that there be all sorts of worms here when you
come to the previous ARM evaluation boards (which is why the capabilities
stuff got written in the first place) where there's a horrid mixture of
BGR/RGB ordering at various levels of the system - some of which must be
set correctly because the CLCD output isn't strictly used as R bits
G bits and B bits (to support different formats from the CLCD's native
formats.)

^ 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