* [PATCH 3/3] ARM: Exynos: Rework platform data for lcd controller for Origen board
From: Thomas Abraham @ 2012-03-03 15:46 UTC (permalink / raw)
To: linux-fbdev
Cc: FlorianSchandinat, linux-samsung-soc, kgene.kim, jg1.han,
ben-linux, patches
In-Reply-To: <1330789808-8253-1-git-send-email-thomas.abraham@linaro.org>
The 'default_win' element in the platform data is removed and the lcd panel
video timing values are moved out of individual window configuration data.
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Jingoo Han <jg1.han@samsung.com>
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
---
arch/arm/mach-exynos/mach-origen.c | 24 ++++++++++++++----------
1 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c
index f57aed4..dc4ecc3 100644
--- a/arch/arm/mach-exynos/mach-origen.c
+++ b/arch/arm/mach-exynos/mach-origen.c
@@ -583,22 +583,26 @@ static struct platform_device origen_lcd_hv070wsa = {
};
static struct s3c_fb_pd_win origen_fb_win0 = {
- .win_mode = {
- .left_margin = 64,
- .right_margin = 16,
- .upper_margin = 64,
- .lower_margin = 16,
- .hsync_len = 48,
- .vsync_len = 3,
- .xres = 1024,
- .yres = 600,
- },
+ .xres = 512,
+ .yres = 300,
.max_bpp = 32,
.default_bpp = 24,
};
+static struct fb_videomode lcd_hv070wsa_timing = {
+ .left_margin = 64,
+ .right_margin = 16,
+ .upper_margin = 64,
+ .lower_margin = 16,
+ .hsync_len = 48,
+ .vsync_len = 3,
+ .xres = 1024,
+ .yres = 600,
+};
+
static struct s3c_fb_platdata origen_lcd_pdata __initdata = {
.win[0] = &origen_fb_win0,
+ .vtiming = &lcd_hv070wsa_timing,
.vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
.vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC |
VIDCON1_INV_VCLK,
--
1.6.6.rc2
^ permalink raw reply related
* [PATCH 2/3] video: s3c-fb: remove 'default_win' element from platform data
From: Thomas Abraham @ 2012-03-03 15:46 UTC (permalink / raw)
To: linux-fbdev
Cc: FlorianSchandinat, linux-samsung-soc, kgene.kim, jg1.han,
ben-linux, patches
In-Reply-To: <1330789808-8253-1-git-send-email-thomas.abraham@linaro.org>
The decision to enable or disable the data output to the lcd panel from
the controller need not be based on the value of 'default_win' element
in the platform data. Instead, the data output to the panel is enabled
if any of the windows are active, else data output is disabled.
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Jingoo Han <jg1.han@samsung.com>
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
---
arch/arm/plat-samsung/include/plat/fb.h | 2 --
drivers/video/s3c-fb.c | 24 ++++--------------------
2 files changed, 4 insertions(+), 22 deletions(-)
diff --git a/arch/arm/plat-samsung/include/plat/fb.h b/arch/arm/plat-samsung/include/plat/fb.h
index 39d6bd7..536002f 100644
--- a/arch/arm/plat-samsung/include/plat/fb.h
+++ b/arch/arm/plat-samsung/include/plat/fb.h
@@ -62,8 +62,6 @@ struct s3c_fb_platdata {
struct s3c_fb_pd_win *win[S3C_FB_MAX_WIN];
struct fb_videomode *vtiming;
- u32 default_win;
-
u32 vidcon0;
u32 vidcon1;
};
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c
index 8e05d4d..8baba31 100644
--- a/drivers/video/s3c-fb.c
+++ b/drivers/video/s3c-fb.c
@@ -531,7 +531,7 @@ static int s3c_fb_set_par(struct fb_info *info)
/* disable the window whilst we update it */
writel(0, regs + WINCON(win_no));
- if (win_no = sfb->pdata->default_win)
+ if (!sfb->output_on)
s3c_fb_enable(sfb, 1);
/* write the buffer address */
@@ -792,7 +792,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
struct s3c_fb_win *win = info->par;
struct s3c_fb *sfb = win->parent;
unsigned int index = win->index;
- u32 wincon;
+ u32 wincon, output_on = sfb->output_on;
dev_dbg(sfb->dev, "blank mode %d\n", blank_mode);
@@ -838,27 +838,11 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
* it is highly likely that we also do not need to output
* anything.
*/
-
- /* We could do something like the following code, but the current
- * system of using framebuffer events means that we cannot make
- * the distinction between just window 0 being inactive and all
- * the windows being down.
- *
- * s3c_fb_enable(sfb, sfb->enabled ? 1 : 0);
- */
-
- /* we're stuck with this until we can do something about overriding
- * the power control using the blanking event for a single fb.
- */
- if (index = sfb->pdata->default_win) {
- shadow_protect_win(win, 1);
- s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0);
- shadow_protect_win(win, 0);
- }
+ s3c_fb_enable(sfb, sfb->enabled ? 1 : 0);
pm_runtime_put_sync(sfb->dev);
- return 0;
+ return output_on = sfb->output_on;
}
/**
--
1.6.6.rc2
^ permalink raw reply related
* [PATCH 1/3] video: s3c-fb: move video interface timing out of window setup data
From: Thomas Abraham @ 2012-03-03 15:46 UTC (permalink / raw)
To: linux-fbdev
Cc: FlorianSchandinat, linux-samsung-soc, kgene.kim, jg1.han,
ben-linux, patches
In-Reply-To: <1330789808-8253-1-git-send-email-thomas.abraham@linaro.org>
The video interface timing is independent of the window setup data.
The resolution of the window can be smaller than that of the lcd
panel to which the video data is output.
So move the video timing data from the per-window setup data to the
platform specific section in the platform data. This also removes
the restriction that atleast one window should have the same
resolution as that of the panel attached.
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Jingoo Han <jg1.han@samsung.com>
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
---
arch/arm/plat-samsung/include/plat/fb.h | 9 ++-
drivers/video/s3c-fb.c | 106 +++++++++++++++++--------------
2 files changed, 63 insertions(+), 52 deletions(-)
diff --git a/arch/arm/plat-samsung/include/plat/fb.h b/arch/arm/plat-samsung/include/plat/fb.h
index 0fedf47..39d6bd7 100644
--- a/arch/arm/plat-samsung/include/plat/fb.h
+++ b/arch/arm/plat-samsung/include/plat/fb.h
@@ -24,15 +24,16 @@
/**
* struct s3c_fb_pd_win - per window setup data
- * @win_mode: The display parameters to initialise (not for window 0)
+ * @xres : The window X size.
+ * @yres : The window Y size.
* @virtual_x: The virtual X size.
* @virtual_y: The virtual Y size.
*/
struct s3c_fb_pd_win {
- struct fb_videomode win_mode;
-
unsigned short default_bpp;
unsigned short max_bpp;
+ unsigned short xres;
+ unsigned short yres;
unsigned short virtual_x;
unsigned short virtual_y;
};
@@ -45,6 +46,7 @@ struct s3c_fb_pd_win {
* @default_win: default window layer number to be used for UI layer.
* @vidcon0: The base vidcon0 values to control the panel data format.
* @vidcon1: The base vidcon1 values to control the panel data output.
+ * @vtiming: Video timing when connected to a RGB type panel.
* @win: The setup data for each hardware window, or NULL for unused.
* @display_mode: The LCD output display mode.
*
@@ -58,6 +60,7 @@ struct s3c_fb_platdata {
void (*setup_gpio)(void);
struct s3c_fb_pd_win *win[S3C_FB_MAX_WIN];
+ struct fb_videomode *vtiming;
u32 default_win;
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c
index 1fb7ddf..8e05d4d 100644
--- a/drivers/video/s3c-fb.c
+++ b/drivers/video/s3c-fb.c
@@ -495,7 +495,6 @@ static int s3c_fb_set_par(struct fb_info *info)
u32 alpha = 0;
u32 data;
u32 pagewidth;
- int clkdiv;
dev_dbg(sfb->dev, "setting framebuffer parameters\n");
@@ -532,46 +531,9 @@ static int s3c_fb_set_par(struct fb_info *info)
/* disable the window whilst we update it */
writel(0, regs + WINCON(win_no));
- /* use platform specified window as the basis for the lcd timings */
-
- if (win_no = sfb->pdata->default_win) {
- clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock);
-
- data = sfb->pdata->vidcon0;
- data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
-
- if (clkdiv > 1)
- data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR;
- else
- data &= ~VIDCON0_CLKDIR; /* 1:1 clock */
-
- /* write the timing data to the panel */
-
- if (sfb->variant.is_2443)
- data |= (1 << 5);
-
- writel(data, regs + VIDCON0);
-
+ if (win_no = sfb->pdata->default_win)
s3c_fb_enable(sfb, 1);
- data = VIDTCON0_VBPD(var->upper_margin - 1) |
- VIDTCON0_VFPD(var->lower_margin - 1) |
- VIDTCON0_VSPW(var->vsync_len - 1);
-
- writel(data, regs + sfb->variant.vidtcon);
-
- data = VIDTCON1_HBPD(var->left_margin - 1) |
- VIDTCON1_HFPD(var->right_margin - 1) |
- VIDTCON1_HSPW(var->hsync_len - 1);
-
- /* VIDTCON1 */
- writel(data, regs + sfb->variant.vidtcon + 4);
-
- data = VIDTCON2_LINEVAL(var->yres - 1) |
- VIDTCON2_HOZVAL(var->xres - 1);
- writel(data, regs + sfb->variant.vidtcon + 8);
- }
-
/* write the buffer address */
/* start and end registers stride is 8 */
@@ -1136,11 +1098,11 @@ static int __devinit s3c_fb_alloc_memory(struct s3c_fb *sfb,
dev_dbg(sfb->dev, "allocating memory for display\n");
- real_size = windata->win_mode.xres * windata->win_mode.yres;
+ real_size = windata->xres * windata->yres;
virt_size = windata->virtual_x * windata->virtual_y;
dev_dbg(sfb->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n",
- real_size, windata->win_mode.xres, windata->win_mode.yres,
+ real_size, windata->xres, windata->yres,
virt_size, windata->virtual_x, windata->virtual_y);
size = (real_size > virt_size) ? real_size : virt_size;
@@ -1222,7 +1184,7 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
struct s3c_fb_win **res)
{
struct fb_var_screeninfo *var;
- struct fb_videomode *initmode;
+ struct fb_videomode initmode;
struct s3c_fb_pd_win *windata;
struct s3c_fb_win *win;
struct fb_info *fbinfo;
@@ -1243,11 +1205,11 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
}
windata = sfb->pdata->win[win_no];
- initmode = &windata->win_mode;
+ initmode = *sfb->pdata->vtiming;
WARN_ON(windata->max_bpp = 0);
- WARN_ON(windata->win_mode.xres = 0);
- WARN_ON(windata->win_mode.yres = 0);
+ WARN_ON(windata->xres = 0);
+ WARN_ON(windata->yres = 0);
win = fbinfo->par;
*res = win;
@@ -1286,7 +1248,9 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
}
/* setup the initial video mode from the window */
- fb_videomode_to_var(&fbinfo->var, initmode);
+ initmode.xres = windata->xres;
+ initmode.yres = windata->yres;
+ fb_videomode_to_var(&fbinfo->var, &initmode);
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
fbinfo->fix.accel = FB_ACCEL_NONE;
@@ -1331,6 +1295,51 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
}
/**
+ * s3c_fb_set_rgb_timing() - set video timing for rgb interface.
+ * @sfb: The base resources for the hardware.
+ *
+ * Set horizontal and vertical lcd rgb interface timing.
+ */
+static void s3c_fb_set_rgb_timing(struct s3c_fb *sfb)
+{
+ struct fb_videomode *vmode = sfb->pdata->vtiming;
+ void __iomem *regs = sfb->regs;
+ int clkdiv;
+ u32 data;
+
+ if (!vmode->pixclock)
+ s3c_fb_missing_pixclock(vmode);
+
+ clkdiv = s3c_fb_calc_pixclk(sfb, vmode->pixclock);
+
+ data = sfb->pdata->vidcon0;
+ data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
+
+ if (clkdiv > 1)
+ data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR;
+ else
+ data &= ~VIDCON0_CLKDIR; /* 1:1 clock */
+
+ if (sfb->variant.is_2443)
+ data |= (1 << 5);
+ writel(data, regs + VIDCON0);
+
+ data = VIDTCON0_VBPD(vmode->upper_margin - 1) |
+ VIDTCON0_VFPD(vmode->lower_margin - 1) |
+ VIDTCON0_VSPW(vmode->vsync_len - 1);
+ writel(data, regs + sfb->variant.vidtcon);
+
+ data = VIDTCON1_HBPD(vmode->left_margin - 1) |
+ VIDTCON1_HFPD(vmode->right_margin - 1) |
+ VIDTCON1_HSPW(vmode->hsync_len - 1);
+ writel(data, regs + sfb->variant.vidtcon + 4);
+
+ data = VIDTCON2_LINEVAL(vmode->yres - 1) |
+ VIDTCON2_HOZVAL(vmode->xres - 1);
+ writel(data, regs + sfb->variant.vidtcon + 8);
+}
+
+/**
* s3c_fb_clear_win() - clear hardware window registers.
* @sfb: The base resources for the hardware.
* @win: The window to process.
@@ -1473,15 +1482,14 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
writel(0xffffff, regs + WKEYCON1);
}
+ s3c_fb_set_rgb_timing(sfb);
+
/* we have the register setup, start allocating framebuffers */
for (win = 0; win < fbdrv->variant.nr_windows; win++) {
if (!pd->win[win])
continue;
- if (!pd->win[win]->win_mode.pixclock)
- s3c_fb_missing_pixclock(&pd->win[win]->win_mode);
-
ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win],
&sfb->windows[win]);
if (ret < 0) {
--
1.6.6.rc2
^ permalink raw reply related
* [PATCH 0/3] video: s3c-fb: Rearrange the elements in platform data
From: Thomas Abraham @ 2012-03-03 15:46 UTC (permalink / raw)
To: linux-fbdev
Cc: FlorianSchandinat, linux-samsung-soc, kgene.kim, jg1.han,
ben-linux, patches
This patchset rearranges the elements in the platform data of the s3c-fb
driver with the intent of adding device tree support to the driver in
subsequent patches.
The first patch moves the video timing information from the individual window
setup data into the platform specific configuration section in the platform
data. The video timing is independent of the window setup. The resolution of
the window could be smaller than that of the lcd panel attached. So the video
timing data is removed from window configuration data.
The second patch removes the need for the 'default_win' element in the
platform data. This element was used to decide whether the video data
output from the controller should be enabled or disabled when the window
specified by 'default_win' is enabled or disabled. With the first patch
removing the need for atleast one window to be of the same resolution as
that of the lcd panel, it is now possible to decide when to enable/disable
the video data output based on the state of each window. If any of the
window is active, the lcd data output is enabled. Otherwise, the lcd data
output is disabled. Hence, the 'default_win' parameter from the platform
data can be removed, which anyways cannot be specified when using
device tree.
The third patch reworks the platform data of the display controller based
on the changes introduced in the first two patches.
The patchset does not include the rework of platform data of all the other
boards which have platform data for the s3c-fb driver. I am posting this
patchset to know if the the changes are conceptually correct. If the first
two patches are acceptable, I will resubmit this patchset with changes to
the platform data for all boards which have s3c-fb platform data.
Thomas Abraham (3):
video: s3c-fb: move video interface timing out of window setup data
video: s3c-fb: remove 'default_win' element from platform data
ARM: Exynos: Rework platform data for lcd controller for Origen board
arch/arm/mach-exynos/mach-origen.c | 24 ++++---
arch/arm/plat-samsung/include/plat/fb.h | 11 ++-
drivers/video/s3c-fb.c | 128 ++++++++++++++----------------
3 files changed, 80 insertions(+), 83 deletions(-)
^ permalink raw reply
* [GIT PULL] fbdev fixes#2 for 3.3
From: Florian Tobias Schandinat @ 2012-03-02 20:36 UTC (permalink / raw)
To: Linus Torvalds; +Cc: LKML, linux-fbdev@vger.kernel.org
Hi Linus,
please pull these fixes for OMAP and viafb.
Thanks,
Florian Tobias Schandinat
The following changes since commit b01543dfe67bb1d191998e90d20534dc354de059:
Linux 3.3-rc4 (2012-02-18 15:53:33 -0800)
are available in the git repository at:
git://github.com/schandinat/linux-2.6.git fbdev-fixes-for-3.3-2
for you to fetch changes up to a3d0e4aecaa32001e02d5ce860d38f14095d06d2:
OMAPDSS: APPLY: make ovl_enable/disable synchronous (2012-02-29 22:42:28 +0000)
----------------------------------------------------------------
fbdev fixes for 3.3
It includes:
- two fixes for OMAP HDMI
- one fix to make new OMAP functions behave as they are supposed to
- one Kconfig dependency fix
- two fixes for viafb for modesetting on VX900 hardware
----------------------------------------------------------------
Archit Taneja (1):
OMAPDSS: HACK: Ensure DSS clock domain gets out of idle when HDMI is enabled
Florian Tobias Schandinat (3):
Merge branch 'for-3.3-rc' of git://gitorious.org/linux-omap-dss2/linux
into fbdev-for-linus
viafb: select HW scaling on VX900 for IGA2
viafb: fix IGA1 modesetting on VX900
Rob Clark (1):
OMAPDSS: HDMI: hot plug detect fix
Tomi Valkeinen (2):
OMAPDSS: panel-dvi: Add Kconfig dependency on I2C
OMAPDSS: APPLY: make ovl_enable/disable synchronous
drivers/video/omap2/displays/Kconfig | 2 +-
drivers/video/omap2/dss/apply.c | 6 ++++++
drivers/video/omap2/dss/hdmi.c | 24 +++++++++++++++++++++++-
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 9 +--------
drivers/video/via/hw.c | 4 ++++
5 files changed, 35 insertions(+), 10 deletions(-)
^ permalink raw reply
* Re: Kernel Display and Video API Consolidation mini-summit at ELC 2012 - Notes
From: Heiko Stübner @ 2012-03-02 14:23 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Sakari Ailus, Sumit Semwal, Jesse Barker, Jesse Barnes, Rob Clark,
Pawel Osciak, Marek Szyprowski, Tomasz Stanislawski, Magnus Damm,
Marcus Lorentzon, Alexander Deucher, linux-media, dri-devel,
linux-fbdev
In-Reply-To: <1775349.d0yvHiVdjB@avalon>
Am Freitag, 17. Februar 2012, 00:25:51 schrieb Laurent Pinchart:
> Hello everybody,
>
> First of all, I would like to thank all the attendees for their
> participation in the mini-summit that helped make the meeting a success.
>
> Here are my consolidated notes that cover both the Linaro Connect meeting
> and the ELC meeting. They're also available at
> http://www.ideasonboard.org/media/meetings/.
>
>
> Kernel Display and Video API Consolidation mini-summit at ELC 2012
> ------------------------------------------------------------------
>
[...]
> *** Display Panel Drivers ***
>
> Goal: Sharing display panel drivers between display controllers from
> different vendors.
>
> Panels are connected to the display controller through a standard bus
> with a control channel (DSI and eDP are two major such buses). Various
> vendors have created proprietary interfaces for panel drivers:
>
> - TI on OMAP (drivers/video/omap2/displays).
> - Samsung on Exynos (drivers/video/exynos).
> - ST-Ericsson on MCDE
> (http://www.igloocommunity.org/gitweb/?p=kernel/igloo-
> kernel.git;a=tree;f=drivers/video/mcde)
> - Renesas is working on a similar interface for SH Mobile.
>
> HDMI-on-DSI transmitters, while not panels per-se, can be supported
> through the same API.
>
> A Low level Linux Display Framework (https://lkml.org/lkml/2011/9/15/107)
> has been proposed and overlaps with this topic.
>
> For DSI, a possible abstraction level would be a DCS (Display Command
> Set) bus. Panels and/or HDMI-on-DSI transmitter drivers would be
> implemented as DCS drivers.
>
> Action points:
> - Marcus to work on a proposal for DCS-based panels (with Tomi Valkeinen
> and Morimoto-san).
It would also be interesting to see something similar for MIPI-DBI (type B for
me) [aka rfbi on omap], as most epaper displays use this to transmit data and
currently use half-baked interfaces.
So hopefully I'll be able to follow the discussion and can then try to convert
your findings to the dbi case.
Heiko
^ permalink raw reply
* [PATCH] fbdev: remove "default y" from FB_SH_MOBILE_MERAM
From: Kuninori Morimoto @ 2012-03-02 9:06 UTC (permalink / raw)
To: linux-fbdev
"default y" of FB_SH_MOBILE_MERAM was overkill option.
"depends on FB_SH_MOBILE_LCDC" is very enough here.
This patch remove it.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
drivers/video/Kconfig | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 549b960..dfec9b7 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1994,7 +1994,6 @@ config FB_SH_MOBILE_HDMI
config FB_SH_MOBILE_MERAM
tristate "SuperH Mobile MERAM read ahead support for LCDC"
depends on FB_SH_MOBILE_LCDC
- default y
---help---
Enable MERAM support for the SH-Mobile LCD controller.
--
1.7.5.4
^ permalink raw reply related
* [patch] backlight: s6e63m0: corruption storing gamma mode
From: Dan Carpenter @ 2012-03-02 6:56 UTC (permalink / raw)
To: linux-fbdev
strict_strtoul() writes a long but ->gamma_mode only has space to store
an int, so on 64 bit systems we end up scribbling over
->gamma_table_count as well. I've changed it to use kstrtouint()
instead.
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
diff --git a/drivers/video/backlight/s6e63m0.c b/drivers/video/backlight/s6e63m0.c
index 9b0092e..e264f55 100644
--- a/drivers/video/backlight/s6e63m0.c
+++ b/drivers/video/backlight/s6e63m0.c
@@ -690,7 +690,7 @@ static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
struct backlight_device *bd = NULL;
int brightness, rc;
- rc = strict_strtoul(buf, 0, (unsigned long *)&lcd->gamma_mode);
+ rc = kstrtouint(buf, 0, &lcd->gamma_mode);
if (rc < 0)
return rc;
^ permalink raw reply related
* [PATCH] video:uvesafb: notice user when we failed to save hardware state
From: Wang YanQing @ 2012-03-02 1:45 UTC (permalink / raw)
To: spock; +Cc: FlorianSchandinat, linux-fbdev, linux-kernel
uvesafb_open may failed to save hardware state when kmalloc failed
in uvesafb_vbe_state_save, we should check this and notice user.
Signed-off-by: Wang YanQing <udknight@gmail.com>
---
drivers/video/uvesafb.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c
index e7f69ef..9db3de3 100644
--- a/drivers/video/uvesafb.c
+++ b/drivers/video/uvesafb.c
@@ -362,7 +362,7 @@ static u8 *uvesafb_vbe_state_save(struct uvesafb_par *par)
state = kmalloc(par->vbe_state_size, GFP_KERNEL);
if (!state)
- return NULL;
+ return ERR_PTR(-ENOMEM);
task = uvesafb_prep();
if (!task) {
@@ -1172,9 +1172,17 @@ static int uvesafb_open(struct fb_info *info, int user)
{
struct uvesafb_par *par = info->par;
int cnt = atomic_read(&par->ref_count);
+ u8 *buf = NULL;
- if (!cnt && par->vbe_state_size)
- par->vbe_state_orig = uvesafb_vbe_state_save(par);
+ if (!cnt && par->vbe_state_size) {
+ buf = uvesafb_vbe_state_save(par);
+ if (IS_ERR(buf)) {
+ printk(KERN_WARNING "uvesafb: save hardware state"
+ "failed, error code is %ld!\n", PTR_ERR(buf));
+ } else {
+ par->vbe_state_orig = buf;
+ }
+ }
atomic_inc(&par->ref_count);
return 0;
--
1.7.9.2.315.g25a78
^ permalink raw reply related
* [PATCH] video:uvesafb: Fix oops that uvesafb try to execute NX-protected page
From: Wang YanQing @ 2012-03-02 0:48 UTC (permalink / raw)
To: FlorianSchandinat; +Cc: linux-fbdev, linux-kernel, spock
Ok! I think I have learned to make thing simple and send to the right people:)
This patch try to fix the oops below that catched in my machine
[ 81.560602] uvesafb: NVIDIA Corporation, GT216 Board - 0696a290, Chip Rev , OEM: NVIDIA, VBE v3.0
[ 81.609384] uvesafb: protected mode interface info at c000:d350
[ 81.609388] uvesafb: pmi: set display start = c00cd3b3, set palette = c00cd40e
[ 81.609390] uvesafb: pmi: ports = 3b4 3b5 3ba 3c0 3c1 3c4 3c5 3c6 3c7 3c8 3c9 3cc 3ce 3cf 3d0 3d1 3d2 3d3 3d4 3d5 3da
[ 81.614558] uvesafb: VBIOS/hardware doesn't support DDC transfers
[ 81.614562] uvesafb: no monitor limits have been set, default refresh rate will be used
[ 81.614994] uvesafb: scrolling: ypan using protected mode interface, yres_virtualI15
[ 81.744147] kernel tried to execute NX-protected page - exploit attempt? (uid: 0)
[ 81.744153] BUG: unable to handle kernel paging request at c00cd3b3
[ 81.744159] IP: [<c00cd3b3>] 0xc00cd3b2
[ 81.744167] *pdpt = 00000000016d6001 *pde = 0000000001c7b067 *pte = 80000000000cd163
[ 81.744171] Oops: 0011 [#1] SMP
[ 81.744174] Modules linked in: uvesafb(+) cfbcopyarea cfbimgblt cfbfillrect
[ 81.744178]
[ 81.744181] Pid: 3497, comm: modprobe Not tainted 3.3.0-rc4NX+ #71 Acer Aspire 4741 /Aspire 4741
[ 81.744185] EIP: 0060:[<c00cd3b3>] EFLAGS: 00010246 CPU: 0
[ 81.744187] EIP is at 0xc00cd3b3
[ 81.744189] EAX: 00004f07 EBX: 00000000 ECX: 00000000 EDX: 00000000
[ 81.744191] ESI: f763f000 EDI: f763f6e8 EBP: f57f3a0c ESP: f57f3a00
[ 81.744192] DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068
[ 81.744195] Process modprobe (pid: 3497, tiõ7f2000 task÷48c600 task.tiõ7f2000)
[ 81.744196] Stack:
[ 81.744197] f82512c5 f759341c 00000000 f57f3a30 c124a9bc 00000001 00000001 000001e0
[ 81.744202] f8251280 f763f000 f7593400 00000000 f57f3a40 c12598dd f5c0c000 00000000
[ 81.744206] f57f3b10 c1255efe c125a21a 00000006 f763f09c 00000000 c1c6cb60 f7593400
[ 81.744210] Call Trace:
[ 81.744215] [<f82512c5>] ? uvesafb_pan_display+0x45/0x60 [uvesafb]
[ 81.744222] [<c124a9bc>] fb_pan_display+0x10c/0x160
[ 81.744226] [<f8251280>] ? uvesafb_vbe_find_mode+0x180/0x180 [uvesafb]
[ 81.744230] [<c12598dd>] bit_update_start+0x1d/0x50
[ 81.744232] [<c1255efe>] fbcon_switch+0x39e/0x550
[ 81.744235] [<c125a21a>] ? bit_cursor+0x4ea/0x560
[ 81.744240] [<c129b6cb>] redraw_screen+0x12b/0x220
[ 81.744245] [<c128843b>] ? tty_do_resize+0x3b/0xc0
[ 81.744247] [<c129ef42>] vc_do_resize+0x3d2/0x3e0
[ 81.744250] [<c129efb4>] vc_resize+0x14/0x20
[ 81.744253] [<c12586bd>] fbcon_init+0x29d/0x500
[ 81.744255] [<c12984c4>] ? set_inverse_trans_unicode+0xe4/0x110
[ 81.744258] [<c129b378>] visual_init+0xb8/0x150
[ 81.744261] [<c129c16c>] bind_con_driver+0x16c/0x360
[ 81.744264] [<c129b47e>] ? register_con_driver+0x6e/0x190
[ 81.744267] [<c129c3a1>] take_over_console+0x41/0x50
[ 81.744269] [<c1257b7a>] fbcon_takeover+0x6a/0xd0
[ 81.744272] [<c12594b8>] fbcon_event_notify+0x758/0x790
[ 81.744277] [<c10929e2>] notifier_call_chain+0x42/0xb0
[ 81.744280] [<c1092d30>] __blocking_notifier_call_chain+0x60/0x90
[ 81.744283] [<c1092d7a>] blocking_notifier_call_chain+0x1a/0x20
[ 81.744285] [<c124a5a1>] fb_notifier_call_chain+0x11/0x20
[ 81.744288] [<c124b759>] register_framebuffer+0x1d9/0x2b0
[ 81.744293] [<c1061c73>] ? ioremap_wc+0x33/0x40
[ 81.744298] [<f82537c6>] uvesafb_probe+0xaba/0xc40 [uvesafb]
[ 81.744302] [<c12bb81f>] platform_drv_probe+0xf/0x20
[ 81.744306] [<c12ba558>] driver_probe_device+0x68/0x170
[ 81.744309] [<c12ba731>] __device_attach+0x41/0x50
[ 81.744313] [<c12b9088>] bus_for_each_drv+0x48/0x70
[ 81.744316] [<c12ba7f3>] device_attach+0x83/0xa0
[ 81.744319] [<c12ba6f0>] ? __driver_attach+0x90/0x90
[ 81.744321] [<c12b991f>] bus_probe_device+0x6f/0x90
[ 81.744324] [<c12b8a45>] device_add+0x5e5/0x680
[ 81.744329] [<c122a1a3>] ? kvasprintf+0x43/0x60
[ 81.744332] [<c121e6e4>] ? kobject_set_name_vargs+0x64/0x70
[ 81.744335] [<c121e6e4>] ? kobject_set_name_vargs+0x64/0x70
[ 81.744339] [<c12bbe9f>] platform_device_add+0xff/0x1b0
[ 81.744343] [<f8252906>] uvesafb_init+0x50/0x9b [uvesafb]
[ 81.744346] [<c100111f>] do_one_initcall+0x2f/0x170
[ 81.744350] [<f82528b6>] ? uvesafb_is_valid_mode+0x66/0x66 [uvesafb]
[ 81.744355] [<c10c6994>] sys_init_module+0xf4/0x1410
[ 81.744359] [<c1157fc0>] ? vfsmount_lock_local_unlock_cpu+0x30/0x30
[ 81.744363] [<c144cb10>] sysenter_do_call+0x12/0x36
[ 81.744365] Code: f5 00 00 00 32 f6 66 8b da 66 d1 e3 66 ba d4 03 8a e3 b0 1c 66 ef b0 1e 66 ef 8a e7 b0 1d 66 ef b0 1f 66 ef e8 fa 00 00 00 61 c3 <60> e8 c8 00 00 00 66 8b f3 66 8b da 66 ba d4 03 b0 0c 8a e5 66
[ 81.744388] EIP: [<c00cd3b3>] 0xc00cd3b3 SS:ESP 0068:f57f3a00
[ 81.744391] CR2: 00000000c00cd3b3
[ 81.744393] ---[ end trace 18b2c87c925b54d6 ]---
Signed-off-by: Wang YanQing <udknight@gmail.com>
---
drivers/video/uvesafb.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c
index e7f69ef..f9a670d 100644
--- a/drivers/video/uvesafb.c
+++ b/drivers/video/uvesafb.c
@@ -23,6 +23,7 @@
#include <video/uvesafb.h>
#ifdef CONFIG_X86
#include <video/vga.h>
+#include <linux/pci.h>
#endif
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
@@ -815,8 +816,15 @@ static int __devinit uvesafb_vbe_init(struct fb_info *info)
par->pmi_setpal = pmi_setpal;
par->ypan = ypan;
- if (par->pmi_setpal || par->ypan)
- uvesafb_vbe_getpmi(task, par);
+ if (par->pmi_setpal || par->ypan) {
+ if (pcibios_enabled) {
+ uvesafb_vbe_getpmi(task, par);
+ } else {
+ par->pmi_setpal = par->ypan = 0;
+ printk(KERN_WARNING "uvesafb: PCI BIOS area is NX."
+ "Can't use protected mode interface\n");
+ }
+ }
#else
/* The protected mode interface is not available on non-x86. */
par->pmi_setpal = par->ypan = 0;
--
1.7.9.2.315.g25a78
^ permalink raw reply related
* [PATCH v2 57/57] fbdev: sh_mobile_lcdc: Implement overlays support
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
To: linux-fbdev
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/sh_mobile_lcdcfb.c | 955 +++++++++++++++++++++++++++++++++++---
include/video/sh_mobile_lcdc.h | 7 +
2 files changed, 890 insertions(+), 72 deletions(-)
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 36dad2f..b763fd8 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -12,6 +12,7 @@
#include <linux/backlight.h>
#include <linux/clk.h>
#include <linux/console.h>
+#include <linux/ctype.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/gpio.h>
@@ -32,12 +33,176 @@
#include "sh_mobile_lcdcfb.h"
+/* ----------------------------------------------------------------------------
+ * Overlay register definitions
+ */
+
+#define LDBCR 0xb00
+#define LDBCR_UPC(n) (1 << ((n) + 16))
+#define LDBCR_UPF(n) (1 << ((n) + 8))
+#define LDBCR_UPD(n) (1 << ((n) + 0))
+#define LDBnBSIFR(n) (0xb20 + (n) * 0x20 + 0x00)
+#define LDBBSIFR_EN (1 << 31)
+#define LDBBSIFR_VS (1 << 29)
+#define LDBBSIFR_BRSEL (1 << 28)
+#define LDBBSIFR_MX (1 << 27)
+#define LDBBSIFR_MY (1 << 26)
+#define LDBBSIFR_CV3 (3 << 24)
+#define LDBBSIFR_CV2 (2 << 24)
+#define LDBBSIFR_CV1 (1 << 24)
+#define LDBBSIFR_CV0 (0 << 24)
+#define LDBBSIFR_CV_MASK (3 << 24)
+#define LDBBSIFR_LAY_MASK (0xff << 16)
+#define LDBBSIFR_LAY_SHIFT 16
+#define LDBBSIFR_ROP3_MASK (0xff << 16)
+#define LDBBSIFR_ROP3_SHIFT 16
+#define LDBBSIFR_AL_PL8 (3 << 14)
+#define LDBBSIFR_AL_PL1 (2 << 14)
+#define LDBBSIFR_AL_PK (1 << 14)
+#define LDBBSIFR_AL_1 (0 << 14)
+#define LDBBSIFR_AL_MASK (3 << 14)
+#define LDBBSIFR_SWPL (1 << 10)
+#define LDBBSIFR_SWPW (1 << 9)
+#define LDBBSIFR_SWPB (1 << 8)
+#define LDBBSIFR_RY (1 << 7)
+#define LDBBSIFR_CHRR_420 (2 << 0)
+#define LDBBSIFR_CHRR_422 (1 << 0)
+#define LDBBSIFR_CHRR_444 (0 << 0)
+#define LDBBSIFR_RPKF_ARGB32 (0x00 << 0)
+#define LDBBSIFR_RPKF_RGB16 (0x03 << 0)
+#define LDBBSIFR_RPKF_RGB24 (0x0b << 0)
+#define LDBBSIFR_RPKF_MASK (0x1f << 0)
+#define LDBnBSSZR(n) (0xb20 + (n) * 0x20 + 0x04)
+#define LDBBSSZR_BVSS_MASK (0xfff << 16)
+#define LDBBSSZR_BVSS_SHIFT 16
+#define LDBBSSZR_BHSS_MASK (0xfff << 0)
+#define LDBBSSZR_BHSS_SHIFT 0
+#define LDBnBLOCR(n) (0xb20 + (n) * 0x20 + 0x08)
+#define LDBBLOCR_CVLC_MASK (0xfff << 16)
+#define LDBBLOCR_CVLC_SHIFT 16
+#define LDBBLOCR_CHLC_MASK (0xfff << 0)
+#define LDBBLOCR_CHLC_SHIFT 0
+#define LDBnBSMWR(n) (0xb20 + (n) * 0x20 + 0x0c)
+#define LDBBSMWR_BSMWA_MASK (0xffff << 16)
+#define LDBBSMWR_BSMWA_SHIFT 16
+#define LDBBSMWR_BSMW_MASK (0xffff << 0)
+#define LDBBSMWR_BSMW_SHIFT 0
+#define LDBnBSAYR(n) (0xb20 + (n) * 0x20 + 0x10)
+#define LDBBSAYR_FG1A_MASK (0xff << 24)
+#define LDBBSAYR_FG1A_SHIFT 24
+#define LDBBSAYR_FG1R_MASK (0xff << 16)
+#define LDBBSAYR_FG1R_SHIFT 16
+#define LDBBSAYR_FG1G_MASK (0xff << 8)
+#define LDBBSAYR_FG1G_SHIFT 8
+#define LDBBSAYR_FG1B_MASK (0xff << 0)
+#define LDBBSAYR_FG1B_SHIFT 0
+#define LDBnBSACR(n) (0xb20 + (n) * 0x20 + 0x14)
+#define LDBBSACR_FG2A_MASK (0xff << 24)
+#define LDBBSACR_FG2A_SHIFT 24
+#define LDBBSACR_FG2R_MASK (0xff << 16)
+#define LDBBSACR_FG2R_SHIFT 16
+#define LDBBSACR_FG2G_MASK (0xff << 8)
+#define LDBBSACR_FG2G_SHIFT 8
+#define LDBBSACR_FG2B_MASK (0xff << 0)
+#define LDBBSACR_FG2B_SHIFT 0
+#define LDBnBSAAR(n) (0xb20 + (n) * 0x20 + 0x18)
+#define LDBBSAAR_AP_MASK (0xff << 24)
+#define LDBBSAAR_AP_SHIFT 24
+#define LDBBSAAR_R_MASK (0xff << 16)
+#define LDBBSAAR_R_SHIFT 16
+#define LDBBSAAR_GY_MASK (0xff << 8)
+#define LDBBSAAR_GY_SHIFT 8
+#define LDBBSAAR_B_MASK (0xff << 0)
+#define LDBBSAAR_B_SHIFT 0
+#define LDBnBPPCR(n) (0xb20 + (n) * 0x20 + 0x1c)
+#define LDBBPPCR_AP_MASK (0xff << 24)
+#define LDBBPPCR_AP_SHIFT 24
+#define LDBBPPCR_R_MASK (0xff << 16)
+#define LDBBPPCR_R_SHIFT 16
+#define LDBBPPCR_GY_MASK (0xff << 8)
+#define LDBBPPCR_GY_SHIFT 8
+#define LDBBPPCR_B_MASK (0xff << 0)
+#define LDBBPPCR_B_SHIFT 0
+#define LDBnBBGCL(n) (0xb10 + (n) * 0x04)
+#define LDBBBGCL_BGA_MASK (0xff << 24)
+#define LDBBBGCL_BGA_SHIFT 24
+#define LDBBBGCL_BGR_MASK (0xff << 16)
+#define LDBBBGCL_BGR_SHIFT 16
+#define LDBBBGCL_BGG_MASK (0xff << 8)
+#define LDBBBGCL_BGG_SHIFT 8
+#define LDBBBGCL_BGB_MASK (0xff << 0)
+#define LDBBBGCL_BGB_SHIFT 0
+
#define SIDE_B_OFFSET 0x1000
#define MIRROR_OFFSET 0x2000
#define MAX_XRES 1920
#define MAX_YRES 1080
+enum sh_mobile_lcdc_overlay_mode {
+ LCDC_OVERLAY_BLEND,
+ LCDC_OVERLAY_ROP3,
+};
+
+/*
+ * struct sh_mobile_lcdc_overlay - LCDC display overlay
+ *
+ * @channel: LCDC channel this overlay belongs to
+ * @cfg: Overlay configuration
+ * @info: Frame buffer device
+ * @index: Overlay index (0-3)
+ * @base: Overlay registers base address
+ * @enabled: True if the overlay is enabled
+ * @mode: Overlay blending mode (alpha blend or ROP3)
+ * @alpha: Global alpha blending value (0-255, for alpha blending mode)
+ * @rop3: Raster operation (for ROP3 mode)
+ * @fb_mem: Frame buffer virtual memory address
+ * @fb_size: Frame buffer size in bytes
+ * @dma_handle: Frame buffer DMA address
+ * @base_addr_y: Overlay base address (RGB or luma component)
+ * @base_addr_c: Overlay base address (chroma component)
+ * @pan_offset: Current pan offset in bytes
+ * @format: Current pixelf format
+ * @xres: Horizontal visible resolution
+ * @xres_virtual: Horizontal total resolution
+ * @yres: Vertical visible resolution
+ * @yres_virtual: Vertical total resolution
+ * @pitch: Overlay line pitch
+ * @pos_x: Horizontal overlay position
+ * @pos_y: Vertical overlay position
+ */
+struct sh_mobile_lcdc_overlay {
+ struct sh_mobile_lcdc_chan *channel;
+
+ const struct sh_mobile_lcdc_overlay_cfg *cfg;
+ struct fb_info *info;
+
+ unsigned int index;
+ unsigned long base;
+
+ bool enabled;
+ enum sh_mobile_lcdc_overlay_mode mode;
+ unsigned int alpha;
+ unsigned int rop3;
+
+ void *fb_mem;
+ unsigned long fb_size;
+
+ dma_addr_t dma_handle;
+ unsigned long base_addr_y;
+ unsigned long base_addr_c;
+ unsigned long pan_offset;
+
+ const struct sh_mobile_lcdc_format_info *format;
+ unsigned int xres;
+ unsigned int xres_virtual;
+ unsigned int yres;
+ unsigned int yres_virtual;
+ unsigned int pitch;
+ int pos_x;
+ int pos_y;
+};
+
struct sh_mobile_lcdc_priv {
void __iomem *base;
int irq;
@@ -45,7 +210,10 @@ struct sh_mobile_lcdc_priv {
struct device *dev;
struct clk *dot_clk;
unsigned long lddckr;
+
struct sh_mobile_lcdc_chan ch[2];
+ struct sh_mobile_lcdc_overlay overlays[4];
+
struct notifier_block notifier;
int started;
int forced_fourcc; /* 2 channel LCDC must share fourcc setting */
@@ -141,6 +309,13 @@ static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan,
return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]);
}
+static void lcdc_write_overlay(struct sh_mobile_lcdc_overlay *ovl,
+ int reg, unsigned long data)
+{
+ iowrite32(data, ovl->channel->lcdc->base + reg);
+ iowrite32(data, ovl->channel->lcdc->base + reg + SIDE_B_OFFSET);
+}
+
static void lcdc_write(struct sh_mobile_lcdc_priv *priv,
unsigned long reg_offs, unsigned long data)
{
@@ -685,6 +860,93 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
lcdc_write_chan(ch, LDHAJR, tmp);
}
+static void sh_mobile_lcdc_overlay_setup(struct sh_mobile_lcdc_overlay *ovl)
+{
+ u32 format = 0;
+
+ if (!ovl->enabled) {
+ lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), 0);
+ return;
+ }
+
+ ovl->base_addr_y = ovl->dma_handle;
+ ovl->base_addr_c = ovl->base_addr_y + ovl->xres
+ * ovl->yres_virtual;
+
+ switch (ovl->mode) {
+ case LCDC_OVERLAY_BLEND:
+ format = LDBBSIFR_EN | (ovl->alpha << LDBBSIFR_LAY_SHIFT);
+ break;
+
+ case LCDC_OVERLAY_ROP3:
+ format = LDBBSIFR_EN | LDBBSIFR_BRSEL
+ | (ovl->rop3 << LDBBSIFR_ROP3_SHIFT);
+ break;
+ }
+
+ switch (ovl->format->fourcc) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_NV42:
+ format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV24:
+ format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ default:
+ format |= LDBBSIFR_SWPL;
+ break;
+ }
+
+ switch (ovl->format->fourcc) {
+ case V4L2_PIX_FMT_RGB565:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420;
+ break;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422;
+ break;
+ case V4L2_PIX_FMT_NV24:
+ case V4L2_PIX_FMT_NV42:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444;
+ break;
+ }
+
+ lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index));
+
+ lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), format);
+
+ lcdc_write_overlay(ovl, LDBnBSSZR(ovl->index),
+ (ovl->yres << LDBBSSZR_BVSS_SHIFT) |
+ (ovl->xres << LDBBSSZR_BHSS_SHIFT));
+ lcdc_write_overlay(ovl, LDBnBLOCR(ovl->index),
+ (ovl->pos_y << LDBBLOCR_CVLC_SHIFT) |
+ (ovl->pos_x << LDBBLOCR_CHLC_SHIFT));
+ lcdc_write_overlay(ovl, LDBnBSMWR(ovl->index),
+ ovl->pitch << LDBBSMWR_BSMW_SHIFT);
+
+ lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y);
+ lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c);
+
+ lcdc_write(ovl->channel->lcdc, LDBCR,
+ LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index));
+}
+
/*
* __sh_mobile_lcdc_start - Configure and start the LCDC
* @priv: LCDC device
@@ -891,6 +1153,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
}
}
+ for (k = 0; k < ARRAY_SIZE(priv->overlays); ++k) {
+ struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[k];
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
/* Start the LCDC. */
__sh_mobile_lcdc_start(priv);
@@ -974,8 +1241,539 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
sh_mobile_lcdc_clk_off(priv);
}
+static int __sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ if (var->xres > MAX_XRES || var->yres > MAX_YRES)
+ return -EINVAL;
+
+ /* Make sure the virtual resolution is at least as big as the visible
+ * resolution.
+ */
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ if (sh_mobile_format_is_fourcc(var)) {
+ const struct sh_mobile_lcdc_format_info *format;
+
+ format = sh_mobile_format_info(var->grayscale);
+ if (format = NULL)
+ return -EINVAL;
+ var->bits_per_pixel = format->bpp;
+
+ /* Default to RGB and JPEG color-spaces for RGB and YUV formats
+ * respectively.
+ */
+ if (!format->yuv)
+ var->colorspace = V4L2_COLORSPACE_SRGB;
+ else if (var->colorspace != V4L2_COLORSPACE_REC709)
+ var->colorspace = V4L2_COLORSPACE_JPEG;
+ } else {
+ if (var->bits_per_pixel <= 16) { /* RGB 565 */
+ var->bits_per_pixel = 16;
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ } else if (var->bits_per_pixel <= 24) { /* RGB 888 */
+ var->bits_per_pixel = 24;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */
+ var->bits_per_pixel = 32;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ } else
+ return -EINVAL;
+
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+ var->transp.msb_right = 0;
+ }
+
+ /* Make sure we don't exceed our allocated memory. */
+ if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 >
+ info->fix.smem_len)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Frame buffer operations - Overlays
+ */
+
+static ssize_t
+overlay_alpha_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->alpha);
+}
+
+static ssize_t
+overlay_alpha_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ unsigned int alpha;
+ char *endp;
+
+ alpha = simple_strtoul(buf, &endp, 10);
+ if (isspace(*endp))
+ endp++;
+
+ if (endp - buf != count)
+ return -EINVAL;
+
+ if (alpha > 255)
+ return -EINVAL;
+
+ if (ovl->alpha != alpha) {
+ ovl->alpha = alpha;
+
+ if (ovl->mode = LCDC_OVERLAY_BLEND && ovl->enabled)
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
+ return count;
+}
+
+static ssize_t
+overlay_enabled_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->enabled);
+}
+
+static ssize_t
+overlay_enabled_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ bool enabled;
+ char *endp;
+
+ enabled = !!simple_strtoul(buf, &endp, 10);
+ if (isspace(*endp))
+ endp++;
+
+ if (endp - buf != count)
+ return -EINVAL;
+
+ if (ovl->enabled != enabled) {
+ ovl->enabled = enabled;
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
+ return count;
+}
+
+static ssize_t
+overlay_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->mode);
+}
+
+static ssize_t
+overlay_mode_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ unsigned int mode;
+ char *endp;
+
+ mode = simple_strtoul(buf, &endp, 10);
+ if (isspace(*endp))
+ endp++;
+
+ if (endp - buf != count)
+ return -EINVAL;
+
+ if (mode != LCDC_OVERLAY_BLEND && mode != LCDC_OVERLAY_ROP3)
+ return -EINVAL;
+
+ if (ovl->mode != mode) {
+ ovl->mode = mode;
+
+ if (ovl->enabled)
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
+ return count;
+}
+
+static ssize_t
+overlay_position_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return scnprintf(buf, PAGE_SIZE, "%d,%d\n", ovl->pos_x, ovl->pos_y);
+}
+
+static ssize_t
+overlay_position_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ char *endp;
+ int pos_x;
+ int pos_y;
+
+ pos_x = simple_strtol(buf, &endp, 10);
+ if (*endp != ',')
+ return -EINVAL;
+
+ pos_y = simple_strtol(endp + 1, &endp, 10);
+ if (isspace(*endp))
+ endp++;
+
+ if (endp - buf != count)
+ return -EINVAL;
+
+ if (ovl->pos_x != pos_x || ovl->pos_y != pos_y) {
+ ovl->pos_x = pos_x;
+ ovl->pos_y = pos_y;
+
+ if (ovl->enabled)
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
+ return count;
+}
+
+static ssize_t
+overlay_rop3_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->rop3);
+}
+
+static ssize_t
+overlay_rop3_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ unsigned int rop3;
+ char *endp;
+
+ rop3 = !!simple_strtoul(buf, &endp, 10);
+ if (isspace(*endp))
+ endp++;
+
+ if (endp - buf != count)
+ return -EINVAL;
+
+ if (rop3 > 255)
+ return -EINVAL;
+
+ if (ovl->rop3 != rop3) {
+ ovl->rop3 = rop3;
+
+ if (ovl->mode = LCDC_OVERLAY_ROP3 && ovl->enabled)
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
+ return count;
+}
+
+static const struct device_attribute overlay_sysfs_attrs[] = {
+ __ATTR(ovl_alpha, S_IRUGO|S_IWUSR,
+ overlay_alpha_show, overlay_alpha_store),
+ __ATTR(ovl_enabled, S_IRUGO|S_IWUSR,
+ overlay_enabled_show, overlay_enabled_store),
+ __ATTR(ovl_mode, S_IRUGO|S_IWUSR,
+ overlay_mode_show, overlay_mode_store),
+ __ATTR(ovl_position, S_IRUGO|S_IWUSR,
+ overlay_position_show, overlay_position_store),
+ __ATTR(ovl_rop3, S_IRUGO|S_IWUSR,
+ overlay_rop3_show, overlay_rop3_store),
+};
+
+static const struct fb_fix_screeninfo sh_mobile_lcdc_overlay_fix = {
+ .id = "SH Mobile LCDC",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .accel = FB_ACCEL_NONE,
+ .xpanstep = 0,
+ .ypanstep = 1,
+ .ywrapstep = 0,
+ .capabilities = FB_CAP_FOURCC,
+};
+
+static int sh_mobile_lcdc_overlay_pan(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ unsigned long base_addr_y;
+ unsigned long base_addr_c;
+ unsigned long pan_offset;
+ unsigned long c_offset;
+
+ if (!ovl->format->yuv)
+ pan_offset = var->yoffset * ovl->pitch
+ + var->xoffset * (ovl->format->bpp / 8);
+ else
+ pan_offset = var->yoffset * ovl->pitch + var->xoffset;
+
+ if (pan_offset = ovl->pan_offset)
+ return 0; /* No change, do nothing */
+
+ /* Set the source address for the next refresh */
+ base_addr_y = ovl->dma_handle + pan_offset;
+
+ ovl->base_addr_y = base_addr_y;
+ ovl->base_addr_c = base_addr_y;
+
+ if (ovl->format->yuv) {
+ /* Set Y offset */
+ c_offset = var->yoffset * ovl->pitch
+ * (ovl->format->bpp - 8) / 8;
+ base_addr_c = ovl->dma_handle
+ + ovl->xres * ovl->yres_virtual
+ + c_offset;
+ /* Set X offset */
+ if (ovl->format->fourcc = V4L2_PIX_FMT_NV24)
+ base_addr_c += 2 * var->xoffset;
+ else
+ base_addr_c += var->xoffset;
+
+ ovl->base_addr_c = base_addr_c;
+ }
+
+ lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y);
+ lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c);
+
+ ovl->pan_offset = pan_offset;
+
+ return 0;
+}
+
+static int sh_mobile_lcdc_overlay_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:
+ return sh_mobile_lcdc_wait_for_vsync(ovl->channel);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int sh_mobile_lcdc_overlay_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ return __sh_mobile_lcdc_check_var(var, info);
+}
+
+static int sh_mobile_lcdc_overlay_set_par(struct fb_info *info)
+{
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ ovl->format + sh_mobile_format_info(sh_mobile_format_fourcc(&info->var));
+
+ ovl->xres = info->var.xres;
+ ovl->xres_virtual = info->var.xres_virtual;
+ ovl->yres = info->var.yres;
+ ovl->yres_virtual = info->var.yres_virtual;
+
+ if (ovl->format->yuv)
+ ovl->pitch = info->var.xres;
+ else
+ ovl->pitch = info->var.xres * ovl->format->bpp / 8;
+
+ sh_mobile_lcdc_overlay_setup(ovl);
+
+ info->fix.line_length = ovl->pitch;
+
+ if (sh_mobile_format_is_fourcc(&info->var)) {
+ info->fix.type = FB_TYPE_FOURCC;
+ info->fix.visual = FB_VISUAL_FOURCC;
+ } else {
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ }
+
+ return 0;
+}
+
+/* Overlay blanking. Disable the overlay when blanked. */
+static int sh_mobile_lcdc_overlay_blank(int blank, struct fb_info *info)
+{
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ printk(KERN_INFO "%s(%u)\n", __func__, blank);
+ ovl->enabled = !blank;
+
+ return 0;
+}
+
+static struct fb_ops sh_mobile_lcdc_overlay_ops = {
+ .owner = THIS_MODULE,
+ .fb_read = fb_sys_read,
+ .fb_write = fb_sys_write,
+ .fb_fillrect = sys_fillrect,
+ .fb_copyarea = sys_copyarea,
+ .fb_imageblit = sys_imageblit,
+ .fb_blank = sh_mobile_lcdc_overlay_blank,
+ .fb_pan_display = sh_mobile_lcdc_overlay_pan,
+ .fb_ioctl = sh_mobile_lcdc_overlay_ioctl,
+ .fb_check_var = sh_mobile_lcdc_overlay_check_var,
+ .fb_set_par = sh_mobile_lcdc_overlay_set_par,
+};
+
+static void
+sh_mobile_lcdc_overlay_fb_unregister(struct sh_mobile_lcdc_overlay *ovl)
+{
+ struct fb_info *info = ovl->info;
+
+ if (info = NULL || info->dev = NULL)
+ return;
+
+ unregister_framebuffer(ovl->info);
+}
+
+static int __devinit
+sh_mobile_lcdc_overlay_fb_register(struct sh_mobile_lcdc_overlay *ovl)
+{
+ struct sh_mobile_lcdc_priv *lcdc = ovl->channel->lcdc;
+ struct fb_info *info = ovl->info;
+ unsigned int i;
+ int ret;
+
+ if (info = NULL)
+ return 0;
+
+ ret = register_framebuffer(info);
+ if (ret < 0)
+ return ret;
+
+ dev_info(lcdc->dev, "registered %s/overlay %u as %dx%d %dbpp.\n",
+ dev_name(lcdc->dev), ovl->index, info->var.xres,
+ info->var.yres, info->var.bits_per_pixel);
+
+ for (i = 0; i < ARRAY_SIZE(overlay_sysfs_attrs); ++i) {
+ ret = device_create_file(info->dev, &overlay_sysfs_attrs[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void
+sh_mobile_lcdc_overlay_fb_cleanup(struct sh_mobile_lcdc_overlay *ovl)
+{
+ struct fb_info *info = ovl->info;
+
+ if (info = NULL || info->device = NULL)
+ return;
+
+ framebuffer_release(info);
+}
+
+static int __devinit
+sh_mobile_lcdc_overlay_fb_init(struct sh_mobile_lcdc_overlay *ovl)
+{
+ struct sh_mobile_lcdc_priv *priv = ovl->channel->lcdc;
+ struct fb_var_screeninfo *var;
+ struct fb_info *info;
+
+ /* Allocate and initialize the frame buffer device. */
+ info = framebuffer_alloc(0, priv->dev);
+ if (info = NULL) {
+ dev_err(priv->dev, "unable to allocate fb_info\n");
+ return -ENOMEM;
+ }
+
+ ovl->info = info;
+
+ info->flags = FBINFO_FLAG_DEFAULT;
+ info->fbops = &sh_mobile_lcdc_overlay_ops;
+ info->device = priv->dev;
+ info->screen_base = ovl->fb_mem;
+ info->par = ovl;
+
+ /* Initialize fixed screen information. Restrict pan to 2 lines steps
+ * for NV12 and NV21.
+ */
+ info->fix = sh_mobile_lcdc_overlay_fix;
+ snprintf(info->fix.id, sizeof(info->fix.id),
+ "SH Mobile LCDC Overlay %u", ovl->index);
+ info->fix.smem_start = ovl->dma_handle;
+ info->fix.smem_len = ovl->fb_size;
+ info->fix.line_length = ovl->pitch;
+
+ if (ovl->format->yuv)
+ info->fix.visual = FB_VISUAL_FOURCC;
+ else
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+
+ if (ovl->format->fourcc = V4L2_PIX_FMT_NV12 ||
+ ovl->format->fourcc = V4L2_PIX_FMT_NV21)
+ info->fix.ypanstep = 2;
+
+ /* Initialize variable screen information. */
+ var = &info->var;
+ memset(var, 0, sizeof(*var));
+ var->xres = ovl->xres;
+ var->yres = ovl->yres;
+ var->xres_virtual = ovl->xres_virtual;
+ var->yres_virtual = ovl->yres_virtual;
+ var->activate = FB_ACTIVATE_NOW;
+
+ /* Use the legacy API by default for RGB formats, and the FOURCC API
+ * for YUV formats.
+ */
+ if (!ovl->format->yuv)
+ var->bits_per_pixel = ovl->format->bpp;
+ else
+ var->grayscale = ovl->format->fourcc;
+
+ return sh_mobile_lcdc_overlay_check_var(var, info);
+}
+
/* -----------------------------------------------------------------------------
- * Frame buffer operations
+ * Frame buffer operations - main frame buffer
*/
static int sh_mobile_lcdc_setcolreg(u_int regno,
@@ -1201,9 +1999,7 @@ static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
unsigned int best_xres = 0;
unsigned int best_yres = 0;
unsigned int i;
-
- if (var->xres > MAX_XRES || var->yres > MAX_YRES)
- return -EINVAL;
+ int ret;
/* If board code provides us with a list of available modes, make sure
* we use one of them. Find the mode closest to the requested one. The
@@ -1238,73 +2034,9 @@ static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
var->yres = best_yres;
}
- /* Make sure the virtual resolution is at least as big as the visible
- * resolution.
- */
- if (var->xres_virtual < var->xres)
- var->xres_virtual = var->xres;
- if (var->yres_virtual < var->yres)
- var->yres_virtual = var->yres;
-
- if (sh_mobile_format_is_fourcc(var)) {
- const struct sh_mobile_lcdc_format_info *format;
-
- format = sh_mobile_format_info(var->grayscale);
- if (format = NULL)
- return -EINVAL;
- var->bits_per_pixel = format->bpp;
-
- /* Default to RGB and JPEG color-spaces for RGB and YUV formats
- * respectively.
- */
- if (!format->yuv)
- var->colorspace = V4L2_COLORSPACE_SRGB;
- else if (var->colorspace != V4L2_COLORSPACE_REC709)
- var->colorspace = V4L2_COLORSPACE_JPEG;
- } else {
- if (var->bits_per_pixel <= 16) { /* RGB 565 */
- var->bits_per_pixel = 16;
- var->red.offset = 11;
- var->red.length = 5;
- var->green.offset = 5;
- var->green.length = 6;
- var->blue.offset = 0;
- var->blue.length = 5;
- var->transp.offset = 0;
- var->transp.length = 0;
- } else if (var->bits_per_pixel <= 24) { /* RGB 888 */
- var->bits_per_pixel = 24;
- var->red.offset = 16;
- var->red.length = 8;
- var->green.offset = 8;
- var->green.length = 8;
- var->blue.offset = 0;
- var->blue.length = 8;
- var->transp.offset = 0;
- var->transp.length = 0;
- } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */
- var->bits_per_pixel = 32;
- var->red.offset = 16;
- var->red.length = 8;
- var->green.offset = 8;
- var->green.length = 8;
- var->blue.offset = 0;
- var->blue.length = 8;
- var->transp.offset = 24;
- var->transp.length = 8;
- } else
- return -EINVAL;
-
- var->red.msb_right = 0;
- var->green.msb_right = 0;
- var->blue.msb_right = 0;
- var->transp.msb_right = 0;
- }
-
- /* Make sure we don't exceed our allocated memory. */
- if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 >
- info->fix.smem_len)
- return -EINVAL;
+ ret = __sh_mobile_lcdc_check_var(var, info);
+ if (ret < 0)
+ return ret;
/* only accept the forced_fourcc for dual channel configurations */
if (p->forced_fourcc &&
@@ -1713,15 +2445,20 @@ static const struct fb_videomode default_720p __devinitconst = {
static int sh_mobile_lcdc_remove(struct platform_device *pdev)
{
struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
- int i;
+ unsigned int i;
fb_unregister_client(&priv->notifier);
+ for (i = 0; i < ARRAY_SIZE(priv->overlays); i++)
+ sh_mobile_lcdc_overlay_fb_unregister(&priv->overlays[i]);
for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]);
sh_mobile_lcdc_stop(priv);
+ for (i = 0; i < ARRAY_SIZE(priv->overlays); i++)
+ sh_mobile_lcdc_overlay_fb_cleanup(&priv->overlays[i]);
+
for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
struct sh_mobile_lcdc_chan *ch = &priv->ch[i];
@@ -1797,6 +2534,61 @@ static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *
}
static int __devinit
+sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv,
+ struct sh_mobile_lcdc_overlay *ovl)
+{
+ const struct sh_mobile_lcdc_format_info *format;
+ int ret;
+
+ if (ovl->cfg->fourcc = 0)
+ return 0;
+
+ /* Validate the format. */
+ format = sh_mobile_format_info(ovl->cfg->fourcc);
+ if (format = NULL) {
+ dev_err(priv->dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc);
+ return -EINVAL;
+ }
+
+ ovl->enabled = false;
+ ovl->mode = LCDC_OVERLAY_BLEND;
+ ovl->alpha = 255;
+ ovl->rop3 = 0;
+ ovl->pos_x = 0;
+ ovl->pos_y = 0;
+
+ /* The default Y virtual resolution is twice the panel size to allow for
+ * double-buffering.
+ */
+ ovl->format = format;
+ ovl->xres = ovl->cfg->max_xres;
+ ovl->xres_virtual = ovl->xres;
+ ovl->yres = ovl->cfg->max_yres;
+ ovl->yres_virtual = ovl->yres * 2;
+
+ if (!format->yuv)
+ ovl->pitch = ovl->xres * format->bpp / 8;
+ else
+ ovl->pitch = ovl->xres;
+
+ /* Allocate frame buffer memory. */
+ ovl->fb_size = ovl->cfg->max_xres * ovl->cfg->max_yres
+ * format->bpp / 8 * 2;
+ ovl->fb_mem = dma_alloc_coherent(priv->dev, ovl->fb_size,
+ &ovl->dma_handle, GFP_KERNEL);
+ if (!ovl->fb_mem) {
+ dev_err(priv->dev, "unable to allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ret = sh_mobile_lcdc_overlay_fb_init(ovl);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int __devinit
sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
struct sh_mobile_lcdc_chan *ch)
{
@@ -2004,6 +2796,17 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
+ for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) {
+ struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i];
+
+ ovl->cfg = &pdata->overlays[i];
+ ovl->channel = &priv->ch[0];
+
+ error = sh_mobile_lcdc_overlay_init(priv, ovl);
+ if (error)
+ goto err1;
+ }
+
error = sh_mobile_lcdc_start(priv);
if (error) {
dev_err(&pdev->dev, "unable to start hardware\n");
@@ -2018,6 +2821,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
+ for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) {
+ struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i];
+
+ error = sh_mobile_lcdc_overlay_fb_register(ovl);
+ if (error)
+ goto err1;
+ }
+
/* Failure ignored */
priv->notifier.notifier_call = sh_mobile_lcdc_notify;
fb_register_client(&priv->notifier);
diff --git a/include/video/sh_mobile_lcdc.h b/include/video/sh_mobile_lcdc.h
index 7571b27..ff43ffc 100644
--- a/include/video/sh_mobile_lcdc.h
+++ b/include/video/sh_mobile_lcdc.h
@@ -166,6 +166,12 @@ struct sh_mobile_lcdc_bl_info {
int (*get_brightness)(void);
};
+struct sh_mobile_lcdc_overlay_cfg {
+ int fourcc;
+ unsigned int max_xres;
+ unsigned int max_yres;
+};
+
struct sh_mobile_lcdc_chan_cfg {
int chan;
int fourcc;
@@ -186,6 +192,7 @@ struct sh_mobile_lcdc_chan_cfg {
struct sh_mobile_lcdc_info {
int clock_source;
struct sh_mobile_lcdc_chan_cfg ch[2];
+ struct sh_mobile_lcdc_overlay_cfg overlays[4];
struct sh_mobile_meram_info *meram_dev;
};
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 56/57] fbdev: sh_mobile_lcdc: Rename fb operation handlers with a common prefix
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
To: linux-fbdev
Make all fb operation handlers start with sh_mobile_lcdc_ in preparation
for the multi-plane support.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/sh_mobile_lcdcfb.c | 48 +++++++++++++++++++------------------
1 files changed, 25 insertions(+), 23 deletions(-)
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index b6fb8a4..36dad2f 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -384,8 +384,8 @@ sh_mobile_lcdc_must_reconfigure(struct sh_mobile_lcdc_chan *ch,
return true;
}
-static int sh_mobile_check_var(struct fb_var_screeninfo *var,
- struct fb_info *info);
+static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info);
static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
enum sh_mobile_lcdc_entity_event event,
@@ -439,7 +439,7 @@ static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
fb_videomode_to_var(&var, mode);
var.bits_per_pixel = info->var.bits_per_pixel;
var.grayscale = info->var.grayscale;
- ret = sh_mobile_check_var(&var, info);
+ ret = sh_mobile_lcdc_check_var(&var, info);
break;
}
@@ -585,7 +585,7 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int sh_mobile_wait_for_vsync(struct sh_mobile_lcdc_chan *ch)
+static int sh_mobile_lcdc_wait_for_vsync(struct sh_mobile_lcdc_chan *ch)
{
unsigned long ldintr;
int ret;
@@ -686,7 +686,7 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
}
/*
- * __sh_mobile_lcdc_start - Configure and tart the LCDC
+ * __sh_mobile_lcdc_start - Configure and start the LCDC
* @priv: LCDC device
*
* Configure all enabled channels and start the LCDC device. All external
@@ -1034,8 +1034,8 @@ static void sh_mobile_lcdc_imageblit(struct fb_info *info,
sh_mobile_lcdc_deferred_io_touch(info);
}
-static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
- struct fb_info *info)
+static int sh_mobile_lcdc_pan(struct fb_var_screeninfo *var,
+ struct fb_info *info)
{
struct sh_mobile_lcdc_chan *ch = info->par;
struct sh_mobile_lcdc_priv *priv = ch->lcdc;
@@ -1098,14 +1098,15 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
return 0;
}
-static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
- unsigned long arg)
+static int sh_mobile_lcdc_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
{
+ struct sh_mobile_lcdc_chan *ch = info->par;
int retval;
switch (cmd) {
case FBIO_WAITFORVSYNC:
- retval = sh_mobile_wait_for_vsync(info->par);
+ retval = sh_mobile_lcdc_wait_for_vsync(ch);
break;
default:
@@ -1157,7 +1158,7 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
* Locking: both .fb_release() and .fb_open() are called with info->lock held if
* user = 1, or with console sem held, if user = 0.
*/
-static int sh_mobile_release(struct fb_info *info, int user)
+static int sh_mobile_lcdc_release(struct fb_info *info, int user)
{
struct sh_mobile_lcdc_chan *ch = info->par;
@@ -1178,7 +1179,7 @@ static int sh_mobile_release(struct fb_info *info, int user)
return 0;
}
-static int sh_mobile_open(struct fb_info *info, int user)
+static int sh_mobile_lcdc_open(struct fb_info *info, int user)
{
struct sh_mobile_lcdc_chan *ch = info->par;
@@ -1191,7 +1192,8 @@ static int sh_mobile_open(struct fb_info *info, int user)
return 0;
}
-static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
{
struct sh_mobile_lcdc_chan *ch = info->par;
struct sh_mobile_lcdc_priv *p = ch->lcdc;
@@ -1312,7 +1314,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
return 0;
}
-static int sh_mobile_set_par(struct fb_info *info)
+static int sh_mobile_lcdc_set_par(struct fb_info *info)
{
struct sh_mobile_lcdc_chan *ch = info->par;
int ret;
@@ -1382,8 +1384,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info)
* mode will reenable the clocks and update the screen in time,
* so it does not need this. */
if (!info->fbdefio) {
- sh_mobile_wait_for_vsync(ch);
- sh_mobile_wait_for_vsync(ch);
+ sh_mobile_lcdc_wait_for_vsync(ch);
+ sh_mobile_lcdc_wait_for_vsync(ch);
}
sh_mobile_lcdc_clk_off(p);
}
@@ -1401,12 +1403,12 @@ static struct fb_ops sh_mobile_lcdc_ops = {
.fb_copyarea = sh_mobile_lcdc_copyarea,
.fb_imageblit = sh_mobile_lcdc_imageblit,
.fb_blank = sh_mobile_lcdc_blank,
- .fb_pan_display = sh_mobile_fb_pan_display,
- .fb_ioctl = sh_mobile_ioctl,
- .fb_open = sh_mobile_open,
- .fb_release = sh_mobile_release,
- .fb_check_var = sh_mobile_check_var,
- .fb_set_par = sh_mobile_set_par,
+ .fb_pan_display = sh_mobile_lcdc_pan,
+ .fb_ioctl = sh_mobile_lcdc_ioctl,
+ .fb_open = sh_mobile_lcdc_open,
+ .fb_release = sh_mobile_lcdc_release,
+ .fb_check_var = sh_mobile_lcdc_check_var,
+ .fb_set_par = sh_mobile_lcdc_set_par,
};
static void
@@ -1536,7 +1538,7 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
else
var->grayscale = ch->format->fourcc;
- ret = sh_mobile_check_var(var, info);
+ ret = sh_mobile_lcdc_check_var(var, info);
if (ret)
return ret;
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 55/57] fbdev: sh_mobile_lcdc: Constify sh_mobile_lcdc_fix structure
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
To: linux-fbdev
The structure is only read, make it const.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/sh_mobile_lcdcfb.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 7a0b301..b6fb8a4 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -1002,7 +1002,7 @@ static int sh_mobile_lcdc_setcolreg(u_int regno,
return 0;
}
-static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
+static const struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
.id = "SH Mobile LCDC",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR,
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 54/57] fbdev: sh_mobile_meram: Remove unneeded sanity checks
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
To: linux-fbdev
The meram_register(), meram_unregister() and meram_update() operations
check that the pointers they get from the caller are not NULL. Those
checks can be remove, as the caller already ensures that the pointers
are valid.
The platform sanity checks can also be removed, as the operations can't
be accessed without valid platform data anyway.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/sh_mobile_lcdcfb.c | 5 +----
drivers/video/sh_mobile_meram.c | 32 ++++++--------------------------
include/video/sh_mobile_meram.h | 15 +++++++--------
3 files changed, 14 insertions(+), 38 deletions(-)
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 4b54cd5..7a0b301 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -1072,14 +1072,11 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
if (ch->meram) {
struct sh_mobile_meram_info *mdev;
- int ret;
mdev = priv->meram_dev;
- ret = mdev->ops->meram_update(mdev, ch->meram,
+ mdev->ops->meram_update(mdev, ch->meram,
base_addr_y, base_addr_c,
&base_addr_y, &base_addr_c);
- if (ret)
- return ret;
}
ch->base_addr_y = base_addr_y;
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index 2ce0d8f..d9f7a44 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -451,21 +451,15 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
unsigned int *pitch)
{
struct sh_mobile_meram_fb_cache *cache;
- struct sh_mobile_meram_priv *priv;
- struct platform_device *pdev;
+ struct sh_mobile_meram_priv *priv = pdata->priv;
+ struct platform_device *pdev = pdata->pdev;
unsigned int out_pitch;
- if (!pdata || !pdata->priv || !pdata->pdev || !cfg)
- return ERR_PTR(-EINVAL);
-
if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
pixelformat != SH_MOBILE_MERAM_PF_RGB)
return ERR_PTR(-EINVAL);
- priv = pdata->priv;
- pdev = pdata->pdev;
-
dev_dbg(&pdev->dev, "registering %dx%d (%s)", xres, yres,
!pixelformat ? "yuv" : "rgb");
@@ -500,16 +494,11 @@ err:
return cache;
}
-static int
+static void
sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
{
struct sh_mobile_meram_fb_cache *cache = data;
- struct sh_mobile_meram_priv *priv;
-
- if (!pdata || !pdata->priv || !data)
- return -EINVAL;
-
- priv = pdata->priv;
+ struct sh_mobile_meram_priv *priv = pdata->priv;
mutex_lock(&priv->lock);
@@ -521,22 +510,15 @@ sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
meram_free(priv, cache);
mutex_unlock(&priv->lock);
-
- return 0;
}
-static int
+static void
sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
unsigned long base_addr_y, unsigned long base_addr_c,
unsigned long *icb_addr_y, unsigned long *icb_addr_c)
{
struct sh_mobile_meram_fb_cache *cache = data;
- struct sh_mobile_meram_priv *priv;
-
- if (!pdata || !pdata->priv || !data)
- return -EINVAL;
-
- priv = pdata->priv;
+ struct sh_mobile_meram_priv *priv = pdata->priv;
mutex_lock(&priv->lock);
@@ -544,8 +526,6 @@ sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
mutex_unlock(&priv->lock);
-
- return 0;
}
static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
diff --git a/include/video/sh_mobile_meram.h b/include/video/sh_mobile_meram.h
index 553335c..29b2fd3 100644
--- a/include/video/sh_mobile_meram.h
+++ b/include/video/sh_mobile_meram.h
@@ -49,16 +49,15 @@ struct sh_mobile_meram_ops {
unsigned int *pitch);
/* unregister usage of meram */
- int (*meram_unregister)(struct sh_mobile_meram_info *meram_dev,
- void *data);
+ void (*meram_unregister)(struct sh_mobile_meram_info *meram_dev,
+ void *data);
/* update meram settings */
- int (*meram_update)(struct sh_mobile_meram_info *meram_dev,
- void *data,
- unsigned long base_addr_y,
- unsigned long base_addr_c,
- unsigned long *icb_addr_y,
- unsigned long *icb_addr_c);
+ void (*meram_update)(struct sh_mobile_meram_info *meram_dev, void *data,
+ unsigned long base_addr_y,
+ unsigned long base_addr_c,
+ unsigned long *icb_addr_y,
+ unsigned long *icb_addr_c);
};
#endif /* __VIDEO_SH_MOBILE_MERAM_H__ */
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 53/57] fbdev: sh_mobile_meram: Don't perform update in register operation
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
To: linux-fbdev
Remove the RGB or Y/C base address update from the meram_register()
operation, as this belongs to the meram_update() operation.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/sh_mobile_lcdcfb.c | 8 +++++---
drivers/video/sh_mobile_meram.c | 15 ++-------------
include/video/sh_mobile_meram.h | 4 ----
3 files changed, 7 insertions(+), 20 deletions(-)
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 0338516..4b54cd5 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -882,11 +882,13 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
meram = mdev->ops->meram_register(mdev, ch->cfg->meram_cfg,
ch->pitch, ch->yres, pixelformat,
- ch->base_addr_y, ch->base_addr_c,
- &ch->base_addr_y, &ch->base_addr_c,
&ch->pitch);
- if (!IS_ERR(meram))
+ if (!IS_ERR(meram)) {
+ mdev->ops->meram_update(mdev, meram,
+ ch->base_addr_y, ch->base_addr_c,
+ &ch->base_addr_y, &ch->base_addr_c);
ch->meram = meram;
+ }
}
/* Start the LCDC. */
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index 085c49a..2ce0d8f 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -448,10 +448,6 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
const struct sh_mobile_meram_cfg *cfg,
unsigned int xres, unsigned int yres,
unsigned int pixelformat,
- unsigned long base_addr_y,
- unsigned long base_addr_c,
- unsigned long *icb_addr_y,
- unsigned long *icb_addr_c,
unsigned int *pitch)
{
struct sh_mobile_meram_fb_cache *cache;
@@ -470,9 +466,8 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
priv = pdata->priv;
pdev = pdata->pdev;
- dev_dbg(&pdev->dev, "registering %dx%d (%s) (y=%08lx, c=%08lx)",
- xres, yres, (!pixelformat) ? "yuv" : "rgb",
- base_addr_y, base_addr_c);
+ dev_dbg(&pdev->dev, "registering %dx%d (%s)", xres, yres,
+ !pixelformat ? "yuv" : "rgb");
/* we can't handle wider than 8192px */
if (xres > 8192) {
@@ -500,12 +495,6 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2,
&out_pitch);
- meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
- meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
-
- dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx",
- *icb_addr_y, *icb_addr_c);
-
err:
mutex_unlock(&priv->lock);
return cache;
diff --git a/include/video/sh_mobile_meram.h b/include/video/sh_mobile_meram.h
index 8979607..553335c 100644
--- a/include/video/sh_mobile_meram.h
+++ b/include/video/sh_mobile_meram.h
@@ -46,10 +46,6 @@ struct sh_mobile_meram_ops {
const struct sh_mobile_meram_cfg *cfg,
unsigned int xres, unsigned int yres,
unsigned int pixelformat,
- unsigned long base_addr_y,
- unsigned long base_addr_c,
- unsigned long *icb_addr_y,
- unsigned long *icb_addr_c,
unsigned int *pitch);
/* unregister usage of meram */
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 52/57] arm: mach-shmobile: Constify sh_mobile_meram_cfg structures
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
To: linux-fbdev
The structures, passed to the sh_mobile_lcdcfb driver through platform
data, are read only by the driver. Make them const.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
arch/arm/mach-shmobile/board-ap4evb.c | 5 +++--
arch/arm/mach-shmobile/board-mackerel.c | 4 ++--
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 9d6ad2d..28a2301 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -587,7 +587,8 @@ static const struct fb_videomode ap4evb_lcdc_modes[] = {
#endif
},
};
-static struct sh_mobile_meram_cfg lcd_meram_cfg = {
+
+static const struct sh_mobile_meram_cfg lcd_meram_cfg = {
.icb[0] = {
.meram_size = 0x40,
},
@@ -864,7 +865,7 @@ static long ap4evb_clk_optimize(unsigned long target, unsigned long *best_freq,
return error;
}
-static struct sh_mobile_meram_cfg hdmi_meram_cfg = {
+static const struct sh_mobile_meram_cfg hdmi_meram_cfg = {
.icb[0] = {
.meram_size = 0x100,
},
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c
index 4acd748..ca084dd 100644
--- a/arch/arm/mach-shmobile/board-mackerel.c
+++ b/arch/arm/mach-shmobile/board-mackerel.c
@@ -374,7 +374,7 @@ static int mackerel_get_brightness(void)
return gpio_get_value(GPIO_PORT31);
}
-static struct sh_mobile_meram_cfg lcd_meram_cfg = {
+static const struct sh_mobile_meram_cfg lcd_meram_cfg = {
.icb[0] = {
.meram_size = 0x40,
},
@@ -460,7 +460,7 @@ static struct platform_device hdmi_device = {
},
};
-static struct sh_mobile_meram_cfg hdmi_meram_cfg = {
+static const struct sh_mobile_meram_cfg hdmi_meram_cfg = {
.icb[0] = {
.meram_size = 0x100,
},
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 51/57] fbdev: sh_mobile_lcdc: Don't store copy of platform data
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
To: linux-fbdev
Instead of copying the whole platform data structure to struct
sh_mobile_lcdc_chan, store a const pointer to the channel platform data.
MERAM configuration information needs to be changed at runtime, so copy
it to struct sh_mobile_lcdc_chan.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/sh_mobile_lcdcfb.c | 71 ++++++++++++++++++-------------------
drivers/video/sh_mobile_lcdcfb.h | 3 +-
include/video/sh_mobile_lcdc.h | 2 +-
3 files changed, 38 insertions(+), 38 deletions(-)
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index d0c9026..0338516 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -116,7 +116,7 @@ static bool banked(int reg_nr)
static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan)
{
- return chan->cfg.chan = LCDC_CHAN_SUBLCD;
+ return chan->cfg->chan = LCDC_CHAN_SUBLCD;
}
static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
@@ -289,7 +289,7 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
struct list_head *pagelist)
{
struct sh_mobile_lcdc_chan *ch = info->par;
- struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg.panel_cfg;
+ const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
/* enable clocks before accessing hardware */
sh_mobile_lcdc_clk_on(ch->lcdc);
@@ -336,7 +336,7 @@ static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
static void sh_mobile_lcdc_display_on(struct sh_mobile_lcdc_chan *ch)
{
- struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg.panel_cfg;
+ const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
if (ch->tx_dev) {
int ret;
@@ -356,7 +356,7 @@ static void sh_mobile_lcdc_display_on(struct sh_mobile_lcdc_chan *ch)
static void sh_mobile_lcdc_display_off(struct sh_mobile_lcdc_chan *ch)
{
- struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg.panel_cfg;
+ const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
if (panel->display_off)
panel->display_off();
@@ -644,16 +644,16 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
tmp = ch->ldmt1r_value;
tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL;
tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0;
+ tmp |= (ch->cfg->flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0;
+ tmp |= (ch->cfg->flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0;
+ tmp |= (ch->cfg->flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0;
+ tmp |= (ch->cfg->flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0;
+ tmp |= (ch->cfg->flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0;
lcdc_write_chan(ch, LDMT1R, tmp);
/* setup SYS bus */
- lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
- lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
+ lcdc_write_chan(ch, LDMT2R, ch->cfg->sys_bus_cfg.ldmt2r);
+ lcdc_write_chan(ch, LDMT3R, ch->cfg->sys_bus_cfg.ldmt3r);
/* horizontal configuration */
h_total = mode->xres + mode->hsync_len + mode->left_margin
@@ -717,7 +717,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
/* Power supply */
lcdc_write_chan(ch, LDPMR, 0);
- m = ch->cfg.clock_divider;
+ m = ch->cfg->clock_divider;
if (!m)
continue;
@@ -768,7 +768,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
* continuous read mode.
*/
if (ch->ldmt1r_value & LDMT1R_IFM &&
- ch->cfg.sys_bus_cfg.deferred_io_msec) {
+ ch->cfg->sys_bus_cfg.deferred_io_msec) {
lcdc_write_chan(ch, LDSM1R, LDSM1R_OS);
lcdc_write(priv, _LDINTR, LDINTR_FE);
} else {
@@ -822,13 +822,13 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0);
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
- struct sh_mobile_lcdc_panel_cfg *panel;
+ const struct sh_mobile_lcdc_panel_cfg *panel;
ch = &priv->ch[k];
if (!ch->enabled)
continue;
- panel = &ch->cfg.panel_cfg;
+ panel = &ch->cfg->panel_cfg;
if (panel->setup_sys) {
ret = panel->setup_sys(ch, &sh_mobile_lcdc_sys_bus_ops);
if (ret)
@@ -838,7 +838,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
/* Compute frame buffer base address and pitch for each channel. */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
- struct sh_mobile_meram_cfg *cfg;
int pixelformat;
void *meram;
@@ -850,8 +849,8 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
ch->base_addr_c = ch->base_addr_y + ch->xres * ch->yres_virtual;
/* Enable MERAM if possible. */
- cfg = ch->cfg.meram_cfg;
- if (mdev = NULL || mdev->ops = NULL || cfg = NULL)
+ if (mdev = NULL || mdev->ops = NULL ||
+ ch->cfg->meram_cfg = NULL)
continue;
/* we need to de-init configured ICBs before we can
@@ -881,8 +880,8 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
break;
}
- meram = mdev->ops->meram_register(mdev, cfg, ch->pitch,
- ch->yres, pixelformat,
+ meram = mdev->ops->meram_register(mdev, ch->cfg->meram_cfg,
+ ch->pitch, ch->yres, pixelformat,
ch->base_addr_y, ch->base_addr_c,
&ch->base_addr_y, &ch->base_addr_c,
&ch->pitch);
@@ -901,7 +900,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
if (!ch->enabled)
continue;
- tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
+ tmp = ch->cfg->sys_bus_cfg.deferred_io_msec;
if (ch->ldmt1r_value & LDMT1R_IFM && tmp) {
ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
ch->defio.delay = msecs_to_jiffies(tmp);
@@ -1210,8 +1209,8 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
* distance between two modes is defined as the size of the
* non-overlapping parts of the two rectangles.
*/
- for (i = 0; i < ch->cfg.num_modes; ++i) {
- const struct fb_videomode *mode = &ch->cfg.lcd_modes[i];
+ for (i = 0; i < ch->cfg->num_modes; ++i) {
+ const struct fb_videomode *mode = &ch->cfg->lcd_modes[i];
unsigned int dist;
/* We can only round up. */
@@ -1230,7 +1229,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
}
/* If no available mode can be used, return an error. */
- if (ch->cfg.num_modes != 0) {
+ if (ch->cfg->num_modes != 0) {
if (best_dist = (unsigned int)-1)
return -EINVAL;
@@ -1440,7 +1439,7 @@ sh_mobile_lcdc_channel_fb_register(struct sh_mobile_lcdc_chan *ch)
return ret;
dev_info(ch->lcdc->dev, "registered %s/%s as %dx%d %dbpp.\n",
- dev_name(ch->lcdc->dev), (ch->cfg.chan = LCDC_CHAN_MAINLCD) ?
+ dev_name(ch->lcdc->dev), (ch->cfg->chan = LCDC_CHAN_MAINLCD) ?
"mainlcd" : "sublcd", info->var.xres, info->var.yres,
info->var.bits_per_pixel);
@@ -1525,8 +1524,8 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
*/
var = &info->var;
fb_videomode_to_var(var, mode);
- var->width = ch->cfg.panel_cfg.width;
- var->height = ch->cfg.panel_cfg.height;
+ var->width = ch->cfg->panel_cfg.width;
+ var->height = ch->cfg->panel_cfg.height;
var->yres_virtual = var->yres * 2;
var->activate = FB_ACTIVATE_NOW;
@@ -1558,14 +1557,14 @@ static int sh_mobile_lcdc_update_bl(struct backlight_device *bdev)
bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
brightness = 0;
- return ch->cfg.bl_info.set_brightness(brightness);
+ return ch->cfg->bl_info.set_brightness(brightness);
}
static int sh_mobile_lcdc_get_brightness(struct backlight_device *bdev)
{
struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev);
- return ch->cfg.bl_info.get_brightness();
+ return ch->cfg->bl_info.get_brightness();
}
static int sh_mobile_lcdc_check_fb(struct backlight_device *bdev,
@@ -1586,7 +1585,7 @@ static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent,
{
struct backlight_device *bl;
- bl = backlight_device_register(ch->cfg.bl_info.name, parent, ch,
+ bl = backlight_device_register(ch->cfg->bl_info.name, parent, ch,
&sh_mobile_lcdc_bl_ops, NULL);
if (IS_ERR(bl)) {
dev_err(parent, "unable to register backlight device: %ld\n",
@@ -1594,7 +1593,7 @@ static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent,
return NULL;
}
- bl->props.max_brightness = ch->cfg.bl_info.max_brightness;
+ bl->props.max_brightness = ch->cfg->bl_info.max_brightness;
bl->props.brightness = bl->props.max_brightness;
backlight_update_status(bl);
@@ -1727,7 +1726,7 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
if (ch->tx_dev) {
ch->tx_dev->lcdc = NULL;
- module_put(ch->cfg.tx_dev->dev.driver->owner);
+ module_put(ch->cfg->tx_dev->dev.driver->owner);
}
sh_mobile_lcdc_channel_fb_cleanup(ch);
@@ -1758,7 +1757,7 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
{
- int interface_type = ch->cfg.interface_type;
+ int interface_type = ch->cfg->interface_type;
switch (interface_type) {
case RGB8:
@@ -1801,7 +1800,7 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
struct sh_mobile_lcdc_chan *ch)
{
const struct sh_mobile_lcdc_format_info *format;
- struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg;
+ const struct sh_mobile_lcdc_chan_cfg *cfg = ch->cfg;
const struct fb_videomode *max_mode;
const struct fb_videomode *mode;
unsigned int num_modes;
@@ -1944,7 +1943,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
struct sh_mobile_lcdc_chan *ch = priv->ch + num_channels;
ch->lcdc = priv;
- memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i]));
+ ch->cfg = &pdata->ch[i];
error = sh_mobile_lcdc_check_interface(ch);
if (error) {
@@ -1956,7 +1955,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
ch->pan_offset = 0;
/* probe the backlight is there is one defined */
- if (ch->cfg.bl_info.max_brightness)
+ if (ch->cfg->bl_info.max_brightness)
ch->bl = sh_mobile_lcdc_bl_probe(&pdev->dev, ch);
switch (pdata->ch[i].chan) {
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h
index bf1707c..da1c26e 100644
--- a/drivers/video/sh_mobile_lcdcfb.h
+++ b/drivers/video/sh_mobile_lcdcfb.h
@@ -54,7 +54,7 @@ struct sh_mobile_lcdc_entity {
struct sh_mobile_lcdc_chan {
struct sh_mobile_lcdc_priv *lcdc;
struct sh_mobile_lcdc_entity *tx_dev;
- struct sh_mobile_lcdc_chan_cfg cfg;
+ const struct sh_mobile_lcdc_chan_cfg *cfg;
unsigned long *reg_offs;
unsigned long ldmt1r_value;
@@ -66,6 +66,7 @@ struct sh_mobile_lcdc_chan {
void *fb_mem;
unsigned long fb_size;
+
dma_addr_t dma_handle;
unsigned long pan_offset;
diff --git a/include/video/sh_mobile_lcdc.h b/include/video/sh_mobile_lcdc.h
index 484b0a2..7571b27 100644
--- a/include/video/sh_mobile_lcdc.h
+++ b/include/video/sh_mobile_lcdc.h
@@ -178,7 +178,7 @@ struct sh_mobile_lcdc_chan_cfg {
struct sh_mobile_lcdc_panel_cfg panel_cfg;
struct sh_mobile_lcdc_bl_info bl_info;
struct sh_mobile_lcdc_sys_bus_cfg sys_bus_cfg; /* only for SYSn I/F */
- struct sh_mobile_meram_cfg *meram_cfg;
+ const struct sh_mobile_meram_cfg *meram_cfg;
struct platform_device *tx_dev; /* HDMI/DSI transmitter device */
};
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 50/57] fbdev: sh_mobile_meram: Remove unused sh_mobile_meram_icb_cfg fields
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
To: linux-fbdev
The marker_icb and cache_icb fields are not used anymore, remove them.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
include/video/sh_mobile_meram.h | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/include/video/sh_mobile_meram.h b/include/video/sh_mobile_meram.h
index e0f650c..8979607 100644
--- a/include/video/sh_mobile_meram.h
+++ b/include/video/sh_mobile_meram.h
@@ -31,8 +31,6 @@ struct sh_mobile_meram_info {
/* icb config */
struct sh_mobile_meram_icb_cfg {
- unsigned int marker_icb; /* ICB # for Marker ICB */
- unsigned int cache_icb; /* ICB # for Cache ICB */
unsigned int meram_size; /* MERAM Buffer Size to use */
};
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 49/57] arm: mach-shmobile: Don't set MERAM ICB numbers in platform data
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
To: linux-fbdev
The marker and cache ICBs are now allocated automatically, there's no
need to specify them manually anymore.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
arch/arm/mach-shmobile/board-ap4evb.c | 8 --------
arch/arm/mach-shmobile/board-mackerel.c | 8 --------
2 files changed, 0 insertions(+), 16 deletions(-)
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index f215818..9d6ad2d 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -589,13 +589,9 @@ static const struct fb_videomode ap4evb_lcdc_modes[] = {
};
static struct sh_mobile_meram_cfg lcd_meram_cfg = {
.icb[0] = {
- .marker_icb = 28,
- .cache_icb = 24,
.meram_size = 0x40,
},
.icb[1] = {
- .marker_icb = 29,
- .cache_icb = 25,
.meram_size = 0x40,
},
};
@@ -870,13 +866,9 @@ static long ap4evb_clk_optimize(unsigned long target, unsigned long *best_freq,
static struct sh_mobile_meram_cfg hdmi_meram_cfg = {
.icb[0] = {
- .marker_icb = 30,
- .cache_icb = 26,
.meram_size = 0x100,
},
.icb[1] = {
- .marker_icb = 31,
- .cache_icb = 27,
.meram_size = 0x100,
},
};
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c
index 3b85509..4acd748 100644
--- a/arch/arm/mach-shmobile/board-mackerel.c
+++ b/arch/arm/mach-shmobile/board-mackerel.c
@@ -376,13 +376,9 @@ static int mackerel_get_brightness(void)
static struct sh_mobile_meram_cfg lcd_meram_cfg = {
.icb[0] = {
- .marker_icb = 28,
- .cache_icb = 24,
.meram_size = 0x40,
},
.icb[1] = {
- .marker_icb = 29,
- .cache_icb = 25,
.meram_size = 0x40,
},
};
@@ -466,13 +462,9 @@ static struct platform_device hdmi_device = {
static struct sh_mobile_meram_cfg hdmi_meram_cfg = {
.icb[0] = {
- .marker_icb = 30,
- .cache_icb = 26,
.meram_size = 0x100,
},
.icb[1] = {
- .marker_icb = 31,
- .cache_icb = 27,
.meram_size = 0x100,
},
};
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 48/57] fbdev: sh_mobile_meram: Allocate ICBs automatically
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
To: linux-fbdev
Instead of manually specifying the ICBs to use in platform data,
allocate them automatically at runtime. The range of reserved ICBs (for
instance to be used through UIO), if any, is passed in the platform data
reserved_icbs field as a bitmask.
The MERAM registration function now returns a pointer to an opaque MERAM
object, which is passed to the update and unregistration functions.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/sh_mobile_lcdcfb.c | 27 ++--
drivers/video/sh_mobile_lcdcfb.h | 2 +-
drivers/video/sh_mobile_meram.c | 345 ++++++++++++++++++++-----------------
include/video/sh_mobile_meram.h | 27 ++--
4 files changed, 215 insertions(+), 186 deletions(-)
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index adc911f..d0c9026 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -840,6 +840,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
struct sh_mobile_meram_cfg *cfg;
int pixelformat;
+ void *meram;
ch = &priv->ch[k];
if (!ch->enabled)
@@ -856,9 +857,9 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
/* we need to de-init configured ICBs before we can
* re-initialize them.
*/
- if (ch->meram_enabled) {
- mdev->ops->meram_unregister(mdev, cfg);
- ch->meram_enabled = 0;
+ if (ch->meram) {
+ mdev->ops->meram_unregister(mdev, ch->meram);
+ ch->meram = NULL;
}
switch (ch->format->fourcc) {
@@ -880,13 +881,13 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
break;
}
- ret = mdev->ops->meram_register(mdev, cfg, ch->pitch,
+ meram = mdev->ops->meram_register(mdev, cfg, ch->pitch,
ch->yres, pixelformat,
ch->base_addr_y, ch->base_addr_c,
&ch->base_addr_y, &ch->base_addr_c,
&ch->pitch);
- if (!ret)
- ch->meram_enabled = 1;
+ if (!IS_ERR(meram))
+ ch->meram = meram;
}
/* Start the LCDC. */
@@ -951,13 +952,11 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
sh_mobile_lcdc_display_off(ch);
/* disable the meram */
- if (ch->meram_enabled) {
- struct sh_mobile_meram_cfg *cfg;
+ if (ch->meram) {
struct sh_mobile_meram_info *mdev;
- cfg = ch->cfg.meram_cfg;
mdev = priv->meram_dev;
- mdev->ops->meram_unregister(mdev, cfg);
- ch->meram_enabled = 0;
+ mdev->ops->meram_unregister(mdev, ch->meram);
+ ch->meram = 0;
}
}
@@ -1070,14 +1069,12 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
base_addr_c += var->xoffset;
}
- if (ch->meram_enabled) {
- struct sh_mobile_meram_cfg *cfg;
+ if (ch->meram) {
struct sh_mobile_meram_info *mdev;
int ret;
- cfg = ch->cfg.meram_cfg;
mdev = priv->meram_dev;
- ret = mdev->ops->meram_update(mdev, cfg,
+ ret = mdev->ops->meram_update(mdev, ch->meram,
base_addr_y, base_addr_c,
&base_addr_y, &base_addr_c);
if (ret)
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h
index 19a4cd7..bf1707c 100644
--- a/drivers/video/sh_mobile_lcdcfb.h
+++ b/drivers/video/sh_mobile_lcdcfb.h
@@ -59,7 +59,7 @@ struct sh_mobile_lcdc_chan {
unsigned long *reg_offs;
unsigned long ldmt1r_value;
unsigned long enabled; /* ME and SE in LDCNT2R */
- int meram_enabled;
+ void *meram;
struct mutex open_lock; /* protects the use counter */
int use_count;
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index 92dc9bd..085c49a 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -10,6 +10,7 @@
*/
#include <linux/device.h>
+#include <linux/err.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -106,14 +107,16 @@ static const unsigned long icb_regs[] = {
/*
* sh_mobile_meram_icb - MERAM ICB information
* @regs: Registers cache
+ * @index: ICB index
* @offset: MERAM block offset
- * @size: MERAM block size in bytes
+ * @size: MERAM block size in KiB
* @cache_unit: Bytes to cache per ICB
* @pixelformat: Video pixel format of the data stored in the ICB
* @current_reg: Which of Start Address Register A (0) or B (1) is in use
*/
struct sh_mobile_meram_icb {
unsigned long regs[ICB_REGS_SIZE];
+ unsigned int index;
unsigned long offset;
unsigned int size;
@@ -124,6 +127,16 @@ struct sh_mobile_meram_icb {
#define MERAM_ICB_NUM 32
+struct sh_mobile_meram_fb_plane {
+ struct sh_mobile_meram_icb *marker;
+ struct sh_mobile_meram_icb *cache;
+};
+
+struct sh_mobile_meram_fb_cache {
+ unsigned int nplanes;
+ struct sh_mobile_meram_fb_plane planes[2];
+};
+
/*
* sh_mobile_meram_priv - MERAM device
* @base: Registers base address
@@ -184,54 +197,46 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
* Allocation
*/
-/* Check if there's no overlaps in MERAM allocation. */
-static int meram_check_overlap(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_icb_cfg *new)
+/* Allocate ICBs and MERAM for a plane. */
+static int __meram_alloc(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_plane *plane,
+ size_t size)
{
- /* valid ICB? */
- if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f)
- return 1;
-
- if (test_bit(new->marker_icb, &priv->used_icb) ||
- test_bit(new->cache_icb, &priv->used_icb))
- return 1;
+ unsigned long mem;
+ unsigned long idx;
- return 0;
-}
+ idx = find_first_zero_bit(&priv->used_icb, 28);
+ if (idx = 28)
+ return -ENOMEM;
+ plane->cache = &priv->icbs[idx];
-/* Allocate memory for the ICBs and mark them as used. */
-static int meram_alloc(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_icb_cfg *new,
- int pixelformat)
-{
- struct sh_mobile_meram_icb *marker = &priv->icbs[new->marker_icb];
- unsigned long mem;
+ idx = find_next_zero_bit(&priv->used_icb, 32, 28);
+ if (idx = 32)
+ return -ENOMEM;
+ plane->marker = &priv->icbs[idx];
- mem = gen_pool_alloc(priv->pool, new->meram_size * 1024);
+ mem = gen_pool_alloc(priv->pool, size * 1024);
if (mem = 0)
return -ENOMEM;
- __set_bit(new->marker_icb, &priv->used_icb);
- __set_bit(new->cache_icb, &priv->used_icb);
+ __set_bit(plane->marker->index, &priv->used_icb);
+ __set_bit(plane->cache->index, &priv->used_icb);
- marker->offset = mem - priv->meram;
- marker->size = new->meram_size * 1024;
- marker->current_reg = 1;
- marker->pixelformat = pixelformat;
+ plane->marker->offset = mem - priv->meram;
+ plane->marker->size = size;
return 0;
}
-/* Unmark the specified ICB as used. */
-static void meram_free(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_icb_cfg *icb)
+/* Free ICBs and MERAM for a plane. */
+static void __meram_free(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_plane *plane)
{
- struct sh_mobile_meram_icb *marker = &priv->icbs[icb->marker_icb];
+ gen_pool_free(priv->pool, priv->meram + plane->marker->offset,
+ plane->marker->size * 1024);
- gen_pool_free(priv->pool, priv->meram + marker->offset, marker->size);
-
- __clear_bit(icb->marker_icb, &priv->used_icb);
- __clear_bit(icb->cache_icb, &priv->used_icb);
+ __clear_bit(plane->marker->index, &priv->used_icb);
+ __clear_bit(plane->cache->index, &priv->used_icb);
}
/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
@@ -243,42 +248,96 @@ static int is_nvcolor(int cspace)
return 0;
}
+/* Allocate memory for the ICBs and mark them as used. */
+static struct sh_mobile_meram_fb_cache *
+meram_alloc(struct sh_mobile_meram_priv *priv,
+ const struct sh_mobile_meram_cfg *cfg,
+ int pixelformat)
+{
+ struct sh_mobile_meram_fb_cache *cache;
+ unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
+ int ret;
+
+ if (cfg->icb[0].meram_size = 0)
+ return ERR_PTR(-EINVAL);
+
+ if (nplanes = 2 && cfg->icb[1].meram_size = 0)
+ return ERR_PTR(-EINVAL);
+
+ cache = kzalloc(sizeof(*cache), GFP_KERNEL);
+ if (cache = NULL)
+ return ERR_PTR(-ENOMEM);
+
+ cache->nplanes = nplanes;
+
+ ret = __meram_alloc(priv, &cache->planes[0], cfg->icb[0].meram_size);
+ if (ret < 0)
+ goto error;
+
+ cache->planes[0].marker->current_reg = 1;
+ cache->planes[0].marker->pixelformat = pixelformat;
+
+ if (cache->nplanes = 1)
+ return cache;
+
+ ret = __meram_alloc(priv, &cache->planes[1], cfg->icb[1].meram_size);
+ if (ret < 0) {
+ __meram_free(priv, &cache->planes[0]);
+ goto error;
+ }
+
+ return cache;
+
+error:
+ kfree(cache);
+ return ERR_PTR(-ENOMEM);
+}
+
+/* Unmark the specified ICB as used. */
+static void meram_free(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_cache *cache)
+{
+ __meram_free(priv, &cache->planes[0]);
+ if (cache->nplanes = 2)
+ __meram_free(priv, &cache->planes[1]);
+
+ kfree(cache);
+}
+
/* Set the next address to fetch. */
static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_cfg *cfg,
+ struct sh_mobile_meram_fb_cache *cache,
unsigned long base_addr_y,
unsigned long base_addr_c)
{
- struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb];
+ struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
unsigned long target;
icb->current_reg ^= 1;
target = icb->current_reg ? MExxSARB : MExxSARA;
/* set the next address to fetch */
- meram_write_icb(priv->base, cfg->icb[0].cache_icb, target,
+ meram_write_icb(priv->base, cache->planes[0].cache->index, target,
base_addr_y);
- meram_write_icb(priv->base, cfg->icb[0].marker_icb, target,
- base_addr_y +
- priv->icbs[cfg->icb[0].marker_icb].cache_unit);
-
- if (is_nvcolor(icb->pixelformat)) {
- meram_write_icb(priv->base, cfg->icb[1].cache_icb, target,
- base_addr_c);
- meram_write_icb(priv->base, cfg->icb[1].marker_icb, target,
- base_addr_c +
- priv->icbs[cfg->icb[1].marker_icb].cache_unit);
+ meram_write_icb(priv->base, cache->planes[0].marker->index, target,
+ base_addr_y + cache->planes[0].marker->cache_unit);
+
+ if (cache->nplanes = 2) {
+ meram_write_icb(priv->base, cache->planes[1].cache->index,
+ target, base_addr_c);
+ meram_write_icb(priv->base, cache->planes[1].marker->index,
+ target, base_addr_c +
+ cache->planes[1].marker->cache_unit);
}
}
/* Get the next ICB address. */
static void
meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
- const struct sh_mobile_meram_cfg *cfg,
+ struct sh_mobile_meram_fb_cache *cache,
unsigned long *icb_addr_y, unsigned long *icb_addr_c)
{
- struct sh_mobile_meram_priv *priv = pdata->priv;
- struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb];
+ struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
unsigned long icb_offset;
if (pdata->addr_mode = SH_MOBILE_MERAM_MODE0)
@@ -286,9 +345,10 @@ meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
else
icb_offset = 0xc0000000 | (icb->current_reg << 23);
- *icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24);
- if (is_nvcolor(icb->pixelformat))
- *icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24);
+ *icb_addr_y = icb_offset | (cache->planes[0].marker->index << 24);
+ if (cache->nplanes = 2)
+ *icb_addr_c = icb_offset
+ | (cache->planes[1].marker->index << 24);
}
#define MERAM_CALC_BYTECOUNT(x, y) \
@@ -296,11 +356,11 @@ meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
/* Initialize MERAM. */
static int meram_init(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_icb_cfg *icb,
+ struct sh_mobile_meram_fb_plane *plane,
unsigned int xres, unsigned int yres,
unsigned int *out_pitch)
{
- struct sh_mobile_meram_icb *marker = &priv->icbs[icb->marker_icb];
+ struct sh_mobile_meram_icb *marker = plane->marker;
unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
unsigned long bnm;
unsigned int lcdc_pitch;
@@ -319,13 +379,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
line_cnt = total_byte_count >> 11;
*out_pitch = xres;
- save_lines = (icb->meram_size / 16 / MERAM_SEC_LINE);
+ save_lines = plane->marker->size / 16 / MERAM_SEC_LINE;
save_lines *= MERAM_SEC_LINE;
} else {
xpitch = xres;
line_cnt = yres;
*out_pitch = lcdc_pitch;
- save_lines = icb->meram_size / (lcdc_pitch >> 10) / 2;
+ save_lines = plane->marker->size / (lcdc_pitch >> 10) / 2;
save_lines &= 0xff;
}
bnm = (save_lines - 1) << 16;
@@ -333,20 +393,20 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
/* TODO: we better to check if we have enough MERAM buffer size */
/* set up ICB */
- meram_write_icb(priv->base, icb->cache_icb, MExxBSIZE,
+ meram_write_icb(priv->base, plane->cache->index, MExxBSIZE,
MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
- meram_write_icb(priv->base, icb->marker_icb, MExxBSIZE,
+ meram_write_icb(priv->base, plane->marker->index, MExxBSIZE,
MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
- meram_write_icb(priv->base, icb->cache_icb, MExxMNCF, bnm);
- meram_write_icb(priv->base, icb->marker_icb, MExxMNCF, bnm);
+ meram_write_icb(priv->base, plane->cache->index, MExxMNCF, bnm);
+ meram_write_icb(priv->base, plane->marker->index, MExxMNCF, bnm);
- meram_write_icb(priv->base, icb->cache_icb, MExxSBSIZE, xpitch);
- meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch);
+ meram_write_icb(priv->base, plane->cache->index, MExxSBSIZE, xpitch);
+ meram_write_icb(priv->base, plane->marker->index, MExxSBSIZE, xpitch);
/* save a cache unit size */
- priv->icbs[icb->cache_icb].cache_unit = xres * save_lines;
- priv->icbs[icb->marker_icb].cache_unit = xres * save_lines;
+ plane->cache->cache_unit = xres * save_lines;
+ plane->marker->cache_unit = xres * save_lines;
/*
* Set MERAM for framebuffer
@@ -354,13 +414,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
* we also chain the cache_icb and the marker_icb.
* we also split the allocated MERAM buffer between two ICBs.
*/
- meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
- MERAM_MExxCTL_VAL(icb->marker_icb, marker->offset) |
- MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
+ meram_write_icb(priv->base, plane->cache->index, MExxCTL,
+ MERAM_MExxCTL_VAL(plane->marker->index, marker->offset)
+ | MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
MExxCTL_MD_FB);
- meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
- MERAM_MExxCTL_VAL(icb->cache_icb, marker->offset +
- icb->meram_size / 2) |
+ meram_write_icb(priv->base, plane->marker->index, MExxCTL,
+ MERAM_MExxCTL_VAL(plane->cache->index, marker->offset +
+ plane->marker->size / 2) |
MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
MExxCTL_MD_FB);
@@ -368,45 +428,44 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
}
static void meram_deinit(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_icb_cfg *icb)
+ struct sh_mobile_meram_fb_plane *plane)
{
/* disable ICB */
- meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
+ meram_write_icb(priv->base, plane->cache->index, MExxCTL,
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
- meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
+ meram_write_icb(priv->base, plane->marker->index, MExxCTL,
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
- priv->icbs[icb->cache_icb].cache_unit = 0;
- priv->icbs[icb->marker_icb].cache_unit = 0;
+ plane->cache->cache_unit = 0;
+ plane->marker->cache_unit = 0;
}
/* -----------------------------------------------------------------------------
* Registration/unregistration
*/
-static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
- const struct sh_mobile_meram_cfg *cfg,
- unsigned int xres, unsigned int yres,
- unsigned int pixelformat,
- unsigned long base_addr_y,
- unsigned long base_addr_c,
- unsigned long *icb_addr_y,
- unsigned long *icb_addr_c,
- unsigned int *pitch)
+static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
+ const struct sh_mobile_meram_cfg *cfg,
+ unsigned int xres, unsigned int yres,
+ unsigned int pixelformat,
+ unsigned long base_addr_y,
+ unsigned long base_addr_c,
+ unsigned long *icb_addr_y,
+ unsigned long *icb_addr_c,
+ unsigned int *pitch)
{
- struct platform_device *pdev;
+ struct sh_mobile_meram_fb_cache *cache;
struct sh_mobile_meram_priv *priv;
+ struct platform_device *pdev;
unsigned int out_pitch;
- unsigned int n;
- int error = 0;
if (!pdata || !pdata->priv || !pdata->pdev || !cfg)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
pixelformat != SH_MOBILE_MERAM_PF_RGB)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
priv = pdata->priv;
pdev = pdata->pdev;
@@ -418,120 +477,82 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
/* we can't handle wider than 8192px */
if (xres > 8192) {
dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
- return -EINVAL;
- }
-
- /* do we have at least one ICB config? */
- if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) {
- dev_err(&pdev->dev, "at least one ICB is required.");
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
mutex_lock(&priv->lock);
- /* make sure that there's no overlaps */
- if (meram_check_overlap(priv, &cfg->icb[0])) {
- dev_err(&pdev->dev, "conflicting config detected.");
- error = -EINVAL;
- goto err;
- }
- n = 1;
-
- /* do the same if we have the second ICB set */
- if (cfg->icb[1].marker_icb >= 0 && cfg->icb[1].cache_icb >= 0) {
- if (meram_check_overlap(priv, &cfg->icb[1])) {
- dev_err(&pdev->dev, "conflicting config detected.");
- error = -EINVAL;
- goto err;
- }
- n = 2;
- }
-
- if (is_nvcolor(pixelformat) && n != 2) {
- dev_err(&pdev->dev, "requires two ICB sets for planar Y/C.");
- error = -EINVAL;
- goto err;
- }
-
/* We now register the ICBs and allocate the MERAM regions. */
- error = meram_alloc(priv, &cfg->icb[0], pixelformat);
- if (error < 0)
+ cache = meram_alloc(priv, cfg, pixelformat);
+ if (IS_ERR(cache)) {
+ dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
+ PTR_ERR(cache));
goto err;
-
- if (is_nvcolor(pixelformat)) {
- error = meram_alloc(priv, &cfg->icb[1], pixelformat);
- if (error < 0) {
- meram_free(priv, &cfg->icb[0]);
- goto err;
- }
}
/* initialize MERAM */
- meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch);
+ meram_init(priv, &cache->planes[0], xres, yres, &out_pitch);
*pitch = out_pitch;
if (pixelformat = SH_MOBILE_MERAM_PF_NV)
- meram_init(priv, &cfg->icb[1], xres, (yres + 1) / 2,
+ meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2,
&out_pitch);
else if (pixelformat = SH_MOBILE_MERAM_PF_NV24)
- meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2,
+ meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2,
&out_pitch);
- meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
- meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
+ meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
+ meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx",
*icb_addr_y, *icb_addr_c);
err:
mutex_unlock(&priv->lock);
- return error;
+ return cache;
}
-static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata,
- const struct sh_mobile_meram_cfg *cfg)
+static int
+sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
{
+ struct sh_mobile_meram_fb_cache *cache = data;
struct sh_mobile_meram_priv *priv;
- struct sh_mobile_meram_icb *icb;
- if (!pdata || !pdata->priv || !cfg)
+ if (!pdata || !pdata->priv || !data)
return -EINVAL;
priv = pdata->priv;
- icb = &priv->icbs[cfg->icb[0].marker_icb];
mutex_lock(&priv->lock);
- /* deinit & unmark */
- if (is_nvcolor(icb->pixelformat)) {
- meram_deinit(priv, &cfg->icb[1]);
- meram_free(priv, &cfg->icb[1]);
- }
- meram_deinit(priv, &cfg->icb[0]);
- meram_free(priv, &cfg->icb[0]);
+ /* deinit & free */
+ meram_deinit(priv, &cache->planes[0]);
+ if (cache->nplanes = 2)
+ meram_deinit(priv, &cache->planes[1]);
+
+ meram_free(priv, cache);
mutex_unlock(&priv->lock);
return 0;
}
-static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata,
- const struct sh_mobile_meram_cfg *cfg,
- unsigned long base_addr_y,
- unsigned long base_addr_c,
- unsigned long *icb_addr_y,
- unsigned long *icb_addr_c)
+static int
+sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
+ unsigned long base_addr_y, unsigned long base_addr_c,
+ unsigned long *icb_addr_y, unsigned long *icb_addr_c)
{
+ struct sh_mobile_meram_fb_cache *cache = data;
struct sh_mobile_meram_priv *priv;
- if (!pdata || !pdata->priv || !cfg)
+ if (!pdata || !pdata->priv || !data)
return -EINVAL;
priv = pdata->priv;
mutex_lock(&priv->lock);
- meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
- meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
+ meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
+ meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
mutex_unlock(&priv->lock);
@@ -607,6 +628,7 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
struct resource *regs;
struct resource *meram;
+ unsigned int i;
int error;
if (!pdata) {
@@ -627,8 +649,13 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
return -ENOMEM;
}
- /* initialize private data */
+ /* Initialize private data. */
mutex_init(&priv->lock);
+ priv->used_icb = pdata->reserved_icbs;
+
+ for (i = 0; i < MERAM_ICB_NUM; ++i)
+ priv->icbs[i].index = i;
+
pdata->ops = &sh_mobile_meram_ops;
pdata->priv = priv;
pdata->pdev = pdev;
diff --git a/include/video/sh_mobile_meram.h b/include/video/sh_mobile_meram.h
index f7700fc..e0f650c 100644
--- a/include/video/sh_mobile_meram.h
+++ b/include/video/sh_mobile_meram.h
@@ -17,8 +17,13 @@ enum {
struct sh_mobile_meram_priv;
struct sh_mobile_meram_ops;
+/*
+ * struct sh_mobile_meram_info - MERAM platform data
+ * @reserved_icbs: Bitmask of reserved ICBs (for instance used through UIO)
+ */
struct sh_mobile_meram_info {
int addr_mode;
+ u32 reserved_icbs;
struct sh_mobile_meram_ops *ops;
struct sh_mobile_meram_priv *priv;
struct platform_device *pdev;
@@ -39,23 +44,23 @@ struct module;
struct sh_mobile_meram_ops {
struct module *module;
/* register usage of meram */
- int (*meram_register)(struct sh_mobile_meram_info *meram_dev,
- const struct sh_mobile_meram_cfg *cfg,
- unsigned int xres, unsigned int yres,
- unsigned int pixelformat,
- unsigned long base_addr_y,
- unsigned long base_addr_c,
- unsigned long *icb_addr_y,
- unsigned long *icb_addr_c,
- unsigned int *pitch);
+ void *(*meram_register)(struct sh_mobile_meram_info *meram_dev,
+ const struct sh_mobile_meram_cfg *cfg,
+ unsigned int xres, unsigned int yres,
+ unsigned int pixelformat,
+ unsigned long base_addr_y,
+ unsigned long base_addr_c,
+ unsigned long *icb_addr_y,
+ unsigned long *icb_addr_c,
+ unsigned int *pitch);
/* unregister usage of meram */
int (*meram_unregister)(struct sh_mobile_meram_info *meram_dev,
- const struct sh_mobile_meram_cfg *cfg);
+ void *data);
/* update meram settings */
int (*meram_update)(struct sh_mobile_meram_info *meram_dev,
- const struct sh_mobile_meram_cfg *cfg,
+ void *data,
unsigned long base_addr_y,
unsigned long base_addr_c,
unsigned long *icb_addr_y,
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 47/57] fbdev: sh_mobile_meram: Use genalloc to manage MERAM allocation
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
To: linux-fbdev
Instead of requiring the users to hardcode MERAM allocation in platform
data, allocate blocks at runtime using genalloc.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
arch/arm/mach-shmobile/board-ap4evb.c | 4 -
arch/arm/mach-shmobile/board-mackerel.c | 4 -
drivers/video/Kconfig | 1 +
drivers/video/sh_mobile_meram.c | 112 +++++++++++++++++++------------
include/video/sh_mobile_meram.h | 1 -
5 files changed, 71 insertions(+), 51 deletions(-)
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 17282bc..f215818 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -591,13 +591,11 @@ static struct sh_mobile_meram_cfg lcd_meram_cfg = {
.icb[0] = {
.marker_icb = 28,
.cache_icb = 24,
- .meram_offset = 0x0,
.meram_size = 0x40,
},
.icb[1] = {
.marker_icb = 29,
.cache_icb = 25,
- .meram_offset = 0x40,
.meram_size = 0x40,
},
};
@@ -874,13 +872,11 @@ static struct sh_mobile_meram_cfg hdmi_meram_cfg = {
.icb[0] = {
.marker_icb = 30,
.cache_icb = 26,
- .meram_offset = 0x80,
.meram_size = 0x100,
},
.icb[1] = {
.marker_icb = 31,
.cache_icb = 27,
- .meram_offset = 0x180,
.meram_size = 0x100,
},
};
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c
index c566f8c..3b85509 100644
--- a/arch/arm/mach-shmobile/board-mackerel.c
+++ b/arch/arm/mach-shmobile/board-mackerel.c
@@ -378,13 +378,11 @@ static struct sh_mobile_meram_cfg lcd_meram_cfg = {
.icb[0] = {
.marker_icb = 28,
.cache_icb = 24,
- .meram_offset = 0x0,
.meram_size = 0x40,
},
.icb[1] = {
.marker_icb = 29,
.cache_icb = 25,
- .meram_offset = 0x40,
.meram_size = 0x40,
},
};
@@ -470,13 +468,11 @@ static struct sh_mobile_meram_cfg hdmi_meram_cfg = {
.icb[0] = {
.marker_icb = 30,
.cache_icb = 26,
- .meram_offset = 0x80,
.meram_size = 0x100,
},
.icb[1] = {
.marker_icb = 31,
.cache_icb = 27,
- .meram_offset = 0x180,
.meram_size = 0x100,
},
};
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 8951cbd..a435942 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2016,6 +2016,7 @@ config FB_SH_MOBILE_HDMI
config FB_SH_MOBILE_MERAM
tristate "SuperH Mobile MERAM read ahead support for LCDC"
depends on FB_SH_MOBILE_LCDC
+ select GENERIC_ALLOCATOR
default y
---help---
Enable MERAM support for the SH-Mobile LCD controller.
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index 30a3305..92dc9bd 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -10,6 +10,7 @@
*/
#include <linux/device.h>
+#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -105,15 +106,17 @@ static const unsigned long icb_regs[] = {
/*
* sh_mobile_meram_icb - MERAM ICB information
* @regs: Registers cache
- * @region: Start and end addresses of the MERAM region
+ * @offset: MERAM block offset
+ * @size: MERAM block size in bytes
* @cache_unit: Bytes to cache per ICB
* @pixelformat: Video pixel format of the data stored in the ICB
* @current_reg: Which of Start Address Register A (0) or B (1) is in use
*/
struct sh_mobile_meram_icb {
unsigned long regs[ICB_REGS_SIZE];
+ unsigned long offset;
+ unsigned int size;
- unsigned long region;
unsigned int cache_unit;
unsigned int pixelformat;
unsigned int current_reg;
@@ -124,21 +127,27 @@ struct sh_mobile_meram_icb {
/*
* sh_mobile_meram_priv - MERAM device
* @base: Registers base address
+ * @meram: MERAM physical address
* @regs: Registers cache
* @lock: Protects used_icb and icbs
* @used_icb: Bitmask of used ICBs
* @icbs: ICBs
+ * @pool: Allocation pool to manage the MERAM
*/
struct sh_mobile_meram_priv {
void __iomem *base;
+ unsigned long meram;
unsigned long regs[MERAM_REGS_SIZE];
struct mutex lock;
unsigned long used_icb;
struct sh_mobile_meram_icb icbs[MERAM_ICB_NUM];
+
+ struct gen_pool *pool;
};
/* settings */
+#define MERAM_GRANULARITY 1024
#define MERAM_SEC_LINE 15
#define MERAM_LINE_WIDTH 2048
@@ -175,18 +184,10 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
* Allocation
*/
-#define MERAM_CACHE_START(p) ((p) >> 16)
-#define MERAM_CACHE_END(p) ((p) & 0xffff)
-#define MERAM_CACHE_SET(o, s) ((((o) & 0xffff) << 16) | \
- (((o) + (s) - 1) & 0xffff))
-
/* Check if there's no overlaps in MERAM allocation. */
static int meram_check_overlap(struct sh_mobile_meram_priv *priv,
const struct sh_mobile_meram_icb_cfg *new)
{
- unsigned int used_start, used_end, meram_start, meram_end;
- unsigned int i;
-
/* valid ICB? */
if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f)
return 1;
@@ -195,43 +196,40 @@ static int meram_check_overlap(struct sh_mobile_meram_priv *priv,
test_bit(new->cache_icb, &priv->used_icb))
return 1;
- for (i = 0; i < MERAM_ICB_NUM; i++) {
- if (!test_bit(i, &priv->used_icb))
- continue;
-
- used_start = MERAM_CACHE_START(priv->icbs[i].region);
- used_end = MERAM_CACHE_END(priv->icbs[i].region);
- meram_start = new->meram_offset;
- meram_end = new->meram_offset + new->meram_size;
-
- if ((meram_start >= used_start && meram_start < used_end) ||
- (meram_end > used_start && meram_end < used_end))
- return 1;
- }
-
return 0;
}
-/* Mark the specified ICB as used. */
-static void meram_mark(struct sh_mobile_meram_priv *priv,
+/* Allocate memory for the ICBs and mark them as used. */
+static int meram_alloc(struct sh_mobile_meram_priv *priv,
const struct sh_mobile_meram_icb_cfg *new,
int pixelformat)
{
+ struct sh_mobile_meram_icb *marker = &priv->icbs[new->marker_icb];
+ unsigned long mem;
+
+ mem = gen_pool_alloc(priv->pool, new->meram_size * 1024);
+ if (mem = 0)
+ return -ENOMEM;
+
__set_bit(new->marker_icb, &priv->used_icb);
__set_bit(new->cache_icb, &priv->used_icb);
- priv->icbs[new->marker_icb].region = MERAM_CACHE_SET(new->meram_offset,
- new->meram_size);
- priv->icbs[new->cache_icb].region = MERAM_CACHE_SET(new->meram_offset,
- new->meram_size);
- priv->icbs[new->marker_icb].current_reg = 1;
- priv->icbs[new->marker_icb].pixelformat = pixelformat;
+ marker->offset = mem - priv->meram;
+ marker->size = new->meram_size * 1024;
+ marker->current_reg = 1;
+ marker->pixelformat = pixelformat;
+
+ return 0;
}
/* Unmark the specified ICB as used. */
-static void meram_unmark(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_icb_cfg *icb)
+static void meram_free(struct sh_mobile_meram_priv *priv,
+ const struct sh_mobile_meram_icb_cfg *icb)
{
+ struct sh_mobile_meram_icb *marker = &priv->icbs[icb->marker_icb];
+
+ gen_pool_free(priv->pool, priv->meram + marker->offset, marker->size);
+
__clear_bit(icb->marker_icb, &priv->used_icb);
__clear_bit(icb->cache_icb, &priv->used_icb);
}
@@ -302,6 +300,7 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
unsigned int xres, unsigned int yres,
unsigned int *out_pitch)
{
+ struct sh_mobile_meram_icb *marker = &priv->icbs[icb->marker_icb];
unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
unsigned long bnm;
unsigned int lcdc_pitch;
@@ -356,11 +355,11 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
* we also split the allocated MERAM buffer between two ICBs.
*/
meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
- MERAM_MExxCTL_VAL(icb->marker_icb, icb->meram_offset) |
+ MERAM_MExxCTL_VAL(icb->marker_icb, marker->offset) |
MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
MExxCTL_MD_FB);
meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
- MERAM_MExxCTL_VAL(icb->cache_icb, icb->meram_offset +
+ MERAM_MExxCTL_VAL(icb->cache_icb, marker->offset +
icb->meram_size / 2) |
MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
MExxCTL_MD_FB);
@@ -454,10 +453,18 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
goto err;
}
- /* we now register the ICB */
- meram_mark(priv, &cfg->icb[0], pixelformat);
- if (is_nvcolor(pixelformat))
- meram_mark(priv, &cfg->icb[1], pixelformat);
+ /* We now register the ICBs and allocate the MERAM regions. */
+ error = meram_alloc(priv, &cfg->icb[0], pixelformat);
+ if (error < 0)
+ goto err;
+
+ if (is_nvcolor(pixelformat)) {
+ error = meram_alloc(priv, &cfg->icb[1], pixelformat);
+ if (error < 0) {
+ meram_free(priv, &cfg->icb[0]);
+ goto err;
+ }
+ }
/* initialize MERAM */
meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch);
@@ -497,10 +504,10 @@ static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata,
/* deinit & unmark */
if (is_nvcolor(icb->pixelformat)) {
meram_deinit(priv, &cfg->icb[1]);
- meram_unmark(priv, &cfg->icb[1]);
+ meram_free(priv, &cfg->icb[1]);
}
meram_deinit(priv, &cfg->icb[0]);
- meram_unmark(priv, &cfg->icb[0]);
+ meram_free(priv, &cfg->icb[0]);
mutex_unlock(&priv->lock);
@@ -626,6 +633,7 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
pdata->priv = priv;
pdata->pdev = pdev;
+ /* Request memory regions and remap the registers. */
if (!request_mem_region(regs->start, resource_size(regs), pdev->name)) {
dev_err(&pdev->dev, "MERAM registers region already claimed\n");
error = -EBUSY;
@@ -646,6 +654,20 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
goto err_ioremap;
}
+ priv->meram = meram->start;
+
+ /* Create and initialize the MERAM memory pool. */
+ priv->pool = gen_pool_create(ilog2(MERAM_GRANULARITY), -1);
+ if (priv->pool = NULL) {
+ error = -ENOMEM;
+ goto err_genpool;
+ }
+
+ error = gen_pool_add(priv->pool, meram->start, resource_size(meram),
+ -1);
+ if (error < 0)
+ goto err_genpool;
+
/* initialize ICB addressing mode */
if (pdata->addr_mode = SH_MOBILE_MERAM_MODE1)
meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
@@ -657,6 +679,10 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
return 0;
+err_genpool:
+ if (priv->pool)
+ gen_pool_destroy(priv->pool);
+ iounmap(priv->base);
err_ioremap:
release_mem_region(meram->start, resource_size(meram));
err_req_meram:
@@ -677,6 +703,8 @@ static int sh_mobile_meram_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
+ gen_pool_destroy(priv->pool);
+
iounmap(priv->base);
release_mem_region(meram->start, resource_size(meram));
release_mem_region(regs->start, resource_size(regs));
diff --git a/include/video/sh_mobile_meram.h b/include/video/sh_mobile_meram.h
index 05ca3f9..f7700fc 100644
--- a/include/video/sh_mobile_meram.h
+++ b/include/video/sh_mobile_meram.h
@@ -28,7 +28,6 @@ struct sh_mobile_meram_info {
struct sh_mobile_meram_icb_cfg {
unsigned int marker_icb; /* ICB # for Marker ICB */
unsigned int cache_icb; /* ICB # for Cache ICB */
- unsigned int meram_offset; /* MERAM Buffer Offset to use */
unsigned int meram_size; /* MERAM Buffer Size to use */
};
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 46/57] fbdev: sh_mobile_meram: Divide the code into sections
From: Laurent Pinchart @ 2012-03-01 15:07 UTC (permalink / raw)
To: linux-fbdev
And rename a couple of constants to make prefixes more uniform.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/sh_mobile_meram.c | 98 +++++++++++++++++---------------------
1 files changed, 44 insertions(+), 54 deletions(-)
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index 0c5b301..30a3305 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -9,16 +9,20 @@
* for more details.
*/
+#include <linux/device.h>
+#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/io.h>
#include <linux/slab.h>
-#include <linux/platform_device.h>
+
#include <video/sh_mobile_meram.h>
-/* meram registers */
+/* -----------------------------------------------------------------------------
+ * MERAM registers
+ */
+
#define MEVCR1 0x4
#define MEVCR1_RST (1 << 31)
#define MEVCR1_WD (1 << 30)
@@ -81,14 +85,12 @@
((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
((xszm1) << MExxBSIZE_XSZM1_SHIFT))
-#define SH_MOBILE_MERAM_ICB_NUM 32
-
static const unsigned long common_regs[] = {
MEVCR1,
MEQSEL1,
MEQSEL2,
};
-#define CMN_REGS_SIZE ARRAY_SIZE(common_regs)
+#define MERAM_REGS_SIZE ARRAY_SIZE(common_regs)
static const unsigned long icb_regs[] = {
MExxCTL,
@@ -117,6 +119,8 @@ struct sh_mobile_meram_icb {
unsigned int current_reg;
};
+#define MERAM_ICB_NUM 32
+
/*
* sh_mobile_meram_priv - MERAM device
* @base: Registers base address
@@ -127,19 +131,19 @@ struct sh_mobile_meram_icb {
*/
struct sh_mobile_meram_priv {
void __iomem *base;
- unsigned long regs[CMN_REGS_SIZE];
+ unsigned long regs[MERAM_REGS_SIZE];
struct mutex lock;
unsigned long used_icb;
- struct sh_mobile_meram_icb icbs[SH_MOBILE_MERAM_ICB_NUM];
+ struct sh_mobile_meram_icb icbs[MERAM_ICB_NUM];
};
/* settings */
-#define MERAM_SEC_LINE 15
-#define MERAM_LINE_WIDTH 2048
+#define MERAM_SEC_LINE 15
+#define MERAM_LINE_WIDTH 2048
-/*
- * MERAM/ICB access functions
+/* -----------------------------------------------------------------------------
+ * Registers access
*/
#define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20)
@@ -167,8 +171,8 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
return ioread32(base + off);
}
-/*
- * register ICB
+/* -----------------------------------------------------------------------------
+ * Allocation
*/
#define MERAM_CACHE_START(p) ((p) >> 16)
@@ -176,10 +180,7 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
#define MERAM_CACHE_SET(o, s) ((((o) & 0xffff) << 16) | \
(((o) + (s) - 1) & 0xffff))
-/*
- * check if there's no overlaps in MERAM allocation.
- */
-
+/* Check if there's no overlaps in MERAM allocation. */
static int meram_check_overlap(struct sh_mobile_meram_priv *priv,
const struct sh_mobile_meram_icb_cfg *new)
{
@@ -194,7 +195,7 @@ static int meram_check_overlap(struct sh_mobile_meram_priv *priv,
test_bit(new->cache_icb, &priv->used_icb))
return 1;
- for (i = 0; i < SH_MOBILE_MERAM_ICB_NUM; i++) {
+ for (i = 0; i < MERAM_ICB_NUM; i++) {
if (!test_bit(i, &priv->used_icb))
continue;
@@ -211,10 +212,7 @@ static int meram_check_overlap(struct sh_mobile_meram_priv *priv,
return 0;
}
-/*
- * mark the specified ICB as used
- */
-
+/* Mark the specified ICB as used. */
static void meram_mark(struct sh_mobile_meram_priv *priv,
const struct sh_mobile_meram_icb_cfg *new,
int pixelformat)
@@ -230,10 +228,7 @@ static void meram_mark(struct sh_mobile_meram_priv *priv,
priv->icbs[new->marker_icb].pixelformat = pixelformat;
}
-/*
- * unmark the specified ICB as used
- */
-
+/* Unmark the specified ICB as used. */
static void meram_unmark(struct sh_mobile_meram_priv *priv,
const struct sh_mobile_meram_icb_cfg *icb)
{
@@ -241,9 +236,7 @@ static void meram_unmark(struct sh_mobile_meram_priv *priv,
__clear_bit(icb->cache_icb, &priv->used_icb);
}
-/*
- * is this a YCbCr(NV12, NV16 or NV24) colorspace
- */
+/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
static int is_nvcolor(int cspace)
{
if (cspace = SH_MOBILE_MERAM_PF_NV ||
@@ -252,9 +245,7 @@ static int is_nvcolor(int cspace)
return 0;
}
-/*
- * set the next address to fetch
- */
+/* Set the next address to fetch. */
static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
const struct sh_mobile_meram_cfg *cfg,
unsigned long base_addr_y,
@@ -282,9 +273,7 @@ static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
}
}
-/*
- * get the next ICB address
- */
+/* Get the next ICB address. */
static void
meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
const struct sh_mobile_meram_cfg *cfg,
@@ -307,10 +296,7 @@ meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
#define MERAM_CALC_BYTECOUNT(x, y) \
(((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
-/*
- * initialize MERAM
- */
-
+/* Initialize MERAM. */
static int meram_init(struct sh_mobile_meram_priv *priv,
const struct sh_mobile_meram_icb_cfg *icb,
unsigned int xres, unsigned int yres,
@@ -395,8 +381,8 @@ static void meram_deinit(struct sh_mobile_meram_priv *priv,
priv->icbs[icb->marker_icb].cache_unit = 0;
}
-/*
- * register the ICB
+/* -----------------------------------------------------------------------------
+ * Registration/unregistration
*/
static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
@@ -545,13 +531,24 @@ static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata,
return 0;
}
+static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
+ .module = THIS_MODULE,
+ .meram_register = sh_mobile_meram_register,
+ .meram_unregister = sh_mobile_meram_unregister,
+ .meram_update = sh_mobile_meram_update,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
static int sh_mobile_meram_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
unsigned int i, j;
- for (i = 0; i < CMN_REGS_SIZE; i++)
+ for (i = 0; i < MERAM_REGS_SIZE; i++)
priv->regs[i] = meram_read_reg(priv->base, common_regs[i]);
for (i = 0; i < 32; i++) {
@@ -583,7 +580,7 @@ static int sh_mobile_meram_runtime_resume(struct device *dev)
priv->icbs[i].regs[j]);
}
- for (i = 0; i < CMN_REGS_SIZE; i++)
+ for (i = 0; i < MERAM_REGS_SIZE; i++)
meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
return 0;
}
@@ -593,15 +590,8 @@ static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = {
.runtime_resume = sh_mobile_meram_runtime_resume,
};
-static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
- .module = THIS_MODULE,
- .meram_register = sh_mobile_meram_register,
- .meram_unregister = sh_mobile_meram_unregister,
- .meram_update = sh_mobile_meram_update,
-};
-
-/*
- * initialize MERAM
+/* -----------------------------------------------------------------------------
+ * Probe/remove and driver init/exit
*/
static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 45/57] fbdev: sh_mobile_meram: Don't inline everything
From: Laurent Pinchart @ 2012-03-01 15:07 UTC (permalink / raw)
To: linux-fbdev
Let the compiler decide which complex functions to inline, and constify
constant static arrays.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/sh_mobile_meram.c | 30 +++++++++++++++---------------
1 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index cddb180..0c5b301 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -83,14 +83,14 @@
#define SH_MOBILE_MERAM_ICB_NUM 32
-static unsigned long common_regs[] = {
+static const unsigned long common_regs[] = {
MEVCR1,
MEQSEL1,
MEQSEL2,
};
#define CMN_REGS_SIZE ARRAY_SIZE(common_regs)
-static unsigned long icb_regs[] = {
+static const unsigned long icb_regs[] = {
MExxCTL,
MExxBSIZE,
MExxMNCF,
@@ -180,8 +180,8 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
* check if there's no overlaps in MERAM allocation.
*/
-static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_icb_cfg *new)
+static int meram_check_overlap(struct sh_mobile_meram_priv *priv,
+ const struct sh_mobile_meram_icb_cfg *new)
{
unsigned int used_start, used_end, meram_start, meram_end;
unsigned int i;
@@ -215,9 +215,9 @@ static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv,
* mark the specified ICB as used
*/
-static inline void meram_mark(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_icb_cfg *new,
- int pixelformat)
+static void meram_mark(struct sh_mobile_meram_priv *priv,
+ const struct sh_mobile_meram_icb_cfg *new,
+ int pixelformat)
{
__set_bit(new->marker_icb, &priv->used_icb);
__set_bit(new->cache_icb, &priv->used_icb);
@@ -234,8 +234,8 @@ static inline void meram_mark(struct sh_mobile_meram_priv *priv,
* unmark the specified ICB as used
*/
-static inline void meram_unmark(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_icb_cfg *icb)
+static void meram_unmark(struct sh_mobile_meram_priv *priv,
+ const struct sh_mobile_meram_icb_cfg *icb)
{
__clear_bit(icb->marker_icb, &priv->used_icb);
__clear_bit(icb->cache_icb, &priv->used_icb);
@@ -244,7 +244,7 @@ static inline void meram_unmark(struct sh_mobile_meram_priv *priv,
/*
* is this a YCbCr(NV12, NV16 or NV24) colorspace
*/
-static inline int is_nvcolor(int cspace)
+static int is_nvcolor(int cspace)
{
if (cspace = SH_MOBILE_MERAM_PF_NV ||
cspace = SH_MOBILE_MERAM_PF_NV24)
@@ -255,10 +255,10 @@ static inline int is_nvcolor(int cspace)
/*
* set the next address to fetch
*/
-static inline void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_cfg *cfg,
- unsigned long base_addr_y,
- unsigned long base_addr_c)
+static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
+ const struct sh_mobile_meram_cfg *cfg,
+ unsigned long base_addr_y,
+ unsigned long base_addr_c)
{
struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb];
unsigned long target;
@@ -285,7 +285,7 @@ static inline void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
/*
* get the next ICB address
*/
-static inline void
+static void
meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
const struct sh_mobile_meram_cfg *cfg,
unsigned long *icb_addr_y, unsigned long *icb_addr_c)
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 44/57] fbdev: sh_mobile_meram: Add struct sh_mobile_meram_icb
From: Laurent Pinchart @ 2012-03-01 15:07 UTC (permalink / raw)
To: linux-fbdev
The new structure stores ICB parameters for ICBs.
Instead of modifying the struct sh_mobile_meram_cfg instances passed by
callers, store the ICB parameters internally and make the public API
take const pointers to sh_mobile_meram_cfg.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/sh_mobile_meram.c | 178 ++++++++++++++++++++-------------------
include/video/sh_mobile_meram.h | 12 +--
2 files changed, 94 insertions(+), 96 deletions(-)
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index 7af2ffe..cddb180 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -100,14 +100,38 @@ static unsigned long icb_regs[] = {
};
#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
+/*
+ * sh_mobile_meram_icb - MERAM ICB information
+ * @regs: Registers cache
+ * @region: Start and end addresses of the MERAM region
+ * @cache_unit: Bytes to cache per ICB
+ * @pixelformat: Video pixel format of the data stored in the ICB
+ * @current_reg: Which of Start Address Register A (0) or B (1) is in use
+ */
+struct sh_mobile_meram_icb {
+ unsigned long regs[ICB_REGS_SIZE];
+
+ unsigned long region;
+ unsigned int cache_unit;
+ unsigned int pixelformat;
+ unsigned int current_reg;
+};
+
+/*
+ * sh_mobile_meram_priv - MERAM device
+ * @base: Registers base address
+ * @regs: Registers cache
+ * @lock: Protects used_icb and icbs
+ * @used_icb: Bitmask of used ICBs
+ * @icbs: ICBs
+ */
struct sh_mobile_meram_priv {
- void __iomem *base;
- struct mutex lock;
- unsigned long used_icb;
- unsigned int used_meram_cache_regions;
- unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM];
- unsigned long cmn_saved_regs[CMN_REGS_SIZE];
- unsigned long icb_saved_regs[ICB_REGS_SIZE * SH_MOBILE_MERAM_ICB_NUM];
+ void __iomem *base;
+ unsigned long regs[CMN_REGS_SIZE];
+
+ struct mutex lock;
+ unsigned long used_icb;
+ struct sh_mobile_meram_icb icbs[SH_MOBILE_MERAM_ICB_NUM];
};
/* settings */
@@ -157,7 +181,7 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
*/
static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_icb_cfg *new)
+ const struct sh_mobile_meram_icb_cfg *new)
{
unsigned int used_start, used_end, meram_start, meram_end;
unsigned int i;
@@ -167,17 +191,20 @@ static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv,
return 1;
if (test_bit(new->marker_icb, &priv->used_icb) ||
- test_bit(new->cache_icb, &priv->used_icb))
+ test_bit(new->cache_icb, &priv->used_icb))
return 1;
- for (i = 0; i < priv->used_meram_cache_regions; i++) {
- used_start = MERAM_CACHE_START(priv->used_meram_cache[i]);
- used_end = MERAM_CACHE_END(priv->used_meram_cache[i]);
+ for (i = 0; i < SH_MOBILE_MERAM_ICB_NUM; i++) {
+ if (!test_bit(i, &priv->used_icb))
+ continue;
+
+ used_start = MERAM_CACHE_START(priv->icbs[i].region);
+ used_end = MERAM_CACHE_END(priv->icbs[i].region);
meram_start = new->meram_offset;
meram_end = new->meram_offset + new->meram_size;
if ((meram_start >= used_start && meram_start < used_end) ||
- (meram_end > used_start && meram_end < used_end))
+ (meram_end > used_start && meram_end < used_end))
return 1;
}
@@ -189,22 +216,18 @@ static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv,
*/
static inline void meram_mark(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_icb_cfg *new)
+ const struct sh_mobile_meram_icb_cfg *new,
+ int pixelformat)
{
- unsigned int n;
-
- if (new->marker_icb < 0 || new->cache_icb < 0)
- return;
-
__set_bit(new->marker_icb, &priv->used_icb);
__set_bit(new->cache_icb, &priv->used_icb);
- n = priv->used_meram_cache_regions;
-
- priv->used_meram_cache[n] = MERAM_CACHE_SET(new->meram_offset,
- new->meram_size);
-
- priv->used_meram_cache_regions++;
+ priv->icbs[new->marker_icb].region = MERAM_CACHE_SET(new->meram_offset,
+ new->meram_size);
+ priv->icbs[new->cache_icb].region = MERAM_CACHE_SET(new->meram_offset,
+ new->meram_size);
+ priv->icbs[new->marker_icb].current_reg = 1;
+ priv->icbs[new->marker_icb].pixelformat = pixelformat;
}
/*
@@ -212,30 +235,10 @@ static inline void meram_mark(struct sh_mobile_meram_priv *priv,
*/
static inline void meram_unmark(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_icb_cfg *icb)
+ const struct sh_mobile_meram_icb_cfg *icb)
{
- unsigned long pattern;
- unsigned int i;
-
- if (icb->marker_icb < 0 || icb->cache_icb < 0)
- return;
-
__clear_bit(icb->marker_icb, &priv->used_icb);
__clear_bit(icb->cache_icb, &priv->used_icb);
-
- pattern = MERAM_CACHE_SET(icb->meram_offset, icb->meram_size);
- for (i = 0; i < priv->used_meram_cache_regions; i++) {
- if (priv->used_meram_cache[i] = pattern) {
- while (i < priv->used_meram_cache_regions - 1) {
- priv->used_meram_cache[i] - priv->used_meram_cache[i + 1] ;
- i++;
- }
- priv->used_meram_cache[i] = 0;
- priv->used_meram_cache_regions--;
- break;
- }
- }
}
/*
@@ -244,7 +247,7 @@ static inline void meram_unmark(struct sh_mobile_meram_priv *priv,
static inline int is_nvcolor(int cspace)
{
if (cspace = SH_MOBILE_MERAM_PF_NV ||
- cspace = SH_MOBILE_MERAM_PF_NV24)
+ cspace = SH_MOBILE_MERAM_PF_NV24)
return 1;
return 0;
}
@@ -253,46 +256,51 @@ static inline int is_nvcolor(int cspace)
* set the next address to fetch
*/
static inline void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_cfg *cfg,
+ const struct sh_mobile_meram_cfg *cfg,
unsigned long base_addr_y,
unsigned long base_addr_c)
{
+ struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb];
unsigned long target;
- cfg->current_reg ^= 1;
- target = cfg->current_reg ? MExxSARB : MExxSARA;
+ icb->current_reg ^= 1;
+ target = icb->current_reg ? MExxSARB : MExxSARA;
/* set the next address to fetch */
- meram_write_icb(priv->base, cfg->icb[0].cache_icb, target,
+ meram_write_icb(priv->base, cfg->icb[0].cache_icb, target,
base_addr_y);
meram_write_icb(priv->base, cfg->icb[0].marker_icb, target,
- base_addr_y + cfg->icb[0].cache_unit);
+ base_addr_y +
+ priv->icbs[cfg->icb[0].marker_icb].cache_unit);
- if (is_nvcolor(cfg->pixelformat)) {
+ if (is_nvcolor(icb->pixelformat)) {
meram_write_icb(priv->base, cfg->icb[1].cache_icb, target,
base_addr_c);
meram_write_icb(priv->base, cfg->icb[1].marker_icb, target,
- base_addr_c + cfg->icb[1].cache_unit);
+ base_addr_c +
+ priv->icbs[cfg->icb[1].marker_icb].cache_unit);
}
}
/*
* get the next ICB address
*/
-static inline void meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
- struct sh_mobile_meram_cfg *cfg,
- unsigned long *icb_addr_y,
- unsigned long *icb_addr_c)
+static inline void
+meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
+ const struct sh_mobile_meram_cfg *cfg,
+ unsigned long *icb_addr_y, unsigned long *icb_addr_c)
{
+ struct sh_mobile_meram_priv *priv = pdata->priv;
+ struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb];
unsigned long icb_offset;
if (pdata->addr_mode = SH_MOBILE_MERAM_MODE0)
- icb_offset = 0x80000000 | (cfg->current_reg << 29);
+ icb_offset = 0x80000000 | (icb->current_reg << 29);
else
- icb_offset = 0xc0000000 | (cfg->current_reg << 23);
+ icb_offset = 0xc0000000 | (icb->current_reg << 23);
*icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24);
- if (is_nvcolor(cfg->pixelformat))
+ if (is_nvcolor(icb->pixelformat))
*icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24);
}
@@ -304,7 +312,7 @@ static inline void meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
*/
static int meram_init(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_icb_cfg *icb,
+ const struct sh_mobile_meram_icb_cfg *icb,
unsigned int xres, unsigned int yres,
unsigned int *out_pitch)
{
@@ -352,7 +360,8 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch);
/* save a cache unit size */
- icb->cache_unit = xres * save_lines;
+ priv->icbs[icb->cache_icb].cache_unit = xres * save_lines;
+ priv->icbs[icb->marker_icb].cache_unit = xres * save_lines;
/*
* Set MERAM for framebuffer
@@ -374,14 +383,16 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
}
static void meram_deinit(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_icb_cfg *icb)
+ const struct sh_mobile_meram_icb_cfg *icb)
{
/* disable ICB */
meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
- icb->cache_unit = 0;
+
+ priv->icbs[icb->cache_icb].cache_unit = 0;
+ priv->icbs[icb->marker_icb].cache_unit = 0;
}
/*
@@ -389,7 +400,7 @@ static void meram_deinit(struct sh_mobile_meram_priv *priv,
*/
static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
- struct sh_mobile_meram_cfg *cfg,
+ const struct sh_mobile_meram_cfg *cfg,
unsigned int xres, unsigned int yres,
unsigned int pixelformat,
unsigned long base_addr_y,
@@ -433,12 +444,6 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
mutex_lock(&priv->lock);
- if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) {
- dev_err(&pdev->dev, "no more ICB available.");
- error = -EINVAL;
- goto err;
- }
-
/* make sure that there's no overlaps */
if (meram_check_overlap(priv, &cfg->icb[0])) {
dev_err(&pdev->dev, "conflicting config detected.");
@@ -464,10 +469,9 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
}
/* we now register the ICB */
- cfg->pixelformat = pixelformat;
- meram_mark(priv, &cfg->icb[0]);
+ meram_mark(priv, &cfg->icb[0], pixelformat);
if (is_nvcolor(pixelformat))
- meram_mark(priv, &cfg->icb[1]);
+ meram_mark(priv, &cfg->icb[1], pixelformat);
/* initialize MERAM */
meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch);
@@ -479,7 +483,6 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2,
&out_pitch);
- cfg->current_reg = 1;
meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
@@ -492,19 +495,21 @@ err:
}
static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata,
- struct sh_mobile_meram_cfg *cfg)
+ const struct sh_mobile_meram_cfg *cfg)
{
struct sh_mobile_meram_priv *priv;
+ struct sh_mobile_meram_icb *icb;
if (!pdata || !pdata->priv || !cfg)
return -EINVAL;
priv = pdata->priv;
+ icb = &priv->icbs[cfg->icb[0].marker_icb];
mutex_lock(&priv->lock);
/* deinit & unmark */
- if (is_nvcolor(cfg->pixelformat)) {
+ if (is_nvcolor(icb->pixelformat)) {
meram_deinit(priv, &cfg->icb[1]);
meram_unmark(priv, &cfg->icb[1]);
}
@@ -517,7 +522,7 @@ static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata,
}
static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata,
- struct sh_mobile_meram_cfg *cfg,
+ const struct sh_mobile_meram_cfg *cfg,
unsigned long base_addr_y,
unsigned long base_addr_c,
unsigned long *icb_addr_y,
@@ -547,18 +552,17 @@ static int sh_mobile_meram_runtime_suspend(struct device *dev)
unsigned int i, j;
for (i = 0; i < CMN_REGS_SIZE; i++)
- priv->cmn_saved_regs[i] = meram_read_reg(priv->base,
- common_regs[i]);
+ priv->regs[i] = meram_read_reg(priv->base, common_regs[i]);
for (i = 0; i < 32; i++) {
if (!test_bit(i, &priv->used_icb))
continue;
for (j = 0; j < ICB_REGS_SIZE; j++) {
- priv->icb_saved_regs[i * ICB_REGS_SIZE + j] + priv->icbs[i].regs[j] meram_read_icb(priv->base, i, icb_regs[j]);
/* Reset ICB on resume */
if (icb_regs[j] = MExxCTL)
- priv->icb_saved_regs[i * ICB_REGS_SIZE + j] |+ priv->icbs[i].regs[j] | MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
}
}
@@ -574,15 +578,13 @@ static int sh_mobile_meram_runtime_resume(struct device *dev)
for (i = 0; i < 32; i++) {
if (!test_bit(i, &priv->used_icb))
continue;
- for (j = 0; j < ICB_REGS_SIZE; j++) {
+ for (j = 0; j < ICB_REGS_SIZE; j++)
meram_write_icb(priv->base, i, icb_regs[j],
- priv->icb_saved_regs[i * ICB_REGS_SIZE + j]);
- }
+ priv->icbs[i].regs[j]);
}
for (i = 0; i < CMN_REGS_SIZE; i++)
- meram_write_reg(priv->base, common_regs[i],
- priv->cmn_saved_regs[i]);
+ meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
return 0;
}
diff --git a/include/video/sh_mobile_meram.h b/include/video/sh_mobile_meram.h
index 6755e3f..05ca3f9 100644
--- a/include/video/sh_mobile_meram.h
+++ b/include/video/sh_mobile_meram.h
@@ -30,14 +30,10 @@ struct sh_mobile_meram_icb_cfg {
unsigned int cache_icb; /* ICB # for Cache ICB */
unsigned int meram_offset; /* MERAM Buffer Offset to use */
unsigned int meram_size; /* MERAM Buffer Size to use */
-
- unsigned int cache_unit; /* bytes to cache per ICB */
};
struct sh_mobile_meram_cfg {
- struct sh_mobile_meram_icb_cfg icb[2];
- int pixelformat;
- int current_reg;
+ struct sh_mobile_meram_icb_cfg icb[2];
};
struct module;
@@ -45,7 +41,7 @@ struct sh_mobile_meram_ops {
struct module *module;
/* register usage of meram */
int (*meram_register)(struct sh_mobile_meram_info *meram_dev,
- struct sh_mobile_meram_cfg *cfg,
+ const struct sh_mobile_meram_cfg *cfg,
unsigned int xres, unsigned int yres,
unsigned int pixelformat,
unsigned long base_addr_y,
@@ -56,11 +52,11 @@ struct sh_mobile_meram_ops {
/* unregister usage of meram */
int (*meram_unregister)(struct sh_mobile_meram_info *meram_dev,
- struct sh_mobile_meram_cfg *cfg);
+ const struct sh_mobile_meram_cfg *cfg);
/* update meram settings */
int (*meram_update)(struct sh_mobile_meram_info *meram_dev,
- struct sh_mobile_meram_cfg *cfg,
+ const struct sh_mobile_meram_cfg *cfg,
unsigned long base_addr_y,
unsigned long base_addr_c,
unsigned long *icb_addr_y,
--
1.7.3.4
^ permalink raw reply related
* [PATCH v2 43/57] fbdev: sh_mobile_meram: Make current_reg field store the current reg set
From: Laurent Pinchart @ 2012-03-01 15:07 UTC (permalink / raw)
To: linux-fbdev
Make sure current_reg = 0/1 always mean register set A/B through all
the code.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/video/sh_mobile_meram.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index 548f700..7af2ffe 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -259,8 +259,8 @@ static inline void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
{
unsigned long target;
- target = (cfg->current_reg) ? MExxSARA : MExxSARB;
cfg->current_reg ^= 1;
+ target = cfg->current_reg ? MExxSARB : MExxSARA;
/* set the next address to fetch */
meram_write_icb(priv->base, cfg->icb[0].cache_icb, target,
--
1.7.3.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox