2015-02-25 21:02:35

by Arun Ramamurthy

[permalink] [raw]
Subject: [PATCH] video: ARM CLCD: Added dt support to set tim2 register

Added code based on linaro tree:
http://git.linaro.org/kernel/linux-linaro-stable.git
with commit id:6846e7822c4cab5a84672baace3b768c2d0db142
at drivers/video/amba-clcd.c. This lets the driver set
certain tim2 register bits after reading them from
device tree.

Reviewed-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
Signed-off-by: Arun Ramamurthy <[email protected]>
---
.../devicetree/bindings/video/arm,pl11x.txt | 17 ++++++++-
drivers/video/fbdev/amba-clcd.c | 41 ++++++++++++++++++++++
2 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/video/arm,pl11x.txt b/Documentation/devicetree/bindings/video/arm,pl11x.txt
index 3e3039a..14d6f87 100644
--- a/Documentation/devicetree/bindings/video/arm,pl11x.txt
+++ b/Documentation/devicetree/bindings/video/arm,pl11x.txt
@@ -35,6 +35,21 @@ Optional properties:
cell's memory interface can handle; if not present, the memory
interface is fast enough to handle all possible video modes

+- tim2: Used to set certain bits in LCDTiming2 register.
+ It can be TIM2_CLKSEL or TIM2_IOE or both
+
+ TIM2_CLKSEL: This bit drives the CLCDCLKSEL signal. It is the select
+ signal for the external LCD clock multiplexor.
+
+ TIM2_IOE: Invert output enable:
+ 0 = CLAC output pin is active HIGH in TFT mode
+ 1 = CLAC output pin is active LOW in TFT mode.
+ This bit selects the active polarity of the output enable signal in
+ TFT mode. In this mode, the CLAC pin is an enable that indicates to
+ the LCD panel when valid display data is available. In active
+ display mode, data is driven onto the LCD data lines at the
+ programmed edge of CLCP when CLAC is in its active state.
+
Required sub-nodes:

- port: describes LCD panel signals, following the common binding
@@ -76,7 +91,7 @@ Example:
clocks = <&oscclk1>, <&oscclk2>;
clock-names = "clcdclk", "apb_pclk";
max-memory-bandwidth = <94371840>; /* Bps, 1024x768@60 16bpp */
-
+ tim2 = "TIM2_CLKSEL";
port {
clcd_pads: endpoint {
remote-endpoint = <&clcd_panel>;
diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c
index 32c0b6b..4e4e50f 100644
--- a/drivers/video/fbdev/amba-clcd.c
+++ b/drivers/video/fbdev/amba-clcd.c
@@ -41,6 +41,44 @@
/* This is limited to 16 characters when displayed by X startup */
static const char *clcd_name = "CLCD FB";

+struct string_lookup {
+ const char *string;
+ const u32 val;
+};
+
+static const struct string_lookup tim2_lookups[] = {
+ { "TIM2_CLKSEL", TIM2_CLKSEL},
+ { "TIM2_IOE", TIM2_IOE},
+ { NULL, 0},
+};
+
+static u32 parse_setting(const struct string_lookup *lookup, const char *name)
+{
+ int i = 0;
+
+ while (lookup[i].string != NULL) {
+ if (strcmp(lookup[i].string, name) == 0)
+ return lookup[i].val;
+ ++i;
+ }
+ return 0;
+}
+
+static u32 get_string_lookup(struct device_node *node, const char *name,
+ const struct string_lookup *lookup)
+{
+ const char *string;
+ int count, i;
+ u32 ret = 0;
+
+ count = of_property_count_strings(node, name);
+ if (count >= 0)
+ for (i = 0; i < count; i++)
+ if (of_property_read_string_index(node, name, i,
+ &string) == 0)
+ ret |= parse_setting(lookup, string);
+ return ret;
+}
/*
* Unfortunately, the enable/disable functions may be called either from
* process or IRQ context, and we _need_ to delay. This is _not_ good.
@@ -626,6 +664,9 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0)
/* Bypass pixel clock divider, data output on the falling edge */
fb->panel->tim2 = TIM2_BCD | TIM2_IPC;

+ fb->panel->tim2 |= get_string_lookup(fb->dev->dev.of_node,
+ "tim2", tim2_lookups);
+
/* TFT display, vert. comp. interrupt at the start of the back porch */
fb->panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1);

--
2.3.0


2015-02-25 21:02:43

by Arun Ramamurthy

[permalink] [raw]
Subject: [PATCH] video: ARM CLCD: Added support for FBIOPAN_DISPLAY and virtual y resolution

Added code to support FBIOPAN_DISPLAY. Also added yres_virtual
parameter to device tree to set the virtual y resolution

Reviewed-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
Signed-off-by: Arun Ramamurthy <[email protected]>
---
.../devicetree/bindings/video/arm,pl11x.txt | 4 +++
drivers/video/fbdev/amba-clcd.c | 31 +++++++++++++++++++---
2 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/video/arm,pl11x.txt b/Documentation/devicetree/bindings/video/arm,pl11x.txt
index 14d6f87..2262cdb 100644
--- a/Documentation/devicetree/bindings/video/arm,pl11x.txt
+++ b/Documentation/devicetree/bindings/video/arm,pl11x.txt
@@ -50,6 +50,10 @@ Optional properties:
display mode, data is driven onto the LCD data lines at the
programmed edge of CLCP when CLAC is in its active state.

+- yres_virtual: Virtual Y resolution,
+ It can be used to configure a virtual y resolution. It
+ must be a value larger than the actual y resolution.
+
Required sub-nodes:

- port: describes LCD panel signals, following the common binding
diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c
index 4e4e50f..3bc09ad 100644
--- a/drivers/video/fbdev/amba-clcd.c
+++ b/drivers/video/fbdev/amba-clcd.c
@@ -454,6 +454,18 @@ static int clcdfb_mmap(struct fb_info *info,
return ret;
}

+static int clcdfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct clcd_fb *fb;
+
+ info->var = *var;
+ fb = to_clcd(info);
+ clcdfb_set_start(fb);
+
+ return 0;
+}
+
static struct fb_ops clcdfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = clcdfb_check_var,
@@ -464,6 +476,7 @@ static struct fb_ops clcdfb_ops = {
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_mmap = clcdfb_mmap,
+ .fb_pan_display = clcdfb_pan_display,
};

static int clcdfb_register(struct clcd_fb *fb)
@@ -517,14 +530,16 @@ static int clcdfb_register(struct clcd_fb *fb)
fb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
fb->fb.fix.type_aux = 0;
fb->fb.fix.xpanstep = 0;
- fb->fb.fix.ypanstep = 0;
+ if (fb->fb.var.yres_virtual > fb->panel->mode.yres)
+ fb->fb.fix.ypanstep = 1;
+ else
+ fb->fb.fix.ypanstep = 0;
fb->fb.fix.ywrapstep = 0;
fb->fb.fix.accel = FB_ACCEL_NONE;

fb->fb.var.xres = fb->panel->mode.xres;
fb->fb.var.yres = fb->panel->mode.yres;
fb->fb.var.xres_virtual = fb->panel->mode.xres;
- fb->fb.var.yres_virtual = fb->panel->mode.yres;
fb->fb.var.bits_per_pixel = fb->panel->bpp;
fb->fb.var.grayscale = fb->panel->grayscale;
fb->fb.var.pixclock = fb->panel->mode.pixclock;
@@ -690,7 +705,7 @@ static int clcdfb_of_init_display(struct clcd_fb *fb)
struct device_node *endpoint;
int err;
unsigned int bpp;
- u32 max_bandwidth;
+ u32 max_bandwidth, yres_virtual;
u32 tft_r0b0g0[3];

fb->panel = devm_kzalloc(&fb->dev->dev, sizeof(*fb->panel), GFP_KERNEL);
@@ -730,6 +745,14 @@ static int clcdfb_of_init_display(struct clcd_fb *fb)
fb->panel->width = -1;
fb->panel->height = -1;

+ /* if yres_virtual property is not specified in device tree,
+ * set it as the actual y resolution */
+ if (of_property_read_u32(fb->dev->dev.of_node,
+ "yres_virtual", &yres_virtual))
+ fb->fb.var.yres_virtual = fb->panel->mode.yres;
+ else
+ fb->fb.var.yres_virtual = yres_virtual;
+
if (of_property_read_u32_array(endpoint,
"arm,pl11x,tft-r0g0b0-pads",
tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) == 0)
@@ -797,7 +820,7 @@ static int clcdfb_of_dma_setup(struct clcd_fb *fb)
if (err)
return err;

- framesize = fb->panel->mode.xres * fb->panel->mode.yres *
+ framesize = fb->panel->mode.xres * fb->fb.var.yres_virtual *
fb->panel->bpp / 8;
fb->fb.screen_base = dma_alloc_coherent(&fb->dev->dev, framesize,
&dma, GFP_KERNEL);
--
2.3.0

2015-02-25 21:03:00

by Arun Ramamurthy

[permalink] [raw]
Subject: [PATCH] video: ARM CLCD: Added support for FBIO_WAITFORVSYNC

Added ioctl and interrupt handler functions to support FBIO_WAITFORVSYNC
Also corrected documentation to make interrupts and interrupt-names
optional as they are not required properties.

Reviewed-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
Signed-off-by: Arun Ramamurthy <[email protected]>0
---
.../devicetree/bindings/video/arm,pl11x.txt | 11 +--
drivers/video/fbdev/amba-clcd.c | 82 ++++++++++++++++++++++
include/linux/amba/clcd.h | 4 ++
3 files changed, 89 insertions(+), 8 deletions(-)

diff --git a/Documentation/devicetree/bindings/video/arm,pl11x.txt b/Documentation/devicetree/bindings/video/arm,pl11x.txt
index 2262cdb..7d19024 100644
--- a/Documentation/devicetree/bindings/video/arm,pl11x.txt
+++ b/Documentation/devicetree/bindings/video/arm,pl11x.txt
@@ -10,14 +10,6 @@ Required properties:

- reg: base address and size of the control registers block

-- interrupt-names: either the single entry "combined" representing a
- combined interrupt output (CLCDINTR), or the four entries
- "mbe", "vcomp", "lnbu", "fuf" representing the individual
- CLCDMBEINTR, CLCDVCOMPINTR, CLCDLNBUINTR, CLCDFUFINTR interrupts
-
-- interrupts: contains an interrupt specifier for each entry in
- interrupt-names
-
- clock-names: should contain "clcdclk" and "apb_pclk"

- clocks: contains phandle and clock specifier pairs for the entries
@@ -54,6 +46,9 @@ Optional properties:
It can be used to configure a virtual y resolution. It
must be a value larger than the actual y resolution.

+- interrupts: contains an interrupt specifier for the clcd vcomp interrupt
+ This is required for the driver to handle FBIO_WAITFORVSYNC ioctls.
+
Required sub-nodes:

- port: describes LCD panel signals, following the common binding
diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c
index 3bc09ad..9f7a58c 100644
--- a/drivers/video/fbdev/amba-clcd.c
+++ b/drivers/video/fbdev/amba-clcd.c
@@ -30,6 +30,8 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_graph.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
#include <video/display_timing.h>
#include <video/of_display_timing.h>
#include <video/videomode.h>
@@ -466,6 +468,73 @@ static int clcdfb_pan_display(struct fb_var_screeninfo *var,
return 0;
}

+static int clcdfb_ioctl(struct fb_info *info,
+ unsigned int cmd, unsigned long args)
+{
+ struct clcd_fb *fb = to_clcd(info);
+ int retval = 0;
+ u32 val, ienb_val;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:{
+ if (fb->lcd_irq <= 0) {
+ retval = -EINVAL;
+ break;
+ }
+ /* disable Vcomp interrupts */
+ ienb_val = readl(fb->regs + fb->off_ienb);
+ ienb_val &= ~CLCD_PL111_IENB_VCOMP;
+ writel(ienb_val, fb->regs + fb->off_ienb);
+
+ /* clear Vcomp interrupt */
+ writel(CLCD_PL111_IENB_VCOMP, fb->regs + CLCD_PL111_ICR);
+
+ /* Generate Interrupt at the start of Vsync */
+ reinit_completion(&fb->wait);
+ val = readl(fb->regs + fb->off_cntl);
+ val &= ~(CNTL_LCDVCOMP(3));
+ writel(val, fb->regs + fb->off_cntl);
+
+ /* enable Vcomp interrupt */
+ ienb_val = readl(fb->regs + fb->off_ienb);
+ ienb_val |= CLCD_PL111_IENB_VCOMP;
+ writel(ienb_val, fb->regs + fb->off_ienb);
+ if (!wait_for_completion_interruptible_timeout
+ (&fb->wait, HZ/10))
+ retval = -ETIMEDOUT;
+ break;
+ }
+ default:
+ retval = -ENOIOCTLCMD;
+ break;
+ }
+ return retval;
+}
+
+static irqreturn_t clcd_interrupt(int irq, void *data)
+{
+ struct clcd_fb *fb = data;
+ u32 val;
+
+ /* check for vsync interrupt */
+ val = readl(fb->regs + CLCD_PL111_MIS);
+
+ if (val & CLCD_PL111_IENB_VCOMP) {
+ val = readl(fb->regs + fb->off_ienb);
+ val &= ~CLCD_PL111_IENB_VCOMP;
+
+ /* disable Vcomp interrupts */
+ writel(val, fb->regs + fb->off_ienb);
+
+ /* clear Vcomp interrupt */
+ writel(CLCD_PL111_IENB_VCOMP, fb->regs + CLCD_PL111_ICR);
+
+ complete(&fb->wait);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
static struct fb_ops clcdfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = clcdfb_check_var,
@@ -477,6 +546,7 @@ static struct fb_ops clcdfb_ops = {
.fb_imageblit = cfb_imageblit,
.fb_mmap = clcdfb_mmap,
.fb_pan_display = clcdfb_pan_display,
+ .fb_ioctl = clcdfb_ioctl,
};

static int clcdfb_register(struct clcd_fb *fb)
@@ -753,6 +823,18 @@ static int clcdfb_of_init_display(struct clcd_fb *fb)
else
fb->fb.var.yres_virtual = yres_virtual;

+ fb->lcd_irq = irq_of_parse_and_map(fb->dev->dev.of_node, 0);
+ if (fb->lcd_irq > 0) {
+ err = devm_request_irq(&fb->dev->dev,
+ fb->lcd_irq, clcd_interrupt,
+ IRQF_SHARED, "clcd", fb);
+ if (err < 0) {
+ dev_err(&fb->dev->dev, "unable to register CLCD interrupt\n");
+ return err;
+ }
+ init_completion(&fb->wait);
+ }
+
if (of_property_read_u32_array(endpoint,
"arm,pl11x,tft-r0g0b0-pads",
tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) == 0)
diff --git a/include/linux/amba/clcd.h b/include/linux/amba/clcd.h
index e82e3ee..6a3bc2d 100644
--- a/include/linux/amba/clcd.h
+++ b/include/linux/amba/clcd.h
@@ -28,6 +28,8 @@
#define CLCD_PL110_UCUR 0x00000028
#define CLCD_PL110_LCUR 0x0000002C

+#define CLCD_PL111_IENB_VCOMP (1 << 3)
+
#define CLCD_PL111_CNTL 0x00000018
#define CLCD_PL111_IENB 0x0000001c
#define CLCD_PL111_RIS 0x00000020
@@ -184,6 +186,8 @@ struct clcd_fb {
u32 clcd_cntl;
u32 cmap[16];
bool clk_enabled;
+ struct completion wait;
+ int lcd_irq;
};

static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs)
--
2.3.0

2015-02-25 21:02:45

by Arun Ramamurthy

[permalink] [raw]
Subject: [PATCH] video: ARM CLCD: Correcting timing checks for STN and TFT dispalys

The minimum values for timing parameters such as left margin,
right margin etc are different for STN and TFT dispalys.
This commit fixes a check that does not account for
this difference.

Reviewed-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
Signed-off-by: Arun Ramamurthy <[email protected]>
---
include/linux/amba/clcd.h | 32 ++++++++++++++++++++++----------
1 file changed, 22 insertions(+), 10 deletions(-)

diff --git a/include/linux/amba/clcd.h b/include/linux/amba/clcd.h
index 6a3bc2d..0fe8a17 100644
--- a/include/linux/amba/clcd.h
+++ b/include/linux/amba/clcd.h
@@ -288,16 +288,28 @@ static inline int clcdfb_check(struct clcd_fb *fb, struct fb_var_screeninfo *var
var->xres_virtual = var->xres = (var->xres + 15) & ~15;
var->yres_virtual = var->yres = (var->yres + 1) & ~1;

-#define CHECK(e,l,h) (var->e < l || var->e > h)
- if (CHECK(right_margin, (5+1), 256) || /* back porch */
- CHECK(left_margin, (5+1), 256) || /* front porch */
- CHECK(hsync_len, (5+1), 256) ||
- var->xres > 4096 ||
- var->lower_margin > 255 || /* back porch */
- var->upper_margin > 255 || /* front porch */
- var->vsync_len > 32 ||
- var->yres > 1024)
- return -EINVAL;
+#define CHECK(e, l, h) (var->e < l || var->e > h)
+ if (!(fb->panel->cntl & CNTL_LCDTFT)) {
+ if (CHECK(right_margin, (5+1), 256) || /* back porch */
+ CHECK(left_margin, (5+1), 256) || /* front porch */
+ CHECK(hsync_len, (5+1), 256) ||
+ var->xres > 4096 ||
+ var->lower_margin > 255 || /* back porch */
+ var->upper_margin > 255 || /* front porch */
+ var->vsync_len > 32 ||
+ var->yres > 1024)
+ return -EINVAL;
+ } else {
+ if (CHECK(right_margin, 1, 256) || /* back porch */
+ CHECK(left_margin, 1, 256) || /* front porch */
+ CHECK(hsync_len, 1, 256) ||
+ var->xres > 4096 ||
+ var->lower_margin > 255 || /* back porch */
+ var->upper_margin > 255 || /* front porch */
+ var->vsync_len > 32 ||
+ var->yres > 1024)
+ return -EINVAL;
+ }
#undef CHECK

/* single panel mode: PCD = max(PCD, 1) */
--
2.3.0