* [PATCH 44/66] arch/powerpc/sysdev/cpm2_pic.h: Checkpatch cleanup
From: Andrea Gelmini @ 2010-02-27 16:51 UTC (permalink / raw)
To: linux-kernel; +Cc: Andrea Gelmini, Paul Mackerras, linuxppc-dev
In-Reply-To: <1267289508-31031-1-git-send-email-andrea.gelmini@gelma.net>
arch/powerpc/sysdev/cpm2_pic.h:6: ERROR: "(foo*)" should be "(foo *)"
Signed-off-by: Andrea Gelmini <andrea.gelmini@gelma.net>
---
arch/powerpc/sysdev/cpm2_pic.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/sysdev/cpm2_pic.h b/arch/powerpc/sysdev/cpm2_pic.h
index 30e5828..2c5f70c 100644
--- a/arch/powerpc/sysdev/cpm2_pic.h
+++ b/arch/powerpc/sysdev/cpm2_pic.h
@@ -3,6 +3,6 @@
extern unsigned int cpm2_get_irq(void);
-extern void cpm2_pic_init(struct device_node*);
+extern void cpm2_pic_init(struct device_node *);
#endif /* _PPC_KERNEL_CPM2_H */
--
1.7.0.90.g251a4
^ permalink raw reply related
* [PATCH] powerpc: Fix SMP build with disabled CPU hotplugging.
From: Adam Lackorzynski @ 2010-02-27 17:07 UTC (permalink / raw)
To: linuxppc-dev
Compiling 2.6.33 with SMP enabled and HOTPLUG_CPU disabled gives me the
following link errors:
LD init/built-in.o
LD .tmp_vmlinux1
arch/powerpc/platforms/built-in.o: In function `.smp_xics_setup_cpu':
smp.c:(.devinit.text+0x88): undefined reference to `.set_cpu_current_state'
smp.c:(.devinit.text+0x94): undefined reference to `.set_default_offline_state'
arch/powerpc/platforms/built-in.o: In function `.smp_pSeries_kick_cpu':
smp.c:(.devinit.text+0x13c): undefined reference to `.set_preferred_offline_state'
smp.c:(.devinit.text+0x148): undefined reference to `.get_cpu_current_state'
smp.c:(.devinit.text+0x1a8): undefined reference to `.get_cpu_current_state'
make: *** [.tmp_vmlinux1] Error 1
The following change fixes that for me and seems to work as expected.
Signed-off-by: Adam Lackorzynski <adam@os.inf.tu-dresden.de>
---
arch/powerpc/platforms/pseries/offline_states.h | 22 +++++++++++++++++++++-
1 files changed, 21 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/offline_states.h b/arch/powerpc/platforms/pseries/offline_states.h
index 22574e0..202d869 100644
--- a/arch/powerpc/platforms/pseries/offline_states.h
+++ b/arch/powerpc/platforms/pseries/offline_states.h
@@ -9,10 +9,30 @@ enum cpu_state_vals {
CPU_MAX_OFFLINE_STATES
};
+#ifdef CONFIG_HOTPLUG_CPU
extern enum cpu_state_vals get_cpu_current_state(int cpu);
extern void set_cpu_current_state(int cpu, enum cpu_state_vals state);
-extern enum cpu_state_vals get_preferred_offline_state(int cpu);
extern void set_preferred_offline_state(int cpu, enum cpu_state_vals state);
extern void set_default_offline_state(int cpu);
+#else
+static inline enum cpu_state_vals get_cpu_current_state(int cpu)
+{
+ return CPU_STATE_ONLINE;
+}
+
+static inline void set_cpu_current_state(int cpu, enum cpu_state_vals state)
+{
+}
+
+static inline void set_preferred_offline_state(int cpu, enum cpu_state_vals state)
+{
+}
+
+static inline void set_default_offline_state(int cpu)
+{
+}
+#endif
+
+extern enum cpu_state_vals get_preferred_offline_state(int cpu);
extern int start_secondary(void);
#endif
--
1.5.6.5
Adam
--
Adam adam@os.inf.tu-dresden.de
Lackorzynski http://os.inf.tu-dresden.de/~adam/
^ permalink raw reply related
* [PATCH 0/3] Rework MPC5121 DIU support (for 2.6.34)
From: Anatolij Gustschin @ 2010-02-27 21:58 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-fbdev, wd, dzu, jrigby, Anatolij Gustschin, yorksun
In-Reply-To: <fa686aa41002161006l3b301febt94fe990df4bddfe9@mail.gmail.com>
This patch series rework DIU support patch submitted
previously with the patch series for updating MPC5121
support in mainline. It doesn't add new panel timing data
to the framebuffer driver anymore. Instead we now allow
encoding this data in the device tree. First two patches
add this support.
The third patch for DIU support is rebased, but still
depends on patches for adding MPC5121 USB support (because
it touches shared platform code).
It is intended for inclusion in 2.6.34, since without
DIU support patch framebuffer doesn't work on mpc5121.
Anatolij Gustschin (3):
video: add support for getting video mode from device tree
fbdev: fsl-diu-fb.c: allow setting panel video mode from DT
powerpc/mpc5121: shared DIU framebuffer support
arch/powerpc/platforms/512x/mpc5121_ads.c | 7 +
arch/powerpc/platforms/512x/mpc5121_generic.c | 13 ++
arch/powerpc/platforms/512x/mpc512x.h | 3 +
arch/powerpc/platforms/512x/mpc512x_shared.c | 282 +++++++++++++++++++++++++
arch/powerpc/sysdev/fsl_soc.h | 1 +
drivers/video/Kconfig | 6 +
drivers/video/Makefile | 1 +
drivers/video/fsl-diu-fb.c | 88 +++++---
drivers/video/fsl-diu-fb.h | 223 -------------------
drivers/video/ofmode.c | 95 +++++++++
drivers/video/ofmode.h | 6 +
include/linux/fsl-diu-fb.h | 223 +++++++++++++++++++
12 files changed, 693 insertions(+), 255 deletions(-)
delete mode 100644 drivers/video/fsl-diu-fb.h
create mode 100644 drivers/video/ofmode.c
create mode 100644 drivers/video/ofmode.h
create mode 100644 include/linux/fsl-diu-fb.h
^ permalink raw reply
* [PATCH 1/3] video: add support for getting video mode from device tree
From: Anatolij Gustschin @ 2010-02-27 21:58 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-fbdev, wd, dzu, jrigby, Anatolij Gustschin, yorksun
In-Reply-To: <fa686aa41002161006l3b301febt94fe990df4bddfe9@mail.gmail.com>
Framebuffer drivers may want to get panel timing info
from the device tree. This patch adds appropriate support.
Subsequent patch for FSL DIU frame buffer driver makes use
of this functionality.
Signed-off-by: Anatolij Gustschin <agust@denx.de>
---
drivers/video/Kconfig | 5 +++
drivers/video/Makefile | 1 +
drivers/video/ofmode.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/video/ofmode.h | 6 +++
4 files changed, 107 insertions(+), 0 deletions(-)
create mode 100644 drivers/video/ofmode.c
create mode 100644 drivers/video/ofmode.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 5a5c303..dc1beb0 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -203,6 +203,11 @@ config FB_MACMODES
depends on FB
default n
+config FB_OF_MODE
+ tristate
+ depends on FB && OF
+ default n
+
config FB_BACKLIGHT
bool
depends on FB
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 4ecb30c..c4a912b 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_FB_SVGALIB) += svgalib.o
obj-$(CONFIG_FB_MACMODES) += macmodes.o
obj-$(CONFIG_FB_DDC) += fb_ddc.o
obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o
+obj-$(CONFIG_FB_OF_MODE) += ofmode.o
# Hardware specific drivers go first
obj-$(CONFIG_FB_AMIGA) += amifb.o c2p_planar.o
diff --git a/drivers/video/ofmode.c b/drivers/video/ofmode.c
new file mode 100644
index 0000000..78caad1
--- /dev/null
+++ b/drivers/video/ofmode.c
@@ -0,0 +1,95 @@
+/*
+ * Get video mode settings from Flattened Device Tree.
+ *
+ * Copyright (C) 2010 DENX Software Engineering.
+ * Anatolij Gustschin <agust@denx.de>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License version 2. See the file COPYING in the main directory
+ * of this archive for more details.
+ */
+
+#include <linux/fb.h>
+#include <linux/of.h>
+
+int __devinit of_get_video_mode(struct device_node *np,
+ struct fb_info *info)
+{
+ struct fb_videomode dt_mode;
+ const u32 *prop;
+ unsigned int len;
+
+ if (!np || !info)
+ return -EINVAL;
+
+ prop = of_get_property(np, "width", &len);
+ if (!prop || len != sizeof(u32))
+ goto err;
+ dt_mode.xres = *prop;
+
+ prop = of_get_property(np, "height", &len);
+ if (!prop || len != sizeof(u32))
+ goto err;
+ dt_mode.yres = *prop;
+
+ prop = of_get_property(np, "depth", &len);
+ if (!prop || len != sizeof(u32))
+ goto err;
+ info->var.bits_per_pixel = *prop;
+
+ prop = of_get_property(np, "linebytes", &len);
+ if (!prop || len != sizeof(u32))
+ goto err;
+ info->fix.line_length = *prop;
+
+ prop = of_get_property(np, "refresh", &len);
+ if (prop && len == sizeof(u32))
+ dt_mode.refresh = *prop; /* optional */
+
+ prop = of_get_property(np, "pixclock", &len);
+ if (!prop || len != sizeof(u32))
+ goto err;
+ dt_mode.pixclock = *prop;
+
+ prop = of_get_property(np, "left_margin", &len);
+ if (!prop || len != sizeof(u32))
+ goto err;
+ dt_mode.left_margin = *prop;
+
+ prop = of_get_property(np, "right_margin", &len);
+ if (!prop || len != sizeof(u32))
+ goto err;
+ dt_mode.right_margin = *prop;
+
+ prop = of_get_property(np, "upper_margin", &len);
+ if (!prop || len != sizeof(u32))
+ goto err;
+ dt_mode.upper_margin = *prop;
+
+ prop = of_get_property(np, "lower_margin", &len);
+ if (!prop || len != sizeof(u32))
+ goto err;
+ dt_mode.lower_margin = *prop;
+
+ prop = of_get_property(np, "hsync_len", &len);
+ if (!prop || len != sizeof(u32))
+ goto err;
+ dt_mode.hsync_len = *prop;
+
+ prop = of_get_property(np, "vsync_len", &len);
+ if (!prop || len != sizeof(u32))
+ goto err;
+ dt_mode.vsync_len = *prop;
+
+ prop = of_get_property(np, "sync", &len);
+ if (!prop || len != sizeof(u32))
+ goto err;
+ dt_mode.sync = *prop;
+
+ fb_videomode_to_var(&info->var, &dt_mode);
+
+ return 0;
+err:
+ pr_err("%s: Can't find expected mode entry\n", np->full_name);
+ return -EINVAL;
+}
diff --git a/drivers/video/ofmode.h b/drivers/video/ofmode.h
new file mode 100644
index 0000000..9a13bec
--- /dev/null
+++ b/drivers/video/ofmode.h
@@ -0,0 +1,6 @@
+#ifndef _OFMODE_H
+#define _OFMODE_H
+
+extern int __devinit of_get_video_mode(struct device_node *np,
+ struct fb_info *info);
+#endif
--
1.6.3.3
^ permalink raw reply related
* [PATCH 2/3] fbdev: fsl-diu-fb.c: allow setting panel video mode from DT
From: Anatolij Gustschin @ 2010-02-27 21:58 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-fbdev, wd, dzu, jrigby, Anatolij Gustschin, yorksun
In-Reply-To: <fa686aa41002161006l3b301febt94fe990df4bddfe9@mail.gmail.com>
Add support for specifying display panel data in the device
tree. If no panel data is provided in the device tree, default
video mode will be used as usual.
Signed-off-by: Anatolij Gustschin <agust@denx.de>
---
drivers/video/Kconfig | 1 +
drivers/video/fsl-diu-fb.c | 63 +++++++++++++++++++++++++-------------------
2 files changed, 37 insertions(+), 27 deletions(-)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index dc1beb0..c805ecd 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1850,6 +1850,7 @@ config FB_FSL_DIU
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select PPC_LIB_RHEAP
+ select FB_OF_MODE
---help---
Framebuffer driver for the Freescale SoC DIU
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index 4637bcb..19ca1da 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -36,6 +36,8 @@
#include <sysdev/fsl_soc.h>
#include "fsl-diu-fb.h"
+#include "ofmode.h"
+
/*
* These parameters give default parameters
* for video output 1024x768,
@@ -1168,7 +1170,7 @@ static int init_fbinfo(struct fb_info *info)
return 0;
}
-static int __devinit install_fb(struct fb_info *info)
+static int __devinit install_fb(struct device_node *np, struct fb_info *info)
{
int rc;
struct mfb_info *mfbi = info->par;
@@ -1177,33 +1179,40 @@ static int __devinit install_fb(struct fb_info *info)
if (init_fbinfo(info))
return -EINVAL;
- if (mfbi->index == 0) /* plane 0 */
- aoi_mode = fb_mode;
- else
+ if (mfbi->index == 0) { /* plane 0 */
+ /* use default mode for plane0 if there is no mode in DTB */
+ if (of_get_video_mode(np, info) < 0)
+ aoi_mode = fb_mode;
+ else
+ aoi_mode = NULL;
+ } else
aoi_mode = init_aoi_mode;
- pr_debug("mode used = %s\n", aoi_mode);
- rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db,
- ARRAY_SIZE(fsl_diu_mode_db), &fsl_diu_default_mode, default_bpp);
- switch (rc) {
- case 1:
- pr_debug("using mode specified in @mode\n");
- break;
- case 2:
- pr_debug("using mode specified in @mode "
- "with ignored refresh rate\n");
- break;
- case 3:
- pr_debug("using mode default mode\n");
- break;
- case 4:
- pr_debug("using mode from list\n");
- break;
- default:
- pr_debug("rc = %d\n", rc);
- pr_debug("failed to find mode\n");
- return -EINVAL;
- break;
+ if (aoi_mode) {
+ pr_debug("mode used = %s\n", aoi_mode);
+ rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db,
+ ARRAY_SIZE(fsl_diu_mode_db),
+ &fsl_diu_default_mode, default_bpp);
+ switch (rc) {
+ case 1:
+ pr_debug("using mode specified in @mode\n");
+ break;
+ case 2:
+ pr_debug("using mode specified in @mode "
+ "with ignored refresh rate\n");
+ break;
+ case 3:
+ pr_debug("using mode default mode\n");
+ break;
+ case 4:
+ pr_debug("using mode from list\n");
+ break;
+ default:
+ pr_debug("rc = %d\n", rc);
+ pr_debug("failed to find mode\n");
+ return -EINVAL;
+ break;
+ }
}
pr_debug("xres_virtual %d\n", info->var.xres_virtual);
@@ -1521,7 +1530,7 @@ static int __devinit fsl_diu_probe(struct of_device *ofdev,
mfbi->ad = (struct diu_ad *)((u32)pool.ad.vaddr
+ pool.ad.offset) + i;
mfbi->ad->paddr = pool.ad.paddr + i * sizeof(struct diu_ad);
- ret = install_fb(machine_data->fsl_diu_info[i]);
+ ret = install_fb(np, machine_data->fsl_diu_info[i]);
if (ret) {
dev_err(&ofdev->dev,
"Failed to register framebuffer %d\n",
--
1.6.3.3
^ permalink raw reply related
* [PATCH 3/3] powerpc/mpc5121: shared DIU framebuffer support
From: Anatolij Gustschin @ 2010-02-27 21:58 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-fbdev, wd, dzu, jrigby, Anatolij Gustschin, yorksun
In-Reply-To: <fa686aa41002161006l3b301febt94fe990df4bddfe9@mail.gmail.com>
MPC5121 DIU configuration/setup as initialized by the boot
loader currently will get lost while booting Linux. As a
result displaying the boot splash is not possible through
the boot process.
To prevent this we reserve configured DIU frame buffer
address range while booting and preserve AOI descriptor
and gamma table so that DIU continues displaying through
the whole boot process. On first open from user space
DIU frame buffer driver releases the reserved frame
buffer area and continues to operate as usual.
The patch also moves drivers/video/fsl-diu-fb.h file to
include/linux as we use some DIU structures in platform
code.
'diu_ops' callbacks in platform code borrowed from John's
DIU code.
Signed-off-by: John Rigby <jrigby@gmail.com>
Signed-off-by: Anatolij Gustschin <agust@denx.de>
Cc: Grant Likely <grant.likely@secretlab.ca>
---
arch/powerpc/platforms/512x/mpc5121_ads.c | 7 +
arch/powerpc/platforms/512x/mpc5121_generic.c | 13 ++
arch/powerpc/platforms/512x/mpc512x.h | 3 +
arch/powerpc/platforms/512x/mpc512x_shared.c | 282 +++++++++++++++++++++++++
arch/powerpc/sysdev/fsl_soc.h | 1 +
drivers/video/fsl-diu-fb.c | 25 ++-
drivers/video/fsl-diu-fb.h | 223 -------------------
include/linux/fsl-diu-fb.h | 223 +++++++++++++++++++
8 files changed, 549 insertions(+), 228 deletions(-)
delete mode 100644 drivers/video/fsl-diu-fb.h
create mode 100644 include/linux/fsl-diu-fb.h
diff --git a/arch/powerpc/platforms/512x/mpc5121_ads.c b/arch/powerpc/platforms/512x/mpc5121_ads.c
index ee6ae12..aa4d5a8 100644
--- a/arch/powerpc/platforms/512x/mpc5121_ads.c
+++ b/arch/powerpc/platforms/512x/mpc5121_ads.c
@@ -42,6 +42,7 @@ static void __init mpc5121_ads_setup_arch(void)
for_each_compatible_node(np, "pci", "fsl,mpc5121-pci")
mpc83xx_add_bridge(np);
#endif
+ mpc512x_setup_diu();
}
static void __init mpc5121_ads_init_IRQ(void)
@@ -60,11 +61,17 @@ static int __init mpc5121_ads_probe(void)
return of_flat_dt_is_compatible(root, "fsl,mpc5121ads");
}
+void __init mpc5121_ads_init_early(void)
+{
+ mpc512x_init_diu();
+}
+
define_machine(mpc5121_ads) {
.name = "MPC5121 ADS",
.probe = mpc5121_ads_probe,
.setup_arch = mpc5121_ads_setup_arch,
.init = mpc512x_init,
+ .init_early = mpc5121_ads_init_early,
.init_IRQ = mpc5121_ads_init_IRQ,
.get_irq = ipic_get_irq,
.calibrate_decr = generic_calibrate_decr,
diff --git a/arch/powerpc/platforms/512x/mpc5121_generic.c b/arch/powerpc/platforms/512x/mpc5121_generic.c
index a6c0e3a..3aaa281 100644
--- a/arch/powerpc/platforms/512x/mpc5121_generic.c
+++ b/arch/powerpc/platforms/512x/mpc5121_generic.c
@@ -28,6 +28,7 @@
*/
static char *board[] __initdata = {
"prt,prtlvt",
+ "ifm,pdm360ng",
NULL
};
@@ -48,10 +49,22 @@ static int __init mpc5121_generic_probe(void)
return board[i] != NULL;
}
+void __init mpc512x_generic_init_early(void)
+{
+ mpc512x_init_diu();
+}
+
+void __init mpc512x_generic_setup_arch(void)
+{
+ mpc512x_setup_diu();
+}
+
define_machine(mpc5121_generic) {
.name = "MPC5121 generic",
.probe = mpc5121_generic_probe,
.init = mpc512x_init,
+ .init_early = mpc512x_generic_init_early,
+ .setup_arch = mpc512x_generic_setup_arch,
.init_IRQ = mpc512x_init_IRQ,
.get_irq = ipic_get_irq,
.calibrate_decr = generic_calibrate_decr,
diff --git a/arch/powerpc/platforms/512x/mpc512x.h b/arch/powerpc/platforms/512x/mpc512x.h
index d72b2c7..1cfe9d5 100644
--- a/arch/powerpc/platforms/512x/mpc512x.h
+++ b/arch/powerpc/platforms/512x/mpc512x.h
@@ -17,4 +17,7 @@ extern int __init mpc5121_clk_init(void);
void __init mpc512x_declare_of_platform_devices(void);
extern void mpc512x_restart(char *cmd);
extern void __init mpc5121_usb_init(void);
+extern void __init mpc512x_init_diu(void);
+extern void __init mpc512x_setup_diu(void);
+extern struct fsl_diu_shared_fb diu_shared_fb;
#endif /* __MPC512X_H__ */
diff --git a/arch/powerpc/platforms/512x/mpc512x_shared.c b/arch/powerpc/platforms/512x/mpc512x_shared.c
index fbdf65f..a8c50a6 100644
--- a/arch/powerpc/platforms/512x/mpc512x_shared.c
+++ b/arch/powerpc/platforms/512x/mpc512x_shared.c
@@ -16,7 +16,11 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/of_platform.h>
+#include <linux/fsl-diu-fb.h>
+#include <linux/bootmem.h>
+#include <sysdev/fsl_soc.h>
+#include <asm/cacheflush.h>
#include <asm/machdep.h>
#include <asm/ipic.h>
#include <asm/prom.h>
@@ -53,6 +57,284 @@ void mpc512x_restart(char *cmd)
;
}
+struct fsl_diu_shared_fb {
+ char gamma[0x300]; /* 32-bit aligned! */
+ struct diu_ad ad0; /* 32-bit aligned! */
+ phys_addr_t fb_phys;
+ size_t fb_len;
+ bool in_use;
+};
+
+unsigned int mpc512x_get_pixel_format(unsigned int bits_per_pixel,
+ int monitor_port)
+{
+ unsigned int pix_fmt;
+
+ switch (bits_per_pixel) {
+ case 32:
+ pix_fmt = 0x88883316;
+ break;
+ case 24:
+ pix_fmt = 0x88082219;
+ break;
+ case 16:
+ pix_fmt = 0x65053118;
+ break;
+ default:
+ pix_fmt = 0x00000400;
+ }
+ return pix_fmt;
+}
+
+void mpc512x_set_gamma_table(int monitor_port, char *gamma_table_base)
+{
+}
+
+void mpc512x_set_monitor_port(int monitor_port)
+{
+}
+
+#define CCM_SCFR1 0x0000000c
+#define DIU_DIV_MASK 0x000000ff
+void mpc512x_set_pixel_clock(unsigned int pixclock)
+{
+ unsigned long bestval, bestfreq, speed_ccb, busfreq;
+ unsigned long minpixclock, maxpixclock, pixval;
+ struct device_node *np;
+ void __iomem *ccm;
+ u32 temp;
+ long err;
+ int i;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-clock");
+ if (!np) {
+ pr_err("Can't find clock control module.\n");
+ return;
+ }
+
+ ccm = of_iomap(np, 0);
+ if (!ccm) {
+ pr_err("Can't map clock control module reg.\n");
+ of_node_put(np);
+ return;
+ }
+ of_node_put(np);
+
+ busfreq = 200000000;
+ np = of_find_node_by_type(NULL, "cpu");
+ if (np) {
+ unsigned int size;
+ const unsigned int *prop =
+ of_get_property(np, "bus-frequency", &size);
+ if (prop)
+ busfreq = *prop;
+ of_node_put(np);
+ }
+
+ /* Pixel Clock configuration */
+ pr_debug("DIU: Bus Frequency = %lu\n", busfreq);
+ speed_ccb = busfreq * 4;
+
+ /* Calculate the pixel clock with the smallest error */
+ /* calculate the following in steps to avoid overflow */
+ pr_debug("DIU pixclock in ps - %d\n", pixclock);
+ temp = 1;
+ temp *= 1000000000;
+ temp /= pixclock;
+ temp *= 1000;
+ pixclock = temp;
+ pr_debug("DIU pixclock freq - %u\n", pixclock);
+
+ temp *= 5;
+ temp /= 100; /* pixclock * 0.05 */
+ pr_debug("deviation = %d\n", temp);
+ minpixclock = pixclock - temp;
+ maxpixclock = pixclock + temp;
+ pr_debug("DIU minpixclock - %lu\n", minpixclock);
+ pr_debug("DIU maxpixclock - %lu\n", maxpixclock);
+ pixval = speed_ccb/pixclock;
+ pr_debug("DIU pixval = %lu\n", pixval);
+
+ err = 100000000;
+ bestval = pixval;
+ pr_debug("DIU bestval = %lu\n", bestval);
+
+ bestfreq = 0;
+ for (i = -1; i <= 1; i++) {
+ temp = speed_ccb / (pixval+i);
+ pr_debug("DIU test pixval i=%d, pixval=%lu, temp freq. = %u\n",
+ i, pixval, temp);
+ if ((temp < minpixclock) || (temp > maxpixclock))
+ pr_debug("DIU exceeds monitor range (%lu to %lu)\n",
+ minpixclock, maxpixclock);
+ else if (abs(temp - pixclock) < err) {
+ pr_debug("Entered the else if block %d\n", i);
+ err = abs(temp - pixclock);
+ bestval = pixval + i;
+ bestfreq = temp;
+ }
+ }
+
+ pr_debug("DIU chose = %lx\n", bestval);
+ pr_debug("DIU error = %ld\n NomPixClk ", err);
+ pr_debug("DIU: Best Freq = %lx\n", bestfreq);
+ /* Modify DIU_DIV in CCM SCFR1 */
+ temp = in_be32(ccm + CCM_SCFR1);
+ pr_debug("DIU: Current value of SCFR1: 0x%08x\n", temp);
+ temp &= ~DIU_DIV_MASK;
+ temp |= (bestval & DIU_DIV_MASK);
+ out_be32(ccm + CCM_SCFR1, temp);
+ pr_debug("DIU: Modified value of SCFR1: 0x%08x\n", temp);
+ iounmap(ccm);
+}
+
+ssize_t mpc512x_show_monitor_port(int monitor_port, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0 - 5121 LCD\n");
+}
+
+int mpc512x_set_sysfs_monitor_port(int val)
+{
+ return 0;
+}
+
+struct fsl_diu_shared_fb __attribute__ ((__aligned__(8))) diu_shared_fb;
+EXPORT_SYMBOL_GPL(diu_shared_fb);
+
+#if defined(CONFIG_FB_FSL_DIU) || \
+ defined(CONFIG_FB_FSL_DIU_MODULE)
+static inline void mpc512x_free_bootmem(struct page *page)
+{
+ __ClearPageReserved(page);
+ BUG_ON(PageTail(page));
+ BUG_ON(atomic_read(&page->_count) > 1);
+ atomic_set(&page->_count, 1);
+ __free_page(page);
+ totalram_pages++;
+}
+
+void mpc512x_release_bootmem(void)
+{
+ unsigned long addr = diu_shared_fb.fb_phys & PAGE_MASK;
+ unsigned long size = diu_shared_fb.fb_len;
+ unsigned long start, end;
+
+ if (diu_shared_fb.in_use) {
+ start = PFN_UP(addr);
+ end = PFN_DOWN(addr + size);
+
+ for (; start < end; start++)
+ mpc512x_free_bootmem(pfn_to_page(start));
+
+ diu_shared_fb.in_use = false;
+ }
+ diu_ops.release_bootmem = NULL;
+}
+#endif
+
+/*
+ * Check if DIU was pre-initialized. If so, perform steps
+ * needed to continue displaying through the whole boot process.
+ * Move area descriptor and gamma table elsewhere, they are
+ * destroyed by bootmem allocator otherwise. The frame buffer
+ * address range will be reserved in setup_arch() after bootmem
+ * allocator is up.
+ */
+void __init mpc512x_init_diu(void)
+{
+ struct device_node *np;
+ void __iomem *diu_reg;
+ phys_addr_t desc;
+ void __iomem *vaddr;
+ unsigned long mode, pix_fmt, res, bpp;
+ unsigned long dst;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu");
+ if (!np) {
+ pr_err("No DIU node\n");
+ return;
+ }
+
+ diu_reg = of_iomap(np, 0);
+ of_node_put(np);
+ if (!diu_reg) {
+ pr_err("Can't map DIU\n");
+ return;
+ }
+
+ mode = in_be32(diu_reg + 0x1c);
+ if (mode != 1) {
+ pr_info("%s: DIU OFF\n", __func__);
+ goto out;
+ }
+
+ desc = in_be32(diu_reg);
+ vaddr = ioremap(desc, sizeof(struct diu_ad));
+ if (!vaddr) {
+ pr_err("Can't map DIU area desc.\n");
+ goto out;
+ }
+ memcpy(&diu_shared_fb.ad0, vaddr, sizeof(struct diu_ad));
+ /* flush fb area descriptor */
+ dst = (unsigned long)&diu_shared_fb.ad0;
+ flush_dcache_range(dst, dst + sizeof(struct diu_ad) - 1);
+
+ res = in_be32(diu_reg + 0x28);
+ pix_fmt = in_le32(vaddr);
+ bpp = ((pix_fmt >> 16) & 0x3) + 1;
+ diu_shared_fb.fb_phys = in_le32(vaddr + 4);
+ diu_shared_fb.fb_len = ((res & 0xfff0000) >> 16) * (res & 0xfff) * bpp;
+ diu_shared_fb.in_use = true;
+ iounmap(vaddr);
+
+ desc = in_be32(diu_reg + 0xc);
+ vaddr = ioremap(desc, sizeof(diu_shared_fb.gamma));
+ if (!vaddr) {
+ pr_err("Can't map DIU area desc.\n");
+ diu_shared_fb.in_use = false;
+ goto out;
+ }
+ memcpy(&diu_shared_fb.gamma, vaddr, sizeof(diu_shared_fb.gamma));
+ /* flush gamma table */
+ dst = (unsigned long)&diu_shared_fb.gamma;
+ flush_dcache_range(dst, dst + sizeof(diu_shared_fb.gamma) - 1);
+
+ iounmap(vaddr);
+ out_be32(diu_reg + 0xc, virt_to_phys(&diu_shared_fb.gamma));
+ out_be32(diu_reg + 4, 0);
+ out_be32(diu_reg + 8, 0);
+ out_be32(diu_reg, virt_to_phys(&diu_shared_fb.ad0));
+
+out:
+ iounmap(diu_reg);
+}
+
+void __init mpc512x_setup_diu(void)
+{
+ int ret;
+
+ if (diu_shared_fb.in_use) {
+ ret = reserve_bootmem(diu_shared_fb.fb_phys,
+ diu_shared_fb.fb_len,
+ BOOTMEM_EXCLUSIVE);
+ if (ret) {
+ pr_err("%s: reserve bootmem failed\n", __func__);
+ diu_shared_fb.in_use = false;
+ }
+ }
+
+#if defined(CONFIG_FB_FSL_DIU) || \
+ defined(CONFIG_FB_FSL_DIU_MODULE)
+ diu_ops.get_pixel_format = mpc512x_get_pixel_format;
+ diu_ops.set_gamma_table = mpc512x_set_gamma_table;
+ diu_ops.set_monitor_port = mpc512x_set_monitor_port;
+ diu_ops.set_pixel_clock = mpc512x_set_pixel_clock;
+ diu_ops.show_monitor_port = mpc512x_show_monitor_port;
+ diu_ops.set_sysfs_monitor_port = mpc512x_set_sysfs_monitor_port;
+ diu_ops.release_bootmem = mpc512x_release_bootmem;
+#endif
+}
+
void __init mpc512x_init_IRQ(void)
{
struct device_node *np;
diff --git a/arch/powerpc/sysdev/fsl_soc.h b/arch/powerpc/sysdev/fsl_soc.h
index 19754be..346057d 100644
--- a/arch/powerpc/sysdev/fsl_soc.h
+++ b/arch/powerpc/sysdev/fsl_soc.h
@@ -30,6 +30,7 @@ struct platform_diu_data_ops {
void (*set_pixel_clock) (unsigned int pixclock);
ssize_t (*show_monitor_port) (int monitor_port, char *buf);
int (*set_sysfs_monitor_port) (int val);
+ void (*release_bootmem) (void);
};
extern struct platform_diu_data_ops diu_ops;
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index 19ca1da..263f7da 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -34,7 +34,7 @@
#include <linux/of_platform.h>
#include <sysdev/fsl_soc.h>
-#include "fsl-diu-fb.h"
+#include <linux/fsl-diu-fb.h>
#include "ofmode.h"
@@ -331,8 +331,11 @@ static int fsl_diu_enable_panel(struct fb_info *info)
if (mfbi->type != MFB_TYPE_OFF) {
switch (mfbi->index) {
case 0: /* plane 0 */
- if (hw->desc[0] != ad->paddr)
+ if (in_be32(&hw->desc[0]) != ad->paddr) {
+ out_be32(&dr.diu_reg->diu_mode, 0);
out_be32(&hw->desc[0], ad->paddr);
+ out_be32(&dr.diu_reg->diu_mode, 1);
+ }
break;
case 1: /* plane 1 AOI 0 */
cmfbi = machine_data->fsl_diu_info[2]->par;
@@ -391,7 +394,7 @@ static int fsl_diu_disable_panel(struct fb_info *info)
switch (mfbi->index) {
case 0: /* plane 0 */
- if (hw->desc[0] != machine_data->dummy_ad->paddr)
+ if (in_be32(&hw->desc[0]) != machine_data->dummy_ad->paddr)
out_be32(&hw->desc[0],
machine_data->dummy_ad->paddr);
break;
@@ -1102,6 +1105,10 @@ static int fsl_diu_open(struct fb_info *info, int user)
struct mfb_info *mfbi = info->par;
int res = 0;
+ /* free boot splash memory on first /dev/fb0 open */
+ if (!mfbi->index && diu_ops.release_bootmem)
+ diu_ops.release_bootmem();
+
spin_lock(&diu_lock);
mfbi->count++;
if (mfbi->count == 1) {
@@ -1436,6 +1443,7 @@ static int __devinit fsl_diu_probe(struct of_device *ofdev,
int ret, i, error = 0;
struct resource res;
struct fsl_diu_data *machine_data;
+ int diu_mode;
machine_data = kzalloc(sizeof(struct fsl_diu_data), GFP_KERNEL);
if (!machine_data)
@@ -1472,7 +1480,9 @@ static int __devinit fsl_diu_probe(struct of_device *ofdev,
goto error2;
}
- out_be32(&dr.diu_reg->diu_mode, 0); /* disable DIU anyway*/
+ diu_mode = in_be32(&dr.diu_reg->diu_mode);
+ if (diu_mode != MFB_MODE1)
+ out_be32(&dr.diu_reg->diu_mode, 0); /* disable DIU */
/* Get the IRQ of the DIU */
machine_data->irq = irq_of_parse_and_map(np, 0);
@@ -1520,7 +1530,12 @@ static int __devinit fsl_diu_probe(struct of_device *ofdev,
machine_data->dummy_ad->offset_xyd = 0;
machine_data->dummy_ad->next_ad = 0;
- out_be32(&dr.diu_reg->desc[0], machine_data->dummy_ad->paddr);
+ /* Let DIU display splash screen if it was pre-initialized
+ * by the bootloader, set dummy area descriptor otherwise.
+ */
+ if (diu_mode != MFB_MODE1)
+ out_be32(&dr.diu_reg->desc[0], machine_data->dummy_ad->paddr);
+
out_be32(&dr.diu_reg->desc[1], machine_data->dummy_ad->paddr);
out_be32(&dr.diu_reg->desc[2], machine_data->dummy_ad->paddr);
diff --git a/drivers/video/fsl-diu-fb.h b/drivers/video/fsl-diu-fb.h
deleted file mode 100644
index fc295d7..0000000
--- a/drivers/video/fsl-diu-fb.h
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
- *
- * Freescale DIU Frame Buffer device driver
- *
- * Authors: Hongjun Chen <hong-jun.chen@freescale.com>
- * Paul Widmer <paul.widmer@freescale.com>
- * Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
- * York Sun <yorksun@freescale.com>
- *
- * Based on imxfb.c Copyright (C) 2004 S.Hauer, Pengutronix
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-#ifndef __FSL_DIU_FB_H__
-#define __FSL_DIU_FB_H__
-
-/* Arbitrary threshold to determine the allocation method
- * See mpc8610fb_set_par(), map_video_memory(), and unmap_video_memory()
- */
-#define MEM_ALLOC_THRESHOLD (1024*768*4+32)
-/* Minimum value that the pixel clock can be set to in pico seconds
- * This is determined by platform clock/3 where the minimum platform
- * clock is 533MHz. This gives 5629 pico seconds.
- */
-#define MIN_PIX_CLK 5629
-#define MAX_PIX_CLK 96096
-
-#include <linux/types.h>
-
-struct mfb_alpha {
- int enable;
- int alpha;
-};
-
-struct mfb_chroma_key {
- int enable;
- __u8 red_max;
- __u8 green_max;
- __u8 blue_max;
- __u8 red_min;
- __u8 green_min;
- __u8 blue_min;
-};
-
-struct aoi_display_offset {
- int x_aoi_d;
- int y_aoi_d;
-};
-
-#define MFB_SET_CHROMA_KEY _IOW('M', 1, struct mfb_chroma_key)
-#define MFB_WAIT_FOR_VSYNC _IOW('F', 0x20, u_int32_t)
-#define MFB_SET_BRIGHTNESS _IOW('M', 3, __u8)
-
-#define MFB_SET_ALPHA 0x80014d00
-#define MFB_GET_ALPHA 0x40014d00
-#define MFB_SET_AOID 0x80084d04
-#define MFB_GET_AOID 0x40084d04
-#define MFB_SET_PIXFMT 0x80014d08
-#define MFB_GET_PIXFMT 0x40014d08
-
-#define FBIOGET_GWINFO 0x46E0
-#define FBIOPUT_GWINFO 0x46E1
-
-#ifdef __KERNEL__
-#include <linux/spinlock.h>
-
-/*
- * These are the fields of area descriptor(in DDR memory) for every plane
- */
-struct diu_ad {
- /* Word 0(32-bit) in DDR memory */
-/* __u16 comp; */
-/* __u16 pixel_s:2; */
-/* __u16 pallete:1; */
-/* __u16 red_c:2; */
-/* __u16 green_c:2; */
-/* __u16 blue_c:2; */
-/* __u16 alpha_c:3; */
-/* __u16 byte_f:1; */
-/* __u16 res0:3; */
-
- __be32 pix_fmt; /* hard coding pixel format */
-
- /* Word 1(32-bit) in DDR memory */
- __le32 addr;
-
- /* Word 2(32-bit) in DDR memory */
-/* __u32 delta_xs:11; */
-/* __u32 res1:1; */
-/* __u32 delta_ys:11; */
-/* __u32 res2:1; */
-/* __u32 g_alpha:8; */
- __le32 src_size_g_alpha;
-
- /* Word 3(32-bit) in DDR memory */
-/* __u32 delta_xi:11; */
-/* __u32 res3:5; */
-/* __u32 delta_yi:11; */
-/* __u32 res4:3; */
-/* __u32 flip:2; */
- __le32 aoi_size;
-
- /* Word 4(32-bit) in DDR memory */
- /*__u32 offset_xi:11;
- __u32 res5:5;
- __u32 offset_yi:11;
- __u32 res6:5;
- */
- __le32 offset_xyi;
-
- /* Word 5(32-bit) in DDR memory */
- /*__u32 offset_xd:11;
- __u32 res7:5;
- __u32 offset_yd:11;
- __u32 res8:5; */
- __le32 offset_xyd;
-
-
- /* Word 6(32-bit) in DDR memory */
- __u8 ckmax_r;
- __u8 ckmax_g;
- __u8 ckmax_b;
- __u8 res9;
-
- /* Word 7(32-bit) in DDR memory */
- __u8 ckmin_r;
- __u8 ckmin_g;
- __u8 ckmin_b;
- __u8 res10;
-/* __u32 res10:8; */
-
- /* Word 8(32-bit) in DDR memory */
- __le32 next_ad;
-
- /* Word 9(32-bit) in DDR memory, just for 64-bit aligned */
- __u32 paddr;
-} __attribute__ ((packed));
-
-/* DIU register map */
-struct diu {
- __be32 desc[3];
- __be32 gamma;
- __be32 pallete;
- __be32 cursor;
- __be32 curs_pos;
- __be32 diu_mode;
- __be32 bgnd;
- __be32 bgnd_wb;
- __be32 disp_size;
- __be32 wb_size;
- __be32 wb_mem_addr;
- __be32 hsyn_para;
- __be32 vsyn_para;
- __be32 syn_pol;
- __be32 thresholds;
- __be32 int_status;
- __be32 int_mask;
- __be32 colorbar[8];
- __be32 filling;
- __be32 plut;
-} __attribute__ ((packed));
-
-struct diu_hw {
- struct diu *diu_reg;
- spinlock_t reg_lock;
-
- __u32 mode; /* DIU operation mode */
-};
-
-struct diu_addr {
- __u8 __iomem *vaddr; /* Virtual address */
- dma_addr_t paddr; /* Physical address */
- __u32 offset;
-};
-
-struct diu_pool {
- struct diu_addr ad;
- struct diu_addr gamma;
- struct diu_addr pallete;
- struct diu_addr cursor;
-};
-
-#define FSL_DIU_BASE_OFFSET 0x2C000 /* Offset of DIU */
-#define INT_LCDC 64 /* DIU interrupt number */
-
-#define FSL_AOI_NUM 6 /* 5 AOIs and one dummy AOI */
- /* 1 for plane 0, 2 for plane 1&2 each */
-
-/* Minimum X and Y resolutions */
-#define MIN_XRES 64
-#define MIN_YRES 64
-
-/* HW cursor parameters */
-#define MAX_CURS 32
-
-/* Modes of operation of DIU */
-#define MFB_MODE0 0 /* DIU off */
-#define MFB_MODE1 1 /* All three planes output to display */
-#define MFB_MODE2 2 /* Plane 1 to display, planes 2+3 written back*/
-#define MFB_MODE3 3 /* All three planes written back to memory */
-#define MFB_MODE4 4 /* Color bar generation */
-
-/* INT_STATUS/INT_MASK field descriptions */
-#define INT_VSYNC 0x01 /* Vsync interrupt */
-#define INT_VSYNC_WB 0x02 /* Vsync interrupt for write back operation */
-#define INT_UNDRUN 0x04 /* Under run exception interrupt */
-#define INT_PARERR 0x08 /* Display parameters error interrupt */
-#define INT_LS_BF_VS 0x10 /* Lines before vsync. interrupt */
-
-/* Panels'operation modes */
-#define MFB_TYPE_OUTPUT 0 /* Panel output to display */
-#define MFB_TYPE_OFF 1 /* Panel off */
-#define MFB_TYPE_WB 2 /* Panel written back to memory */
-#define MFB_TYPE_TEST 3 /* Panel generate color bar */
-
-#endif /* __KERNEL__ */
-#endif /* __FSL_DIU_FB_H__ */
diff --git a/include/linux/fsl-diu-fb.h b/include/linux/fsl-diu-fb.h
new file mode 100644
index 0000000..fc295d7
--- /dev/null
+++ b/include/linux/fsl-diu-fb.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Freescale DIU Frame Buffer device driver
+ *
+ * Authors: Hongjun Chen <hong-jun.chen@freescale.com>
+ * Paul Widmer <paul.widmer@freescale.com>
+ * Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
+ * York Sun <yorksun@freescale.com>
+ *
+ * Based on imxfb.c Copyright (C) 2004 S.Hauer, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __FSL_DIU_FB_H__
+#define __FSL_DIU_FB_H__
+
+/* Arbitrary threshold to determine the allocation method
+ * See mpc8610fb_set_par(), map_video_memory(), and unmap_video_memory()
+ */
+#define MEM_ALLOC_THRESHOLD (1024*768*4+32)
+/* Minimum value that the pixel clock can be set to in pico seconds
+ * This is determined by platform clock/3 where the minimum platform
+ * clock is 533MHz. This gives 5629 pico seconds.
+ */
+#define MIN_PIX_CLK 5629
+#define MAX_PIX_CLK 96096
+
+#include <linux/types.h>
+
+struct mfb_alpha {
+ int enable;
+ int alpha;
+};
+
+struct mfb_chroma_key {
+ int enable;
+ __u8 red_max;
+ __u8 green_max;
+ __u8 blue_max;
+ __u8 red_min;
+ __u8 green_min;
+ __u8 blue_min;
+};
+
+struct aoi_display_offset {
+ int x_aoi_d;
+ int y_aoi_d;
+};
+
+#define MFB_SET_CHROMA_KEY _IOW('M', 1, struct mfb_chroma_key)
+#define MFB_WAIT_FOR_VSYNC _IOW('F', 0x20, u_int32_t)
+#define MFB_SET_BRIGHTNESS _IOW('M', 3, __u8)
+
+#define MFB_SET_ALPHA 0x80014d00
+#define MFB_GET_ALPHA 0x40014d00
+#define MFB_SET_AOID 0x80084d04
+#define MFB_GET_AOID 0x40084d04
+#define MFB_SET_PIXFMT 0x80014d08
+#define MFB_GET_PIXFMT 0x40014d08
+
+#define FBIOGET_GWINFO 0x46E0
+#define FBIOPUT_GWINFO 0x46E1
+
+#ifdef __KERNEL__
+#include <linux/spinlock.h>
+
+/*
+ * These are the fields of area descriptor(in DDR memory) for every plane
+ */
+struct diu_ad {
+ /* Word 0(32-bit) in DDR memory */
+/* __u16 comp; */
+/* __u16 pixel_s:2; */
+/* __u16 pallete:1; */
+/* __u16 red_c:2; */
+/* __u16 green_c:2; */
+/* __u16 blue_c:2; */
+/* __u16 alpha_c:3; */
+/* __u16 byte_f:1; */
+/* __u16 res0:3; */
+
+ __be32 pix_fmt; /* hard coding pixel format */
+
+ /* Word 1(32-bit) in DDR memory */
+ __le32 addr;
+
+ /* Word 2(32-bit) in DDR memory */
+/* __u32 delta_xs:11; */
+/* __u32 res1:1; */
+/* __u32 delta_ys:11; */
+/* __u32 res2:1; */
+/* __u32 g_alpha:8; */
+ __le32 src_size_g_alpha;
+
+ /* Word 3(32-bit) in DDR memory */
+/* __u32 delta_xi:11; */
+/* __u32 res3:5; */
+/* __u32 delta_yi:11; */
+/* __u32 res4:3; */
+/* __u32 flip:2; */
+ __le32 aoi_size;
+
+ /* Word 4(32-bit) in DDR memory */
+ /*__u32 offset_xi:11;
+ __u32 res5:5;
+ __u32 offset_yi:11;
+ __u32 res6:5;
+ */
+ __le32 offset_xyi;
+
+ /* Word 5(32-bit) in DDR memory */
+ /*__u32 offset_xd:11;
+ __u32 res7:5;
+ __u32 offset_yd:11;
+ __u32 res8:5; */
+ __le32 offset_xyd;
+
+
+ /* Word 6(32-bit) in DDR memory */
+ __u8 ckmax_r;
+ __u8 ckmax_g;
+ __u8 ckmax_b;
+ __u8 res9;
+
+ /* Word 7(32-bit) in DDR memory */
+ __u8 ckmin_r;
+ __u8 ckmin_g;
+ __u8 ckmin_b;
+ __u8 res10;
+/* __u32 res10:8; */
+
+ /* Word 8(32-bit) in DDR memory */
+ __le32 next_ad;
+
+ /* Word 9(32-bit) in DDR memory, just for 64-bit aligned */
+ __u32 paddr;
+} __attribute__ ((packed));
+
+/* DIU register map */
+struct diu {
+ __be32 desc[3];
+ __be32 gamma;
+ __be32 pallete;
+ __be32 cursor;
+ __be32 curs_pos;
+ __be32 diu_mode;
+ __be32 bgnd;
+ __be32 bgnd_wb;
+ __be32 disp_size;
+ __be32 wb_size;
+ __be32 wb_mem_addr;
+ __be32 hsyn_para;
+ __be32 vsyn_para;
+ __be32 syn_pol;
+ __be32 thresholds;
+ __be32 int_status;
+ __be32 int_mask;
+ __be32 colorbar[8];
+ __be32 filling;
+ __be32 plut;
+} __attribute__ ((packed));
+
+struct diu_hw {
+ struct diu *diu_reg;
+ spinlock_t reg_lock;
+
+ __u32 mode; /* DIU operation mode */
+};
+
+struct diu_addr {
+ __u8 __iomem *vaddr; /* Virtual address */
+ dma_addr_t paddr; /* Physical address */
+ __u32 offset;
+};
+
+struct diu_pool {
+ struct diu_addr ad;
+ struct diu_addr gamma;
+ struct diu_addr pallete;
+ struct diu_addr cursor;
+};
+
+#define FSL_DIU_BASE_OFFSET 0x2C000 /* Offset of DIU */
+#define INT_LCDC 64 /* DIU interrupt number */
+
+#define FSL_AOI_NUM 6 /* 5 AOIs and one dummy AOI */
+ /* 1 for plane 0, 2 for plane 1&2 each */
+
+/* Minimum X and Y resolutions */
+#define MIN_XRES 64
+#define MIN_YRES 64
+
+/* HW cursor parameters */
+#define MAX_CURS 32
+
+/* Modes of operation of DIU */
+#define MFB_MODE0 0 /* DIU off */
+#define MFB_MODE1 1 /* All three planes output to display */
+#define MFB_MODE2 2 /* Plane 1 to display, planes 2+3 written back*/
+#define MFB_MODE3 3 /* All three planes written back to memory */
+#define MFB_MODE4 4 /* Color bar generation */
+
+/* INT_STATUS/INT_MASK field descriptions */
+#define INT_VSYNC 0x01 /* Vsync interrupt */
+#define INT_VSYNC_WB 0x02 /* Vsync interrupt for write back operation */
+#define INT_UNDRUN 0x04 /* Under run exception interrupt */
+#define INT_PARERR 0x08 /* Display parameters error interrupt */
+#define INT_LS_BF_VS 0x10 /* Lines before vsync. interrupt */
+
+/* Panels'operation modes */
+#define MFB_TYPE_OUTPUT 0 /* Panel output to display */
+#define MFB_TYPE_OFF 1 /* Panel off */
+#define MFB_TYPE_WB 2 /* Panel written back to memory */
+#define MFB_TYPE_TEST 3 /* Panel generate color bar */
+
+#endif /* __KERNEL__ */
+#endif /* __FSL_DIU_FB_H__ */
--
1.6.3.3
^ permalink raw reply related
* Re: [PATCH v3 09/11] powerpc/mpc5121: shared DIU framebuffer support
From: Anatolij Gustschin @ 2010-02-27 22:09 UTC (permalink / raw)
To: Grant Likely; +Cc: wd, dzu, John Rigby, linuxppc-dev, York Sun
In-Reply-To: <fa686aa41002161006l3b301febt94fe990df4bddfe9@mail.gmail.com>
On Tue, 16 Feb 2010 11:06:22 -0700
Grant Likely <grant.likely@secretlab.ca> wrote:
> [...]
> > diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
> > index 72d68b3..29c7f31 100644
> > --- a/drivers/video/fsl-diu-fb.c
> > +++ b/drivers/video/fsl-diu-fb.c
> > @@ -34,7 +34,7 @@
> > =C2=A0#include <linux/of_platform.h>
> >
> > =C2=A0#include <sysdev/fsl_soc.h>
> > -#include "fsl-diu-fb.h"
> > +#include <linux/fsl-diu-fb.h>
> >
> > =C2=A0/*
> > =C2=A0* These parameters give default parameters
> > @@ -178,6 +178,21 @@ static struct fb_videomode __devinitdata fsl_diu_m=
ode_db[] =3D {
> > =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0.sync =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =3D FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
> > =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0.vmode =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0=3D FB_VMODE_NONINTERLACED
> > =C2=A0 =C2=A0 =C2=A0 =C2=A0},
> > + =C2=A0 =C2=A0 =C2=A0 {
> > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .name =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =3D "800x480-60",
> > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .refresh =C2=A0 =C2=
=A0 =C2=A0 =C2=A0=3D 60,
> > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .xres =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =3D 800,
> > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .yres =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =3D 480,
> > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .pixclock =C2=A0 =C2=
=A0 =C2=A0 =3D 31250,
> > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .left_margin =C2=A0 =
=C2=A0=3D 86,
> > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .right_margin =C2=A0=
=3D 42,
> > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .upper_margin =C2=A0=
=3D 33,
> > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .lower_margin =C2=A0=
=3D 10,
> > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .hsync_len =C2=A0 =
=C2=A0 =C2=A0=3D 128,
> > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .vsync_len =C2=A0 =
=C2=A0 =C2=A0=3D 2,
> > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .sync =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =3D FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
> > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .vmode =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0=3D FB_VMODE_NONINTERLACED
> > + =C2=A0 =C2=A0 =C2=A0 },
> > =C2=A0};
>=20
> This hunk bothers me. It looks like the type of data that belongs
> either in some common shared .c file, or encoded into the device tree.
> It seems to be data about the display panel, instead of data about
> the framebuffer driver. I know that the driver already uses this
> pattern, but before I merge this patch and further rely on that
> pattern, I think it is worth discussing.
In the updated DIU patches I don't add panel timing data to the
framebuffer driver. It is encoded in the device tree now. If this
is acceptable, I'll send another patch adding the documentation
for added bindings.
Thanks,
Anatolij
^ permalink raw reply
* PCIe switch driver in Linux 2.6.29
From: Hu, Bingquan (NSN - CN/Shanghai) @ 2010-02-27 23:59 UTC (permalink / raw)
To: linuxppc-dev
Hi,
I am new to this mail list and this is my first post, I apologize if I =
break some rules.
Description of the problem:
We incorporated Freescale MPC8548E CPU, PLX PEX8648 PCIe switch and =
Altera FPGA into our solution.
FPGA has one prefetchable 64-bit BAR 0 and 32-bit non-prefetchable =
Bar2.It was recognized successfully,as well as the PCIe switch.
But there are resources collisions on the upstream bridge and the =
downstream bridge connected to FPGA.
the startup log, as well the device tree, is shown below.
My question is:=20
am I missing something in the device tree that standa for the PCIe =
switch?
Do I need to code in the Linux kernel to fix this problem?
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D startup log =
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D
pci 0000:01:00.0: device not available because of BAR 0 =
[0xb0000000-0xb001ffff] collisions
pci 0000:01:00.0: device not available because of BAR 8 =
[0xb0000000-0xb00fffff] collisions
pci 0000:01:00.0: device not available because of BAR 9 =
[0xa0000000-0xa0ffffff] collisions
pci 0000:02:08.0: device not available because of BAR 8 =
[0xb0000000-0xb00fffff] collisions
pci 0000:02:08.0: device not available because of BAR 9 =
[0xa0000000-0xa0ffffff] collisions=20
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D device tree regarding PCIe =
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
pci2: pcie@e000a000 {
cell-index =3D <2>;
interrupt-map-mask =3D <0xf800 0x0 0x0 0x7>;
interrupt-map =3D <
/* IDSEL 0x0 (PEX) */
00000 0x0 0x0 0x1 &mpic 0x0 0x1
00000 0x0 0x0 0x2 &mpic 0x1 0x1
00000 0x0 0x0 0x3 &mpic 0x2 0x1
00000 0x0 0x0 0x4 &mpic 0x3 0x1>;
interrupt-parent =3D <&mpic>;
interrupts =3D <26 2>;
bus-range =3D <0 255>;
ranges =3D <0x2000000 0x0 0x10000000 0xb0000000 0x0 0x10000000
0x43000000 0x0 0x0 0xa0000000 0x0 0x10000000
0x1000000 0x0 0x0 0xe3000000 0x0 0x100000>;
//ranges =3D empty;
clock-frequency =3D <33333333>;
#interrupt-cells =3D <1>;
#size-cells =3D <2>;
#address-cells =3D <3>;
reg =3D <0xe000a000 0x1000>;
compatible =3D "fsl,mpc8548-pcie";
device_type =3D "pci";
pcie@0 {
reg =3D <0x0 0x0 0x0 0x0 0x0>;
#size-cells =3D <2>;
#address-cells =3D <3>;
device_type =3D "pci";
//ranges =3D empty;
ranges =3D <0x2000000 0x0 0x10000000
0x20000000 0x0 0x0
0x0 0x10000000
0x43000000 0x0 0xa0000000
0x43000000 0x0 0xa0000000
0x0 0x10000000
0x1000000 0x0 0x0
0x1000000 0x0 0x0
0x0 0x100000>;
=20
};
};
^ permalink raw reply
* Re: Proposal to move PCI out of arch/powerpc and into drivers/of
From: Benjamin Herrenschmidt @ 2010-02-28 0:32 UTC (permalink / raw)
To: John Linn
Cc: devicetree-discuss, michal.simek, microblaze-uclinux,
linux-kernel, linuxppc-dev, John Williams
In-Reply-To: <27bab769-0788-4b48-96a7-ced65e40144c@SG2EHSMHS007.ehs.local>
On Fri, 2010-02-26 at 16:07 -0700, John Linn wrote:
> Hi all,
>
> We are in the process of putting PCI/PCIe into the microblaze
> architecture.
>
> In order to not duplicate/fork the PCI code in Powerpc, we're proposing
> to move the PCI code from arch/powerpc into drivers/of such that it
> would be common code for Powerpc and MicroBlaze.
>
> This would be the 1st part of a refactoring that would occur with the
> PCI code.
>
> Ben H., would you mind if that happened (move arch/powerpc/kernel/pci*
> to drivers/of/*)?
I don't think we should move the whole thing. I know Grant plans to move
parts of it though. There's quite a few things in there that are still
quite powerpc specific (and even different between ppc32 and ppc64).
I wouldn't mind in the long run if we could make some of powerpc's PCI
implementation bits more generic, such as struct pci_controller, and the
MMIO/PIO remapping mechanisms, since many archs do more or less the same
thing and all in subtely different ways. But that would have to go to
drivers/pci.
Then there's the whole deal with matching OF nodes with PCI devices. A
lot of that is still different even between ppc32 and ppc64. I don't
think any of that is ready to be moved to drivers/of, it's not generic
enough by far.
Cheers,
Ben.
^ permalink raw reply
* Re: Proposal to move PCI out of arch/powerpc and into drivers/of
From: Benjamin Herrenschmidt @ 2010-02-28 0:33 UTC (permalink / raw)
To: Jesse Barnes
Cc: devicetree-discuss, michal.simek, microblaze-uclinux,
linux-kernel, linuxppc-dev, John Linn, John Williams
In-Reply-To: <20100226154425.00dde291@jbarnes-piketon>
On Fri, 2010-02-26 at 15:44 -0800, Jesse Barnes wrote:
>
> Dave and Ben have been talking about this for awhile; seems like a
> good
> excuse to do it. IIRC they had even thought about just dumping the
> code directly into drivers/pci so that API updates won't get
> missed...
Depends which part. Not -all- of our PCI code as John seems to want.
The bits that generate PCI devices from the device-tree, for sure,
though again, even there, there are quite a few dependencies on
constructs and data structures that are powerpc specific, or even ppc64
specific in some cases, and that would have to be cleaned up and/or
abstracted first.
Cheers,
Ben.
^ permalink raw reply
* Re: [PATCH 1/3] video: add support for getting video mode from device tree
From: Grant Likely @ 2010-02-28 6:30 UTC (permalink / raw)
To: Anatolij Gustschin
Cc: linux-fbdev, wd, dzu, jrigby, devicetree-discuss, linuxppc-dev,
yorksun
In-Reply-To: <1267307902-31939-2-git-send-email-agust@denx.de>
Hi Anatolij,
[added cc: to devicetree-discuss@lists.ozlabs.org]
On Sat, Feb 27, 2010 at 2:58 PM, Anatolij Gustschin <agust@denx.de> wrote:
> Framebuffer drivers may want to get panel timing info
> from the device tree. This patch adds appropriate support.
> Subsequent patch for FSL DIU frame buffer driver makes use
> of this functionality.
I think this is moving in the right direction, but there needs to
debate & review over the binding before committing to anything.
Please write a patch that documents the new binding in
Documentation/powerpc/dts-bindings. All new bindings should be
documented and reviewed on devicetree-discuss before merging any
drivers that use them into mainline.
>From what I can tell by reading your code, I suspect that the binding
you've designed will solve your immediate problem, but won't be able
to handle anything slightly more complex, but it also looks like the
binding has been designed to be generic, usable by any display device.
First off, I did a tiny amount of research, and I didn't find any
existing OpenFirmware bindings for describing video displays.
Otherwise, I'd suggest considering that.
>From the little bit that I know, it seems that for most video devices
(ie. PCs) the video card discovers the capabilities of the screen by
reading the monitor's EDID data. However, in your particular case
embedded case, a fixed flat panel is attached, and there isn't any
EDID data provided. Therefore, you need an alternate method of
describing the display capabilities. Rather than designing something
entirely new, you may want to consider using the EDID data format
directly; or at least cover the same things that EDID describes. The
downside to using EDID directly is that it is a packed binary format
that isn't parseable by mere mortals; but the data contained in it
seems about right. The upside is the kernel already knows what to do
with EDID data.
Otherwise you risk designing something that won't be useful for
anything much outside of your own use case. For example, the binding
I see from the code cannot handle a display with multiple output
modes.
Also, since you're now in the realm of describing a video display,
which is separate from the display controller, you should consider
describing the display in a separate device tree node. Maybe
something like this...
video {
compatible =3D "fsl,mpc5121-diu";
display {
compatible =3D "<vendor>,<model>";
edid =3D [edid-data];
};
};
Cheers,
g.
>
> Signed-off-by: Anatolij Gustschin <agust@denx.de>
> ---
> =A0drivers/video/Kconfig =A0| =A0 =A05 +++
> =A0drivers/video/Makefile | =A0 =A01 +
> =A0drivers/video/ofmode.c | =A0 95 ++++++++++++++++++++++++++++++++++++++=
++++++++++
> =A0drivers/video/ofmode.h | =A0 =A06 +++
> =A04 files changed, 107 insertions(+), 0 deletions(-)
> =A0create mode 100644 drivers/video/ofmode.c
> =A0create mode 100644 drivers/video/ofmode.h
>
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 5a5c303..dc1beb0 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -203,6 +203,11 @@ config FB_MACMODES
> =A0 =A0 =A0 =A0depends on FB
> =A0 =A0 =A0 =A0default n
>
> +config FB_OF_MODE
> + =A0 =A0 =A0 tristate
> + =A0 =A0 =A0 depends on FB && OF
> + =A0 =A0 =A0 default n
> +
> =A0config FB_BACKLIGHT
> =A0 =A0 =A0 =A0bool
> =A0 =A0 =A0 =A0depends on FB
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 4ecb30c..c4a912b 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -26,6 +26,7 @@ obj-$(CONFIG_FB_SVGALIB) =A0 =A0 =A0 +=3D svgalib.o
> =A0obj-$(CONFIG_FB_MACMODES) =A0 =A0 =A0+=3D macmodes.o
> =A0obj-$(CONFIG_FB_DDC) =A0 =A0 =A0 =A0 =A0 +=3D fb_ddc.o
> =A0obj-$(CONFIG_FB_DEFERRED_IO) =A0 +=3D fb_defio.o
> +obj-$(CONFIG_FB_OF_MODE) =A0 =A0 =A0 +=3D ofmode.o
>
> =A0# Hardware specific drivers go first
> =A0obj-$(CONFIG_FB_AMIGA) =A0 =A0 =A0 =A0 =A0 =A0+=3D amifb.o c2p_planar.=
o
> diff --git a/drivers/video/ofmode.c b/drivers/video/ofmode.c
> new file mode 100644
> index 0000000..78caad1
> --- /dev/null
> +++ b/drivers/video/ofmode.c
> @@ -0,0 +1,95 @@
> +/*
> + * Get video mode settings from Flattened Device Tree.
> + *
> + * Copyright (C) 2010 DENX Software Engineering.
> + * Anatolij Gustschin <agust@denx.de>
> + *
> + * This file is subject to the terms and conditions of the GNU General
> + * Public License version 2. See the file COPYING in the main directory
> + * of this archive for more details.
> + */
> +
> +#include <linux/fb.h>
> +#include <linux/of.h>
> +
> +int __devinit of_get_video_mode(struct device_node *np,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct fb_i=
nfo *info)
> +{
> + =A0 =A0 =A0 struct fb_videomode dt_mode;
> + =A0 =A0 =A0 const u32 *prop;
> + =A0 =A0 =A0 unsigned int len;
> +
> + =A0 =A0 =A0 if (!np || !info)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> +
> + =A0 =A0 =A0 prop =3D of_get_property(np, "width", &len);
> + =A0 =A0 =A0 if (!prop || len !=3D sizeof(u32))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err;
> + =A0 =A0 =A0 dt_mode.xres =3D *prop;
> +
> + =A0 =A0 =A0 prop =3D of_get_property(np, "height", &len);
> + =A0 =A0 =A0 if (!prop || len !=3D sizeof(u32))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err;
> + =A0 =A0 =A0 dt_mode.yres =3D *prop;
> +
> + =A0 =A0 =A0 prop =3D of_get_property(np, "depth", &len);
> + =A0 =A0 =A0 if (!prop || len !=3D sizeof(u32))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err;
> + =A0 =A0 =A0 info->var.bits_per_pixel =3D *prop;
> +
> + =A0 =A0 =A0 prop =3D of_get_property(np, "linebytes", &len);
> + =A0 =A0 =A0 if (!prop || len !=3D sizeof(u32))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err;
> + =A0 =A0 =A0 info->fix.line_length =3D *prop;
> +
> + =A0 =A0 =A0 prop =3D of_get_property(np, "refresh", &len);
> + =A0 =A0 =A0 if (prop && len =3D=3D sizeof(u32))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dt_mode.refresh =3D *prop; /* optional */
> +
> + =A0 =A0 =A0 prop =3D of_get_property(np, "pixclock", &len);
> + =A0 =A0 =A0 if (!prop || len !=3D sizeof(u32))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err;
> + =A0 =A0 =A0 dt_mode.pixclock =3D *prop;
> +
> + =A0 =A0 =A0 prop =3D of_get_property(np, "left_margin", &len);
> + =A0 =A0 =A0 if (!prop || len !=3D sizeof(u32))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err;
> + =A0 =A0 =A0 dt_mode.left_margin =3D *prop;
> +
> + =A0 =A0 =A0 prop =3D of_get_property(np, "right_margin", &len);
> + =A0 =A0 =A0 if (!prop || len !=3D sizeof(u32))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err;
> + =A0 =A0 =A0 dt_mode.right_margin =3D *prop;
> +
> + =A0 =A0 =A0 prop =3D of_get_property(np, "upper_margin", &len);
> + =A0 =A0 =A0 if (!prop || len !=3D sizeof(u32))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err;
> + =A0 =A0 =A0 dt_mode.upper_margin =3D *prop;
> +
> + =A0 =A0 =A0 prop =3D of_get_property(np, "lower_margin", &len);
> + =A0 =A0 =A0 if (!prop || len !=3D sizeof(u32))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err;
> + =A0 =A0 =A0 dt_mode.lower_margin =3D *prop;
> +
> + =A0 =A0 =A0 prop =3D of_get_property(np, "hsync_len", &len);
> + =A0 =A0 =A0 if (!prop || len !=3D sizeof(u32))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err;
> + =A0 =A0 =A0 dt_mode.hsync_len =3D *prop;
> +
> + =A0 =A0 =A0 prop =3D of_get_property(np, "vsync_len", &len);
> + =A0 =A0 =A0 if (!prop || len !=3D sizeof(u32))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err;
> + =A0 =A0 =A0 dt_mode.vsync_len =3D *prop;
> +
> + =A0 =A0 =A0 prop =3D of_get_property(np, "sync", &len);
> + =A0 =A0 =A0 if (!prop || len !=3D sizeof(u32))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err;
> + =A0 =A0 =A0 dt_mode.sync =3D *prop;
> +
> + =A0 =A0 =A0 fb_videomode_to_var(&info->var, &dt_mode);
> +
> + =A0 =A0 =A0 return 0;
> +err:
> + =A0 =A0 =A0 pr_err("%s: Can't find expected mode entry\n", np->full_nam=
e);
> + =A0 =A0 =A0 return -EINVAL;
> +}
> diff --git a/drivers/video/ofmode.h b/drivers/video/ofmode.h
> new file mode 100644
> index 0000000..9a13bec
> --- /dev/null
> +++ b/drivers/video/ofmode.h
> @@ -0,0 +1,6 @@
> +#ifndef _OFMODE_H
> +#define _OFMODE_H
> +
> +extern int __devinit of_get_video_mode(struct device_node *np,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 struct fb_info *info);
> +#endif
> --
> 1.6.3.3
>
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH 3/3] powerpc/mpc5121: shared DIU framebuffer support
From: Grant Likely @ 2010-02-28 6:50 UTC (permalink / raw)
To: Anatolij Gustschin; +Cc: linux-fbdev, wd, dzu, jrigby, linuxppc-dev, yorksun
In-Reply-To: <1267307902-31939-4-git-send-email-agust@denx.de>
On Sat, Feb 27, 2010 at 2:58 PM, Anatolij Gustschin <agust@denx.de> wrote:
> MPC5121 DIU configuration/setup as initialized by the boot
> loader currently will get lost while booting Linux. As a
> result displaying the boot splash is not possible through
> the boot process.
>
> To prevent this we reserve configured DIU frame buffer
> address range while booting and preserve AOI descriptor
> and gamma table so that DIU continues displaying through
> the whole boot process. On first open from user space
> DIU frame buffer driver releases the reserved frame
> buffer area and continues to operate as usual.
>
> The patch also moves drivers/video/fsl-diu-fb.h file to
> include/linux as we use some DIU structures in platform
> code.
>
> 'diu_ops' callbacks in platform code borrowed from John's
> DIU code.
>
> Signed-off-by: John Rigby <jrigby@gmail.com>
> Signed-off-by: Anatolij Gustschin <agust@denx.de>
> Cc: Grant Likely <grant.likely@secretlab.ca>
On quick glance this patch seems mostly okay, but this patch should
probably be broken up a bit to simplify review and dissociate
unrelated changes. For example, the move of fsl-diu-fb.h is a
discrete change that should be split off. Some more comments
below....
> ---
> =A0arch/powerpc/platforms/512x/mpc5121_ads.c =A0 =A0 | =A0 =A07 +
> =A0arch/powerpc/platforms/512x/mpc5121_generic.c | =A0 13 ++
> =A0arch/powerpc/platforms/512x/mpc512x.h =A0 =A0 =A0 =A0 | =A0 =A03 +
> =A0arch/powerpc/platforms/512x/mpc512x_shared.c =A0| =A0282 +++++++++++++=
++++++++++++
> =A0arch/powerpc/sysdev/fsl_soc.h =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 | =A0 =
=A01 +
> =A0drivers/video/fsl-diu-fb.c =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0| =
=A0 25 ++-
> =A0drivers/video/fsl-diu-fb.h =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0| =
=A0223 -------------------
> =A0include/linux/fsl-diu-fb.h =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0| =
=A0223 +++++++++++++++++++
> =A08 files changed, 549 insertions(+), 228 deletions(-)
> =A0delete mode 100644 drivers/video/fsl-diu-fb.h
> =A0create mode 100644 include/linux/fsl-diu-fb.h
>
> diff --git a/arch/powerpc/platforms/512x/mpc5121_ads.c b/arch/powerpc/pla=
tforms/512x/mpc5121_ads.c
> index ee6ae12..aa4d5a8 100644
> --- a/arch/powerpc/platforms/512x/mpc5121_ads.c
> +++ b/arch/powerpc/platforms/512x/mpc5121_ads.c
> @@ -42,6 +42,7 @@ static void __init mpc5121_ads_setup_arch(void)
> =A0 =A0 =A0 =A0for_each_compatible_node(np, "pci", "fsl,mpc5121-pci")
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0mpc83xx_add_bridge(np);
> =A0#endif
> + =A0 =A0 =A0 mpc512x_setup_diu();
> =A0}
>
> =A0static void __init mpc5121_ads_init_IRQ(void)
> @@ -60,11 +61,17 @@ static int __init mpc5121_ads_probe(void)
> =A0 =A0 =A0 =A0return of_flat_dt_is_compatible(root, "fsl,mpc5121ads");
> =A0}
>
> +void __init mpc5121_ads_init_early(void)
> +{
> + =A0 =A0 =A0 mpc512x_init_diu();
> +}
> +
> =A0define_machine(mpc5121_ads) {
> =A0 =A0 =A0 =A0.name =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D "MPC5121 ADS=
",
> =A0 =A0 =A0 =A0.probe =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D mpc5121_ads_=
probe,
> =A0 =A0 =A0 =A0.setup_arch =A0 =A0 =A0 =A0 =A0 =A0 =3D mpc5121_ads_setup_=
arch,
> =A0 =A0 =A0 =A0.init =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D mpc512x_init=
,
> + =A0 =A0 =A0 .init_early =A0 =A0 =A0 =A0 =A0 =A0 =3D mpc5121_ads_init_ea=
rly,
> =A0 =A0 =A0 =A0.init_IRQ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D mpc5121_ads_init=
_IRQ,
> =A0 =A0 =A0 =A0.get_irq =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D ipic_get_irq,
> =A0 =A0 =A0 =A0.calibrate_decr =A0 =A0 =A0 =A0 =3D generic_calibrate_decr=
,
> diff --git a/arch/powerpc/platforms/512x/mpc5121_generic.c b/arch/powerpc=
/platforms/512x/mpc5121_generic.c
> index a6c0e3a..3aaa281 100644
> --- a/arch/powerpc/platforms/512x/mpc5121_generic.c
> +++ b/arch/powerpc/platforms/512x/mpc5121_generic.c
> @@ -28,6 +28,7 @@
> =A0*/
> =A0static char *board[] __initdata =3D {
> =A0 =A0 =A0 =A0"prt,prtlvt",
> + =A0 =A0 =A0 "ifm,pdm360ng",
You're adding a new board here. This is completely unrelated.
> =A0 =A0 =A0 =A0NULL
> =A0};
>
> @@ -48,10 +49,22 @@ static int __init mpc5121_generic_probe(void)
> =A0 =A0 =A0 =A0return board[i] !=3D NULL;
> =A0}
>
> +void __init mpc512x_generic_init_early(void)
> +{
> + =A0 =A0 =A0 mpc512x_init_diu();
> +}
> +
> +void __init mpc512x_generic_setup_arch(void)
> +{
> + =A0 =A0 =A0 mpc512x_setup_diu();
> +}
> +
> =A0define_machine(mpc5121_generic) {
> =A0 =A0 =A0 =A0.name =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D "MPC5121 gen=
eric",
> =A0 =A0 =A0 =A0.probe =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D mpc5121_gene=
ric_probe,
> =A0 =A0 =A0 =A0.init =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D mpc512x_init=
,
> + =A0 =A0 =A0 .init_early =A0 =A0 =A0 =A0 =A0 =A0 =3D mpc512x_generic_ini=
t_early,
> + =A0 =A0 =A0 .setup_arch =A0 =A0 =A0 =A0 =A0 =A0 =3D mpc512x_generic_set=
up_arch,
> =A0 =A0 =A0 =A0.init_IRQ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D mpc512x_init_IRQ=
,
> =A0 =A0 =A0 =A0.get_irq =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D ipic_get_irq,
> =A0 =A0 =A0 =A0.calibrate_decr =A0 =A0 =A0 =A0 =3D generic_calibrate_decr=
,
> diff --git a/arch/powerpc/platforms/512x/mpc512x.h b/arch/powerpc/platfor=
ms/512x/mpc512x.h
> index d72b2c7..1cfe9d5 100644
> --- a/arch/powerpc/platforms/512x/mpc512x.h
> +++ b/arch/powerpc/platforms/512x/mpc512x.h
> @@ -17,4 +17,7 @@ extern int __init mpc5121_clk_init(void);
> =A0void __init mpc512x_declare_of_platform_devices(void);
> =A0extern void mpc512x_restart(char *cmd);
> =A0extern void __init mpc5121_usb_init(void);
> +extern void __init mpc512x_init_diu(void);
> +extern void __init mpc512x_setup_diu(void);
__init annotations do not belong in header files.
> +extern struct fsl_diu_shared_fb diu_shared_fb;
Hmmmm. I'm not fond of the global data structure. Especially
considering that the struct fsl_diu_shared_fb is defined in
mpc512x_shared.c, so nothing outside of that .c file can do anything
with the structure.
> =A0#endif =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* __MPC512X_H_=
_ */
> diff --git a/arch/powerpc/platforms/512x/mpc512x_shared.c b/arch/powerpc/=
platforms/512x/mpc512x_shared.c
> index fbdf65f..a8c50a6 100644
> --- a/arch/powerpc/platforms/512x/mpc512x_shared.c
> +++ b/arch/powerpc/platforms/512x/mpc512x_shared.c
> @@ -16,7 +16,11 @@
> =A0#include <linux/io.h>
> =A0#include <linux/irq.h>
> =A0#include <linux/of_platform.h>
> +#include <linux/fsl-diu-fb.h>
> +#include <linux/bootmem.h>
> +#include <sysdev/fsl_soc.h>
>
> +#include <asm/cacheflush.h>
> =A0#include <asm/machdep.h>
> =A0#include <asm/ipic.h>
> =A0#include <asm/prom.h>
> @@ -53,6 +57,284 @@ void mpc512x_restart(char *cmd)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0;
> =A0}
>
> +struct fsl_diu_shared_fb {
> + =A0 =A0 =A0 char =A0 =A0 =A0 =A0 =A0 =A0gamma[0x300]; =A0 /* 32-bit ali=
gned! */
> + =A0 =A0 =A0 struct diu_ad =A0 ad0; =A0 =A0 =A0 =A0 =A0 =A0/* 32-bit ali=
gned! */
> + =A0 =A0 =A0 phys_addr_t =A0 =A0 fb_phys;
> + =A0 =A0 =A0 size_t =A0 =A0 =A0 =A0 =A0fb_len;
> + =A0 =A0 =A0 bool =A0 =A0 =A0 =A0 =A0 =A0in_use;
> +};
> +
> +unsigned int mpc512x_get_pixel_format(unsigned int bits_per_pixel,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
int monitor_port)
> +{
> + =A0 =A0 =A0 unsigned int pix_fmt;
> +
> + =A0 =A0 =A0 switch (bits_per_pixel) {
> + =A0 =A0 =A0 case 32:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pix_fmt =3D 0x88883316;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case 24:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pix_fmt =3D 0x88082219;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case 16:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pix_fmt =3D 0x65053118;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pix_fmt =3D 0x00000400;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 return pix_fmt;
> +}
> +
> +void mpc512x_set_gamma_table(int monitor_port, char *gamma_table_base)
> +{
> +}
> +
> +void mpc512x_set_monitor_port(int monitor_port)
> +{
> +}
> +
> +#define CCM_SCFR1 =A0 =A0 =A00x0000000c
> +#define DIU_DIV_MASK =A0 0x000000ff
> +void mpc512x_set_pixel_clock(unsigned int pixclock)
> +{
> + =A0 =A0 =A0 unsigned long bestval, bestfreq, speed_ccb, busfreq;
> + =A0 =A0 =A0 unsigned long minpixclock, maxpixclock, pixval;
> + =A0 =A0 =A0 struct device_node *np;
> + =A0 =A0 =A0 void __iomem *ccm;
> + =A0 =A0 =A0 u32 temp;
> + =A0 =A0 =A0 long err;
> + =A0 =A0 =A0 int i;
> +
> + =A0 =A0 =A0 np =3D of_find_compatible_node(NULL, NULL, "fsl,mpc5121-clo=
ck");
> + =A0 =A0 =A0 if (!np) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("Can't find clock control module.\n"=
);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 ccm =3D of_iomap(np, 0);
> + =A0 =A0 =A0 if (!ccm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("Can't map clock control module reg.=
\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 of_node_put(np);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 of_node_put(np);
> +
> + =A0 =A0 =A0 busfreq =3D 200000000;
Instead of some hard coding some bogus defalt busfreq, you should
error out if the real frequency cannot be determined. Force users to
supply a valid tree.
> + =A0 =A0 =A0 np =3D of_find_node_by_type(NULL, "cpu");
> + =A0 =A0 =A0 if (np) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 unsigned int size;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 const unsigned int *prop =3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 of_get_property(np, "bus-fr=
equency", &size);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (prop)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 busfreq =3D *prop;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 of_node_put(np);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* Pixel Clock configuration */
> + =A0 =A0 =A0 pr_debug("DIU: Bus Frequency =3D %lu\n", busfreq);
> + =A0 =A0 =A0 speed_ccb =3D busfreq * 4;
> +
> + =A0 =A0 =A0 /* Calculate the pixel clock with the smallest error */
> + =A0 =A0 =A0 /* calculate the following in steps to avoid overflow */
> + =A0 =A0 =A0 pr_debug("DIU pixclock in ps - %d\n", pixclock);
> + =A0 =A0 =A0 temp =3D 1;
> + =A0 =A0 =A0 temp *=3D 1000000000;
> + =A0 =A0 =A0 temp /=3D pixclock;
> + =A0 =A0 =A0 temp *=3D 1000;
> + =A0 =A0 =A0 pixclock =3D temp;
Really? I think you can simplify this.
> + =A0 =A0 =A0 pr_debug("DIU pixclock freq - %u\n", pixclock);
> +
> + =A0 =A0 =A0 temp *=3D 5;
> + =A0 =A0 =A0 temp /=3D 100; =A0/* pixclock * 0.05 */
> + =A0 =A0 =A0 pr_debug("deviation =3D %d\n", temp);
> + =A0 =A0 =A0 minpixclock =3D pixclock - temp;
> + =A0 =A0 =A0 maxpixclock =3D pixclock + temp;
> + =A0 =A0 =A0 pr_debug("DIU minpixclock - %lu\n", minpixclock);
> + =A0 =A0 =A0 pr_debug("DIU maxpixclock - %lu\n", maxpixclock);
> + =A0 =A0 =A0 pixval =3D speed_ccb/pixclock;
> + =A0 =A0 =A0 pr_debug("DIU pixval =3D %lu\n", pixval);
> +
> + =A0 =A0 =A0 err =3D 100000000;
> + =A0 =A0 =A0 bestval =3D pixval;
> + =A0 =A0 =A0 pr_debug("DIU bestval =3D %lu\n", bestval);
> +
> + =A0 =A0 =A0 bestfreq =3D 0;
> + =A0 =A0 =A0 for (i =3D -1; i <=3D 1; i++) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 temp =3D speed_ccb / (pixval+i);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("DIU test pixval i=3D%d, pixval=3D=
%lu, temp freq. =3D %u\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 i, pixval, temp);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if ((temp < minpixclock) || (temp > maxpixc=
lock))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("DIU exceeds monit=
or range (%lu to %lu)\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 minpixclock=
, maxpixclock);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else if (abs(temp - pixclock) < err) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("Entered the else =
if block %d\n", i);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D abs(temp - pixclock=
);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 bestval =3D pixval + i;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 bestfreq =3D temp;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 pr_debug("DIU chose =3D %lx\n", bestval);
> + =A0 =A0 =A0 pr_debug("DIU error =3D %ld\n NomPixClk ", err);
> + =A0 =A0 =A0 pr_debug("DIU: Best Freq =3D %lx\n", bestfreq);
> + =A0 =A0 =A0 /* Modify DIU_DIV in CCM SCFR1 */
> + =A0 =A0 =A0 temp =3D in_be32(ccm + CCM_SCFR1);
> + =A0 =A0 =A0 pr_debug("DIU: Current value of SCFR1: 0x%08x\n", temp);
> + =A0 =A0 =A0 temp &=3D ~DIU_DIV_MASK;
> + =A0 =A0 =A0 temp |=3D (bestval & DIU_DIV_MASK);
> + =A0 =A0 =A0 out_be32(ccm + CCM_SCFR1, temp);
> + =A0 =A0 =A0 pr_debug("DIU: Modified value of SCFR1: 0x%08x\n", temp);
> + =A0 =A0 =A0 iounmap(ccm);
> +}
> +
> +ssize_t mpc512x_show_monitor_port(int monitor_port, char *buf)
> +{
> + =A0 =A0 =A0 return snprintf(buf, PAGE_SIZE, "0 - 5121 LCD\n");
> +}
> +
> +int mpc512x_set_sysfs_monitor_port(int val)
> +{
> + =A0 =A0 =A0 return 0;
> +}
> +
> +struct fsl_diu_shared_fb __attribute__ ((__aligned__(8))) diu_shared_fb;
> +EXPORT_SYMBOL_GPL(diu_shared_fb);
> +
> +#if defined(CONFIG_FB_FSL_DIU) || \
> + =A0 =A0defined(CONFIG_FB_FSL_DIU_MODULE)
> +static inline void mpc512x_free_bootmem(struct page *page)
> +{
> + =A0 =A0 =A0 __ClearPageReserved(page);
> + =A0 =A0 =A0 BUG_ON(PageTail(page));
> + =A0 =A0 =A0 BUG_ON(atomic_read(&page->_count) > 1);
> + =A0 =A0 =A0 atomic_set(&page->_count, 1);
> + =A0 =A0 =A0 __free_page(page);
> + =A0 =A0 =A0 totalram_pages++;
> +}
> +
> +void mpc512x_release_bootmem(void)
> +{
> + =A0 =A0 =A0 unsigned long addr =3D diu_shared_fb.fb_phys & PAGE_MASK;
> + =A0 =A0 =A0 unsigned long size =3D diu_shared_fb.fb_len;
> + =A0 =A0 =A0 unsigned long start, end;
> +
> + =A0 =A0 =A0 if (diu_shared_fb.in_use) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 start =3D PFN_UP(addr);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 end =3D PFN_DOWN(addr + size);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 for (; start < end; start++)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc512x_free_bootmem(pfn_to=
_page(start));
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 diu_shared_fb.in_use =3D false;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 diu_ops.release_bootmem =3D NULL;
> +}
> +#endif
> +
> +/*
> + * Check if DIU was pre-initialized. If so, perform steps
> + * needed to continue displaying through the whole boot process.
> + * Move area descriptor and gamma table elsewhere, they are
> + * destroyed by bootmem allocator otherwise. The frame buffer
> + * address range will be reserved in setup_arch() after bootmem
> + * allocator is up.
> + */
> +void __init mpc512x_init_diu(void)
> +{
> + =A0 =A0 =A0 struct device_node *np;
> + =A0 =A0 =A0 void __iomem *diu_reg;
> + =A0 =A0 =A0 phys_addr_t desc;
> + =A0 =A0 =A0 void __iomem *vaddr;
> + =A0 =A0 =A0 unsigned long mode, pix_fmt, res, bpp;
> + =A0 =A0 =A0 unsigned long dst;
> +
> + =A0 =A0 =A0 np =3D of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu=
");
> + =A0 =A0 =A0 if (!np) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("No DIU node\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 diu_reg =3D of_iomap(np, 0);
> + =A0 =A0 =A0 of_node_put(np);
> + =A0 =A0 =A0 if (!diu_reg) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("Can't map DIU\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 mode =3D in_be32(diu_reg + 0x1c);
> + =A0 =A0 =A0 if (mode !=3D 1) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_info("%s: DIU OFF\n", __func__);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 desc =3D in_be32(diu_reg);
> + =A0 =A0 =A0 vaddr =3D ioremap(desc, sizeof(struct diu_ad));
> + =A0 =A0 =A0 if (!vaddr) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("Can't map DIU area desc.\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 memcpy(&diu_shared_fb.ad0, vaddr, sizeof(struct diu_ad));
> + =A0 =A0 =A0 /* flush fb area descriptor */
> + =A0 =A0 =A0 dst =3D (unsigned long)&diu_shared_fb.ad0;
> + =A0 =A0 =A0 flush_dcache_range(dst, dst + sizeof(struct diu_ad) - 1);
> +
> + =A0 =A0 =A0 res =3D in_be32(diu_reg + 0x28);
> + =A0 =A0 =A0 pix_fmt =3D in_le32(vaddr);
> + =A0 =A0 =A0 bpp =3D ((pix_fmt >> 16) & 0x3) + 1;
> + =A0 =A0 =A0 diu_shared_fb.fb_phys =3D in_le32(vaddr + 4);
> + =A0 =A0 =A0 diu_shared_fb.fb_len =3D ((res & 0xfff0000) >> 16) * (res &=
0xfff) * bpp;
> + =A0 =A0 =A0 diu_shared_fb.in_use =3D true;
> + =A0 =A0 =A0 iounmap(vaddr);
> +
> + =A0 =A0 =A0 desc =3D in_be32(diu_reg + 0xc);
> + =A0 =A0 =A0 vaddr =3D ioremap(desc, sizeof(diu_shared_fb.gamma));
> + =A0 =A0 =A0 if (!vaddr) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("Can't map DIU area desc.\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 diu_shared_fb.in_use =3D false;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 memcpy(&diu_shared_fb.gamma, vaddr, sizeof(diu_shared_fb.ga=
mma));
> + =A0 =A0 =A0 /* flush gamma table */
> + =A0 =A0 =A0 dst =3D (unsigned long)&diu_shared_fb.gamma;
> + =A0 =A0 =A0 flush_dcache_range(dst, dst + sizeof(diu_shared_fb.gamma) -=
1);
> +
> + =A0 =A0 =A0 iounmap(vaddr);
> + =A0 =A0 =A0 out_be32(diu_reg + 0xc, virt_to_phys(&diu_shared_fb.gamma))=
;
> + =A0 =A0 =A0 out_be32(diu_reg + 4, 0);
> + =A0 =A0 =A0 out_be32(diu_reg + 8, 0);
> + =A0 =A0 =A0 out_be32(diu_reg, virt_to_phys(&diu_shared_fb.ad0));
> +
> +out:
> + =A0 =A0 =A0 iounmap(diu_reg);
> +}
> +
> +void __init mpc512x_setup_diu(void)
> +{
> + =A0 =A0 =A0 int ret;
> +
> + =A0 =A0 =A0 if (diu_shared_fb.in_use) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D reserve_bootmem(diu_shared_fb.fb_ph=
ys,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
diu_shared_fb.fb_len,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
BOOTMEM_EXCLUSIVE);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (ret) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("%s: reserve bootmem=
failed\n", __func__);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 diu_shared_fb.in_use =3D fa=
lse;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> +
> +#if defined(CONFIG_FB_FSL_DIU) || \
> + =A0 =A0defined(CONFIG_FB_FSL_DIU_MODULE)
> + =A0 =A0 =A0 diu_ops.get_pixel_format =A0 =A0 =A0 =A0=3D mpc512x_get_pix=
el_format;
> + =A0 =A0 =A0 diu_ops.set_gamma_table =A0 =A0 =A0 =A0 =3D mpc512x_set_gam=
ma_table;
> + =A0 =A0 =A0 diu_ops.set_monitor_port =A0 =A0 =A0 =A0=3D mpc512x_set_mon=
itor_port;
> + =A0 =A0 =A0 diu_ops.set_pixel_clock =A0 =A0 =A0 =A0 =3D mpc512x_set_pix=
el_clock;
> + =A0 =A0 =A0 diu_ops.show_monitor_port =A0 =A0 =A0 =3D mpc512x_show_moni=
tor_port;
> + =A0 =A0 =A0 diu_ops.set_sysfs_monitor_port =A0=3D mpc512x_set_sysfs_mon=
itor_port;
> + =A0 =A0 =A0 diu_ops.release_bootmem =A0 =A0 =A0 =A0 =3D mpc512x_release=
_bootmem;
> +#endif
> +}
> +
> =A0void __init mpc512x_init_IRQ(void)
> =A0{
> =A0 =A0 =A0 =A0struct device_node *np;
> diff --git a/arch/powerpc/sysdev/fsl_soc.h b/arch/powerpc/sysdev/fsl_soc.=
h
> index 19754be..346057d 100644
> --- a/arch/powerpc/sysdev/fsl_soc.h
> +++ b/arch/powerpc/sysdev/fsl_soc.h
> @@ -30,6 +30,7 @@ struct platform_diu_data_ops {
> =A0 =A0 =A0 =A0void (*set_pixel_clock) (unsigned int pixclock);
> =A0 =A0 =A0 =A0ssize_t (*show_monitor_port) (int monitor_port, char *buf)=
;
> =A0 =A0 =A0 =A0int (*set_sysfs_monitor_port) (int val);
> + =A0 =A0 =A0 void (*release_bootmem) (void);
> =A0};
>
> =A0extern struct platform_diu_data_ops diu_ops;
> diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
> index 19ca1da..263f7da 100644
> --- a/drivers/video/fsl-diu-fb.c
> +++ b/drivers/video/fsl-diu-fb.c
> @@ -34,7 +34,7 @@
> =A0#include <linux/of_platform.h>
>
> =A0#include <sysdev/fsl_soc.h>
> -#include "fsl-diu-fb.h"
> +#include <linux/fsl-diu-fb.h>
>
> =A0#include "ofmode.h"
>
> @@ -331,8 +331,11 @@ static int fsl_diu_enable_panel(struct fb_info *info=
)
> =A0 =A0 =A0 =A0if (mfbi->type !=3D MFB_TYPE_OFF) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0switch (mfbi->index) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0case 0: =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 /* plane 0 */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hw->desc[0] !=3D ad->pa=
ddr)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (in_be32(&hw->desc[0]) !=
=3D ad->paddr) {
Unrelated bugfix? If so, please split into separate patch.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_be32(&d=
r.diu_reg->diu_mode, 0);
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0out_be32(&=
hw->desc[0], ad->paddr);
This line also looks like it needs fixing.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_be32(&d=
r.diu_reg->diu_mode, 1);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0case 1: =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 /* plane 1 AOI 0 */
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0cmfbi =3D machine_data->fs=
l_diu_info[2]->par;
> @@ -391,7 +394,7 @@ static int fsl_diu_disable_panel(struct fb_info *info=
)
>
> =A0 =A0 =A0 =A0switch (mfbi->index) {
> =A0 =A0 =A0 =A0case 0: =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 /* plane 0 */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hw->desc[0] !=3D machine_data->dummy_ad=
->paddr)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (in_be32(&hw->desc[0]) !=3D machine_data=
->dummy_ad->paddr)
Same bugfix?
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0out_be32(&hw->desc[0],
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0machine_da=
ta->dummy_ad->paddr);
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
> @@ -1102,6 +1105,10 @@ static int fsl_diu_open(struct fb_info *info, int =
user)
> =A0 =A0 =A0 =A0struct mfb_info *mfbi =3D info->par;
> =A0 =A0 =A0 =A0int res =3D 0;
>
> + =A0 =A0 =A0 /* free boot splash memory on first /dev/fb0 open */
> + =A0 =A0 =A0 if (!mfbi->index && diu_ops.release_bootmem)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 diu_ops.release_bootmem();
> +
> =A0 =A0 =A0 =A0spin_lock(&diu_lock);
> =A0 =A0 =A0 =A0mfbi->count++;
> =A0 =A0 =A0 =A0if (mfbi->count =3D=3D 1) {
> @@ -1436,6 +1443,7 @@ static int __devinit fsl_diu_probe(struct of_device=
*ofdev,
> =A0 =A0 =A0 =A0int ret, i, error =3D 0;
> =A0 =A0 =A0 =A0struct resource res;
> =A0 =A0 =A0 =A0struct fsl_diu_data *machine_data;
> + =A0 =A0 =A0 int diu_mode;
>
> =A0 =A0 =A0 =A0machine_data =3D kzalloc(sizeof(struct fsl_diu_data), GFP_=
KERNEL);
> =A0 =A0 =A0 =A0if (!machine_data)
> @@ -1472,7 +1480,9 @@ static int __devinit fsl_diu_probe(struct of_device=
*ofdev,
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0goto error2;
> =A0 =A0 =A0 =A0}
>
> - =A0 =A0 =A0 out_be32(&dr.diu_reg->diu_mode, 0); =A0 =A0 =A0 =A0 =A0 =A0=
/* disable DIU anyway*/
> + =A0 =A0 =A0 diu_mode =3D in_be32(&dr.diu_reg->diu_mode);
> + =A0 =A0 =A0 if (diu_mode !=3D MFB_MODE1)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_be32(&dr.diu_reg->diu_mode, 0); =A0 =A0=
/* disable DIU */
Is this the best approach? Would it be better to make this decision
based on a property in the device tree?
>
> =A0 =A0 =A0 =A0/* Get the IRQ of the DIU */
> =A0 =A0 =A0 =A0machine_data->irq =3D irq_of_parse_and_map(np, 0);
> @@ -1520,7 +1530,12 @@ static int __devinit fsl_diu_probe(struct of_devic=
e *ofdev,
> =A0 =A0 =A0 =A0machine_data->dummy_ad->offset_xyd =3D 0;
> =A0 =A0 =A0 =A0machine_data->dummy_ad->next_ad =3D 0;
>
> - =A0 =A0 =A0 out_be32(&dr.diu_reg->desc[0], machine_data->dummy_ad->padd=
r);
> + =A0 =A0 =A0 /* Let DIU display splash screen if it was pre-initialized
> + =A0 =A0 =A0 =A0* by the bootloader, set dummy area descriptor otherwise=
.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 if (diu_mode !=3D MFB_MODE1)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_be32(&dr.diu_reg->desc[0], machine_data=
->dummy_ad->paddr);
> +
Same as above.
> =A0 =A0 =A0 =A0out_be32(&dr.diu_reg->desc[1], machine_data->dummy_ad->pad=
dr);
> =A0 =A0 =A0 =A0out_be32(&dr.diu_reg->desc[2], machine_data->dummy_ad->pad=
dr);
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH 2/3] fbdev: fsl-diu-fb.c: allow setting panel video mode from DT
From: Grant Likely @ 2010-02-28 6:52 UTC (permalink / raw)
To: Anatolij Gustschin; +Cc: linux-fbdev, wd, dzu, jrigby, linuxppc-dev, yorksun
In-Reply-To: <1267307902-31939-3-git-send-email-agust@denx.de>
On Sat, Feb 27, 2010 at 2:58 PM, Anatolij Gustschin <agust@denx.de> wrote:
> Add support for specifying display panel data in the device
> tree. If no panel data is provided in the device tree, default
> video mode will be used as usual.
>
> Signed-off-by: Anatolij Gustschin <agust@denx.de>
This one seems okay, but the binding in the first patch first needs to
get resolved.
g.
> ---
> =A0drivers/video/Kconfig =A0 =A0 =A0| =A0 =A01 +
> =A0drivers/video/fsl-diu-fb.c | =A0 63 +++++++++++++++++++++++++---------=
----------
> =A02 files changed, 37 insertions(+), 27 deletions(-)
>
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index dc1beb0..c805ecd 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -1850,6 +1850,7 @@ config FB_FSL_DIU
> =A0 =A0 =A0 =A0select FB_CFB_COPYAREA
> =A0 =A0 =A0 =A0select FB_CFB_IMAGEBLIT
> =A0 =A0 =A0 =A0select PPC_LIB_RHEAP
> + =A0 =A0 =A0 select FB_OF_MODE
> =A0 =A0 =A0 =A0---help---
> =A0 =A0 =A0 =A0 =A0Framebuffer driver for the Freescale SoC DIU
>
> diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
> index 4637bcb..19ca1da 100644
> --- a/drivers/video/fsl-diu-fb.c
> +++ b/drivers/video/fsl-diu-fb.c
> @@ -36,6 +36,8 @@
> =A0#include <sysdev/fsl_soc.h>
> =A0#include "fsl-diu-fb.h"
>
> +#include "ofmode.h"
> +
> =A0/*
> =A0* These parameters give default parameters
> =A0* for video output 1024x768,
> @@ -1168,7 +1170,7 @@ static int init_fbinfo(struct fb_info *info)
> =A0 =A0 =A0 =A0return 0;
> =A0}
>
> -static int __devinit install_fb(struct fb_info *info)
> +static int __devinit install_fb(struct device_node *np, struct fb_info *=
info)
> =A0{
> =A0 =A0 =A0 =A0int rc;
> =A0 =A0 =A0 =A0struct mfb_info *mfbi =3D info->par;
> @@ -1177,33 +1179,40 @@ static int __devinit install_fb(struct fb_info *i=
nfo)
> =A0 =A0 =A0 =A0if (init_fbinfo(info))
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return -EINVAL;
>
> - =A0 =A0 =A0 if (mfbi->index =3D=3D 0) =A0 /* plane 0 */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 aoi_mode =3D fb_mode;
> - =A0 =A0 =A0 else
> + =A0 =A0 =A0 if (mfbi->index =3D=3D 0) { /* plane 0 */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* use default mode for plane0 if there is =
no mode in DTB */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (of_get_video_mode(np, info) < 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 aoi_mode =3D fb_mode;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 aoi_mode =3D NULL;
> + =A0 =A0 =A0 } else
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0aoi_mode =3D init_aoi_mode;
> - =A0 =A0 =A0 pr_debug("mode used =3D %s\n", aoi_mode);
> - =A0 =A0 =A0 rc =3D fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mod=
e_db,
> - =A0 =A0 =A0 =A0 =A0 =A0ARRAY_SIZE(fsl_diu_mode_db), &fsl_diu_default_mo=
de, default_bpp);
>
> - =A0 =A0 =A0 switch (rc) {
> - =A0 =A0 =A0 case 1:
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("using mode specified in @mode\n")=
;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> - =A0 =A0 =A0 case 2:
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("using mode specified in @mode "
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "with ignored refresh rate\=
n");
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> - =A0 =A0 =A0 case 3:
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("using mode default mode\n");
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> - =A0 =A0 =A0 case 4:
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("using mode from list\n");
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> - =A0 =A0 =A0 default:
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("rc =3D %d\n", rc);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("failed to find mode\n");
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 if (aoi_mode) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("mode used =3D %s\n", aoi_mode);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rc =3D fb_find_mode(&info->var, info, aoi_m=
ode, fsl_diu_mode_db,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ARRAY_S=
IZE(fsl_diu_mode_db),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &fsl_di=
u_default_mode, default_bpp);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 switch (rc) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case 1:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("using mode specif=
ied in @mode\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case 2:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("using mode specif=
ied in @mode "
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "with ignor=
ed refresh rate\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case 3:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("using mode defaul=
t mode\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case 4:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("using mode from l=
ist\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("rc =3D %d\n", rc)=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_debug("failed to find mo=
de\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> =A0 =A0 =A0 =A0}
>
> =A0 =A0 =A0 =A0pr_debug("xres_virtual %d\n", info->var.xres_virtual);
> @@ -1521,7 +1530,7 @@ static int __devinit fsl_diu_probe(struct of_device=
*ofdev,
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0mfbi->ad =3D (struct diu_ad *)((u32)pool.a=
d.vaddr
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0+ pool.ad.offset) + i;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0mfbi->ad->paddr =3D pool.ad.paddr + i * si=
zeof(struct diu_ad);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D install_fb(machine_data->fsl_diu_in=
fo[i]);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D install_fb(np, machine_data->fsl_di=
u_info[i]);
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (ret) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0dev_err(&ofdev->dev,
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"Failed to=
register framebuffer %d\n",
> --
> 1.6.3.3
>
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH 0/3] Rework MPC5121 DIU support (for 2.6.34)
From: Grant Likely @ 2010-02-28 7:04 UTC (permalink / raw)
To: Anatolij Gustschin; +Cc: linux-fbdev, wd, dzu, jrigby, linuxppc-dev, yorksun
In-Reply-To: <1267307902-31939-1-git-send-email-agust@denx.de>
Hi Anatolij,
On Sat, Feb 27, 2010 at 2:58 PM, Anatolij Gustschin <agust@denx.de> wrote:
> This patch series rework DIU support patch submitted
> previously with the patch series for updating MPC5121
> support in mainline. It doesn't add new panel timing data
> to the framebuffer driver anymore. Instead we now allow
> encoding this data in the device tree. First two patches
> add this support.
Thanks for this work. I've made specific comments on the individual
patches. There are some details to be worked out, but I think it is
moving in the right direction.
> It is intended for inclusion in 2.6.34, since without
> DIU support patch framebuffer doesn't work on mpc5121.
It's actually late for 2.6.34. Unless it is a bug fix, I stop picking
up new patches somewhere in the -rc7 or -rc8 timerframe, and I am
definitely in bug-fix-only mode once the merge window has opened. I
do this to ensure all patches receive a decent amount of linux-next
exposure before I ask Linus to pull them.
I'll being picking up new patches for 2.6.35 into my -next branch
after 2.6.34-rc1 is released.
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH 1/3] video: add support for getting video mode from device tree
From: Mitch Bradley @ 2010-02-28 8:44 UTC (permalink / raw)
To: Grant Likely
Cc: linux-fbdev, wd, dzu, jrigby, devicetree-discuss, linuxppc-dev,
Anatolij Gustschin, yorksun
In-Reply-To: <fa686aa41002272230s7f900c0fv1aabf0e26ccbf84c@mail.gmail.com>
>
> Hi Anatolij,
>
> [added cc: to devicetree-discuss@lists.ozlabs.org]
>
> On Sat, Feb 27, 2010 at 2:58 PM, Anatolij Gustschin <agust@denx.de> wrote:
>
>> > Framebuffer drivers may want to get panel timing info
>> > from the device tree. This patch adds appropriate support.
>> > Subsequent patch for FSL DIU frame buffer driver makes use
>> > of this functionality.
>>
>
> I think this is moving in the right direction, but there needs to
> debate & review over the binding before committing to anything.
> Please write a patch that documents the new binding in
> Documentation/powerpc/dts-bindings. All new bindings should be
> documented and reviewed on devicetree-discuss before merging any
> drivers that use them into mainline.
>
> From what I can tell by reading your code, I suspect that the binding
> you've designed will solve your immediate problem, but won't be able
> to handle anything slightly more complex, but it also looks like the
> binding has been designed to be generic, usable by any display device.
>
> First off, I did a tiny amount of research, and I didn't find any
> existing OpenFirmware bindings for describing video displays.
> Otherwise, I'd suggest considering that.
>
> From the little bit that I know, it seems that for most video devices
> (ie. PCs) the video card discovers the capabilities of the screen by
> reading the monitor's EDID data. However, in your particular case
> embedded case, a fixed flat panel is attached, and there isn't any
> EDID data provided. Therefore, you need an alternate method of
> describing the display capabilities. Rather than designing something
> entirely new, you may want to consider using the EDID data format
> directly; or at least cover the same things that EDID describes. The
> downside to using EDID directly is that it is a packed binary format
> that isn't parseable by mere mortals; but the data contained in it
> seems about right. The upside is the kernel already knows what to do
> with EDID data.
>
> Otherwise you risk designing something that won't be useful for
> anything much outside of your own use case. For example, the binding
> I see from the code cannot handle a display with multiple output
> modes.
>
> Also, since you're now in the realm of describing a video display,
> which is separate from the display controller, you should consider
> describing the display in a separate device tree node. Maybe
> something like this...
>
> video {
> compatible = "fsl,mpc5121-diu";
> display {
> compatible = "<vendor>,<model>";
> edid = [edid-data];
> };
> };
>
As it turns out, I'm doing exactly that - exporting verbatim EDID data
as the value of the "edid" property - for the display node on the Via
version of the OLPC machine. The kernel driver uses it instead of
trying to obtain the EDID data from the monitor, because the builtin
OLPC display cannot supply EDID data through the usual hardware interfaces.
Mitch
> Cheers,
> g.
>
>
^ permalink raw reply
* [PATCH] powerpc: use __ratelimit
From: Akinobu Mita @ 2010-02-28 10:58 UTC (permalink / raw)
To: linux-kernel; +Cc: Paul Mackerras, Akinobu Mita, linuxppc-dev
In-Reply-To: <1267354697-6456-1-git-send-email-akinobu.mita@gmail.com>
Replace open-coded rate limiting logic with __ratelimit().
Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: linuxppc-dev@ozlabs.org
---
arch/powerpc/platforms/iseries/pci.c | 10 +++-------
1 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c
index 175aac8..5873a47 100644
--- a/arch/powerpc/platforms/iseries/pci.c
+++ b/arch/powerpc/platforms/iseries/pci.c
@@ -31,6 +31,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/of.h>
+#include <linux/ratelimit.h>
#include <asm/types.h>
#include <asm/io.h>
@@ -583,14 +584,9 @@ static inline struct device_node *xlate_iomm_address(
orig_addr = (unsigned long __force)addr;
if ((orig_addr < BASE_IO_MEMORY) || (orig_addr >= max_io_memory)) {
- static unsigned long last_jiffies;
- static int num_printed;
+ static DEFINE_RATELIMIT_STATE(ratelimit, 60 * HZ, 10);
- if (time_after(jiffies, last_jiffies + 60 * HZ)) {
- last_jiffies = jiffies;
- num_printed = 0;
- }
- if (num_printed++ < 10)
+ if (__ratelimit(&ratelimit))
printk(KERN_ERR
"iSeries_%s: invalid access at IO address %p\n",
func, addr);
--
1.6.0.6
^ permalink raw reply related
* [RFC PATCH v2 0/9] wii: add usb 2.0 support
From: Albert Herranz @ 2010-02-28 14:07 UTC (permalink / raw)
To: linuxppc-dev, linux-arm-kernel, linux-usb; +Cc: Albert Herranz
The following patch series adds USB 2.0 support for the Wii PowerPC
platform via the EHCI controller present in the "Hollywood" chipset
of the video game console.
v1 -> v2
- enable per-device DMA coherent support on the Wii
- add a generic dmabounce based on ARM dmabounce
- make PowerPC and ARM use the generic dmabounce support
(ARM just compile-tested)
- do not bounce buffers at the USB layer
- handle bounce buffers on the Wii at the platform support layer via the
DMA API
- introduce a flag HCD_NO_COHERENT_MEM to avoid using coherent memory for
USB buffers
- make the "Hollywood" EHCI controller use HCD_NO_COHERENT_MEM and honor
MEM2 DMA constraints to solve the platform restrictions accessing
coherent memory.
Albert Herranz (9):
powerpc: add per-device dma coherent support
wii: have generic dma coherent
dma-coherent: fix bitmap access races
add generic dmabounce support
arm: use generic dmabounce support
powerpc: add optional per-device dmabounce support
wii: add mem2 dma mapping ops
USB: add HCD_NO_COHERENT_MEM host controller driver flag
wii: hollywood ehci controller support
arch/Kconfig | 3 +
arch/arm/Kconfig | 4 +-
arch/arm/common/Kconfig | 6 +-
arch/arm/common/dmabounce.c | 376 +++--------------
arch/arm/include/asm/device.h | 2 +-
arch/powerpc/boot/wii.c | 34 ++
arch/powerpc/include/asm/device.h | 3 +
arch/powerpc/include/asm/dma-mapping.h | 1 +
arch/powerpc/include/asm/wii.h | 25 ++
arch/powerpc/kernel/dma.c | 5 +
arch/powerpc/platforms/embedded6xx/Kconfig | 3 +
arch/powerpc/platforms/embedded6xx/Makefile | 2 +-
arch/powerpc/platforms/embedded6xx/wii-dma.c | 557 ++++++++++++++++++++++++++
drivers/base/dma-coherent.c | 11 +
drivers/usb/core/buffer.c | 15 +-
drivers/usb/core/hcd.c | 50 ++-
drivers/usb/core/hcd.h | 13 +-
drivers/usb/host/Kconfig | 8 +
drivers/usb/host/ehci-hcd.c | 5 +
drivers/usb/host/ehci-hlwd.c | 233 +++++++++++
drivers/usb/host/ehci.h | 23 +
include/linux/dmabounce.h | 71 ++++
lib/Kconfig | 10 +
lib/Makefile | 2 +
lib/dmabounce.c | 389 ++++++++++++++++++
25 files changed, 1512 insertions(+), 339 deletions(-)
create mode 100644 arch/powerpc/include/asm/wii.h
create mode 100755 arch/powerpc/platforms/embedded6xx/wii-dma.c
create mode 100644 drivers/usb/host/ehci-hlwd.c
create mode 100644 include/linux/dmabounce.h
create mode 100644 lib/dmabounce.c
^ permalink raw reply
* [RFC PATCH v2 1/9] powerpc: add per-device dma coherent support
From: Albert Herranz @ 2010-02-28 14:07 UTC (permalink / raw)
To: linuxppc-dev, linux-arm-kernel, linux-usb; +Cc: Albert Herranz
In-Reply-To: <1267366082-15248-1-git-send-email-albert_herranz@yahoo.es>
Use the generic per-device dma coherent allocator on powerpc.
This allows a driver to declare coherent memory area from where
a device can allocate coherent memory chunks.
Signed-off-by: Albert Herranz <albert_herranz@yahoo.es>
---
arch/powerpc/include/asm/dma-mapping.h | 1 +
arch/powerpc/kernel/dma.c | 5 +++++
2 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h
index 80a973b..18ecec8 100644
--- a/arch/powerpc/include/asm/dma-mapping.h
+++ b/arch/powerpc/include/asm/dma-mapping.h
@@ -17,6 +17,7 @@
#include <linux/dma-debug.h>
#include <asm/io.h>
#include <asm/swiotlb.h>
+#include <asm-generic/dma-coherent.h>
#define DMA_ERROR_CODE (~(dma_addr_t)0x0)
diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c
index 6215062..83d5232 100644
--- a/arch/powerpc/kernel/dma.c
+++ b/arch/powerpc/kernel/dma.c
@@ -27,6 +27,9 @@ void *dma_direct_alloc_coherent(struct device *dev, size_t size,
{
void *ret;
#ifdef CONFIG_NOT_COHERENT_CACHE
+ if (dma_alloc_from_coherent(dev, size, dma_handle, &ret))
+ return ret;
+
ret = __dma_alloc_coherent(dev, size, dma_handle, flag);
if (ret == NULL)
return NULL;
@@ -54,6 +57,8 @@ void dma_direct_free_coherent(struct device *dev, size_t size,
void *vaddr, dma_addr_t dma_handle)
{
#ifdef CONFIG_NOT_COHERENT_CACHE
+ if (dma_release_from_coherent(dev, get_order(size), vaddr))
+ return;
__dma_free_coherent(size, vaddr);
#else
free_pages((unsigned long)vaddr, get_order(size));
--
1.6.3.3
^ permalink raw reply related
* [RFC PATCH v2 2/9] wii: have generic dma coherent
From: Albert Herranz @ 2010-02-28 14:07 UTC (permalink / raw)
To: linuxppc-dev, linux-arm-kernel, linux-usb; +Cc: Albert Herranz
In-Reply-To: <1267366082-15248-1-git-send-email-albert_herranz@yahoo.es>
Let the Nintendo Wii gaming console use per-device dma coherent allocations.
This will be used later by some of its drivers.
Signed-off-by: Albert Herranz <albert_herranz@yahoo.es>
---
arch/powerpc/platforms/embedded6xx/Kconfig | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig
index 524d971..fe77ab2 100644
--- a/arch/powerpc/platforms/embedded6xx/Kconfig
+++ b/arch/powerpc/platforms/embedded6xx/Kconfig
@@ -119,6 +119,7 @@ config WII
bool "Nintendo-Wii"
depends on EMBEDDED6xx
select GAMECUBE_COMMON
+ select HAVE_GENERIC_DMA_COHERENT
help
Select WII if configuring for the Nintendo Wii.
More information at: <http://gc-linux.sourceforge.net/>
--
1.6.3.3
^ permalink raw reply related
* [RFC PATCH v2 3/9] dma-coherent: fix bitmap access races
From: Albert Herranz @ 2010-02-28 14:07 UTC (permalink / raw)
To: linuxppc-dev, linux-arm-kernel, linux-usb; +Cc: Albert Herranz
In-Reply-To: <1267366082-15248-1-git-send-email-albert_herranz@yahoo.es>
The coherent per-device memory handling functions use the in-kernel
bitmap library to account for the allocated regions.
The bitmap functions, though, do not protect the bitmap structure from
being modified concurrently. This can lead, for example, to double
allocations if dma_alloc_from_coherent() is called while another
dma_alloc_from_coherent() is already in progress.
Fix those races by protecting concurrent modifications of the allocation
bitmap. spin_lock_irqsave()/spin_unlock_irqrestore() is used as the
allocation/release functions are planned to be used in interrupt context
for streaming DMA mappings/unmappings via bounce buffers.
Signed-off-by: Albert Herranz <albert_herranz@yahoo.es>
---
drivers/base/dma-coherent.c | 11 +++++++++++
1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c
index 962a3b5..9d27d63 100644
--- a/drivers/base/dma-coherent.c
+++ b/drivers/base/dma-coherent.c
@@ -11,6 +11,7 @@ struct dma_coherent_mem {
int size;
int flags;
unsigned long *bitmap;
+ spinlock_t lock; /* protect bitmap operations */
};
int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
@@ -44,6 +45,7 @@ int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
dev->dma_mem->device_base = device_addr;
dev->dma_mem->size = pages;
dev->dma_mem->flags = flags;
+ spin_lock_init(&dev->dma_mem->lock);
if (flags & DMA_MEMORY_MAP)
return DMA_MEMORY_MAP;
@@ -77,6 +79,7 @@ void *dma_mark_declared_memory_occupied(struct device *dev,
{
struct dma_coherent_mem *mem = dev->dma_mem;
int pos, err;
+ unsigned long flags;
size += device_addr & ~PAGE_MASK;
@@ -84,7 +87,9 @@ void *dma_mark_declared_memory_occupied(struct device *dev,
return ERR_PTR(-EINVAL);
pos = (device_addr - mem->device_base) >> PAGE_SHIFT;
+ spin_lock_irqsave(&mem->lock, flags);
err = bitmap_allocate_region(mem->bitmap, pos, get_order(size));
+ spin_unlock_irqrestore(&mem->lock, flags);
if (err != 0)
return ERR_PTR(err);
return mem->virt_base + (pos << PAGE_SHIFT);
@@ -112,6 +117,7 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size,
struct dma_coherent_mem *mem;
int order = get_order(size);
int pageno;
+ unsigned long flags;
if (!dev)
return 0;
@@ -124,7 +130,9 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size,
if (unlikely(size > (mem->size << PAGE_SHIFT)))
goto err;
+ spin_lock_irqsave(&mem->lock, flags);
pageno = bitmap_find_free_region(mem->bitmap, mem->size, order);
+ spin_unlock_irqrestore(&mem->lock, flags);
if (unlikely(pageno < 0))
goto err;
@@ -163,12 +171,15 @@ EXPORT_SYMBOL(dma_alloc_from_coherent);
int dma_release_from_coherent(struct device *dev, int order, void *vaddr)
{
struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
+ unsigned long flags;
if (mem && vaddr >= mem->virt_base && vaddr <
(mem->virt_base + (mem->size << PAGE_SHIFT))) {
int page = (vaddr - mem->virt_base) >> PAGE_SHIFT;
+ spin_lock_irqsave(&mem->lock, flags);
bitmap_release_region(mem->bitmap, page, order);
+ spin_unlock_irqrestore(&mem->lock, flags);
return 1;
}
return 0;
--
1.6.3.3
^ permalink raw reply related
* [RFC PATCH v2 4/9] add generic dmabounce support
From: Albert Herranz @ 2010-02-28 14:07 UTC (permalink / raw)
To: linuxppc-dev, linux-arm-kernel, linux-usb; +Cc: Albert Herranz
In-Reply-To: <1267366082-15248-1-git-send-email-albert_herranz@yahoo.es>
This patch makes part of the ARM dmabounce code available to other
architectures as a generic API.
See included kernel-doc annotations for the actual API implemented.
An architecture can opt-in for generic dmabounce support by defining
HAVE_DMABOUNCE.
This support will be used later to address DMA memory access restrictions
on the Nintendo Wii video game console.
Signed-off-by: Albert Herranz <albert_herranz@yahoo.es>
---
arch/Kconfig | 3 +
include/linux/dmabounce.h | 77 +++++++++
lib/Kconfig | 10 ++
lib/Makefile | 2 +
lib/dmabounce.c | 395 +++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 487 insertions(+), 0 deletions(-)
create mode 100644 include/linux/dmabounce.h
create mode 100644 lib/dmabounce.c
diff --git a/arch/Kconfig b/arch/Kconfig
index 9d055b4..98ff26d 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -130,6 +130,9 @@ config HAVE_CLK
config HAVE_DMA_API_DEBUG
bool
+config HAVE_DMABOUNCE
+ bool
+
config HAVE_DEFAULT_NO_SPIN_MUTEXES
bool
diff --git a/include/linux/dmabounce.h b/include/linux/dmabounce.h
new file mode 100644
index 0000000..d60dc04
--- /dev/null
+++ b/include/linux/dmabounce.h
@@ -0,0 +1,77 @@
+#ifndef _LINUX_DMABOUNCE_H
+#define _LINUX_DMABOUNCE_H
+
+/* FIXME remove later when arch/arm/common/dmabounce.c is updated */
+#ifndef CONFIG_ARM
+
+#ifdef CONFIG_DMABOUNCE
+
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/device.h>
+
+struct dmabounce_info;
+
+#ifdef CONFIG_DMABOUNCE_STATS
+struct dmabounce_stats {
+ unsigned long total_allocs;
+ unsigned long map_op_count;
+ unsigned long bounce_count;
+};
+extern struct dmabounce_stats *dmabounce_get_stats(struct dmabounce_info *);
+#define DMABOUNCE_DO_STATS(i, X) do { dmabounce_get_stats(i)->X ; } while (0)
+#else
+#define DMABOUNCE_DO_STATS(i, X) do { } while (0)
+#endif /* CONFIG_DMABOUNCE_STATS */
+
+struct dmabounce_pool {
+ unsigned long size;
+ struct dma_pool *pool;
+#ifdef CONFIG_DMABOUNCE_STATS
+ unsigned long allocs;
+#endif
+};
+
+struct dmabounce_buffer {
+ struct list_head node;
+
+ /* original buffer */
+ void *buf;
+ size_t size;
+ enum dma_data_direction dir;
+
+ /* bounced buffer */
+ void *bounce_buf;
+ dma_addr_t bounce_buf_dma;
+
+ struct dmabounce_pool *pool;
+};
+
+extern struct dmabounce_buffer *
+dmabounce_alloc_buffer(struct dmabounce_info *info,
+ void *buf, size_t size, enum dma_data_direction dir,
+ gfp_t gfp);
+extern void dmabounce_free_buffer(struct dmabounce_info *info,
+ struct dmabounce_buffer *bb);
+extern struct dmabounce_buffer *
+dmabounce_find_buffer(struct dmabounce_info *info, dma_addr_t bounce_buf_dma,
+ size_t size, enum dma_data_direction dir);
+
+extern struct dmabounce_info *
+dmabounce_info_alloc(struct device *dev,
+ size_t small_buffer_size, size_t large_buffer_size,
+ size_t align, size_t boundary);
+extern void dmabounce_info_free(struct dmabounce_info *info);
+
+extern int dmabounce_info_register(struct device *dev,
+ struct dmabounce_info *info);
+extern void dmabounce_info_unregister(struct device *dev);
+
+#endif /* CONFIG_DMABOUNCE */
+
+/* FIXME remove later when arch/arm/common/dmabounce.c is updated */
+#endif /* !CONFIG_ARM */
+
+#endif /* _LINUX_DMABOUNCE_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 97b136f..b53b7dc 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -207,4 +207,14 @@ config GENERIC_ATOMIC64
config LRU_CACHE
tristate
+config DMABOUNCE
+ bool
+ depends on HAVE_DMABOUNCE
+ select ZONE_DMA if ARM
+ default y
+
+config DMABOUNCE_STATS
+ bool "Track dmabounce statistics"
+ depends on DMABOUNCE
+
endmenu
diff --git a/lib/Makefile b/lib/Makefile
index 3b0b4a6..097c2ed 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -100,6 +100,8 @@ obj-$(CONFIG_GENERIC_CSUM) += checksum.o
obj-$(CONFIG_GENERIC_ATOMIC64) += atomic64.o
+obj-$(CONFIG_DMABOUNCE) += dmabounce.o
+
hostprogs-y := gen_crc32table
clean-files := crc32table.h
diff --git a/lib/dmabounce.c b/lib/dmabounce.c
new file mode 100644
index 0000000..620d314
--- /dev/null
+++ b/lib/dmabounce.c
@@ -0,0 +1,395 @@
+/*
+ * lib/dmabounce.c
+ *
+ * Generic DMA bounce buffer functions.
+ * Copyright (C) 2010 Albert Herranz <albert_herranz@yahoo.es>
+ *
+ * Based on arch/arm/common/dmabounce.c
+ *
+ * Original version by Brad Parker (brad@heeltoe.com)
+ * Re-written by Christopher Hoover <ch@murgatroid.com>
+ * Made generic by Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Copyright (C) 2002 Hewlett Packard Company.
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+/* FIXME remove later when arch/arm/common/dmabounce.c is updated */
+#ifndef CONFIG_ARM
+
+#define DRV_MODULE_NAME "dmabounce"
+#define DRV_DESCRIPTION "Generic DMA bounce buffer functions"
+#define DRV_AUTHOR "Christopher Hoover <ch@hpl.hp.com>, " \
+ "Deepak Saxena <dsaxena@plexity.net>, " \
+ "Albert Herranz <albert_herranz@yahoo.es>"
+
+#define pr_fmt(fmt) DRV_MODULE_NAME ": " fmt
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/dmabounce.h>
+
+
+struct dmabounce_info {
+ struct device *dev;
+ struct list_head bounce_buffers;
+
+#ifdef CONFIG_DMABOUNCE_STATS
+ struct dmabounce_stats stats;
+ int attr_res;
+#endif
+ struct dmabounce_pool small;
+ struct dmabounce_pool large;
+
+ rwlock_t lock;
+};
+
+#ifdef CONFIG_DMABOUNCE_STATS
+struct dmabounce_stats *dmabounce_get_stats(struct dmabounce_info *info)
+{
+ return &info->stats;
+}
+
+static ssize_t dmabounce_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dmabounce_info *info = dev->archdata.dmabounce;
+ return sprintf(buf, "%lu %lu %lu %lu %lu %lu\n",
+ info->small.allocs,
+ info->large.allocs,
+ info->stats.total_allocs -
+ info->small.allocs - info->large.allocs,
+ info->stats.total_allocs,
+ info->stats.map_op_count,
+ info->stats.bounce_count);
+}
+
+static DEVICE_ATTR(dmabounce_stats, 0400, dmabounce_show, NULL);
+#endif /* CONFIG_DMABOUNCE_STATS */
+
+/**
+ * dmabounce_alloc_buffer() - try to allocate a coherent DMA bounce buffer
+ * @info: This dmabounce_info.
+ * @buf: Original buffer virtual address.
+ * @size: Original buffer length.
+ * @dir: Direction of DMA transfer for this buffer.
+ * @gfp: Flags used for memory allocations.
+ *
+ * Use this function to allocate a coherent buffer of size @size associated
+ * to a dmabounce_info structure @info.
+ * The allocation will be performed using the @gfp allocation flags.
+ *
+ * The coherent buffer can be used later to bounce data from/to the
+ * corresponding normal buffer @buf with the specified direction @dir.
+ *
+ * If the buffer cannot be allocated the function returns NULL.
+ * Otherwise, the allocated buffer is returned.
+ */
+struct dmabounce_buffer *
+dmabounce_alloc_buffer(struct dmabounce_info *info,
+ void *buf, size_t size, enum dma_data_direction dir,
+ gfp_t gfp)
+{
+ struct device *dev = info->dev;
+ struct dmabounce_pool *pool = NULL;
+ struct dmabounce_buffer *bb;
+ unsigned long flags;
+
+ dev_dbg(dev, "%s(buf=%p, size=%d, dir=%d)\n", __func__, buf, size, dir);
+
+ if (size <= info->small.size)
+ pool = &info->small;
+ else if (size <= info->large.size)
+ pool = &info->large;
+
+ bb = kzalloc(sizeof(*bb), gfp);
+ if (!bb) {
+ dev_err(dev, "%s: kmalloc failed\n", __func__);
+ goto out;
+ }
+
+ if (pool) {
+ bb->bounce_buf = dma_pool_alloc(pool->pool, gfp,
+ &bb->bounce_buf_dma);
+ } else {
+ bb->bounce_buf = dma_alloc_coherent(dev, size,
+ &bb->bounce_buf_dma, gfp);
+ }
+
+ if (!bb->bounce_buf) {
+ dev_err(dev, "%s: error allocating DMA memory (size=%d)\n",
+ __func__, size);
+ kfree(bb);
+ bb = NULL;
+ goto out;
+ }
+
+#ifdef CONFIG_DMABOUNCE_STATS
+ if (pool)
+ pool->allocs++;
+#endif
+ DMABOUNCE_DO_STATS(info, total_allocs++);
+
+ bb->buf = buf;
+ bb->size = size;
+ bb->dir = dir;
+ bb->pool = pool;
+
+ write_lock_irqsave(&info->lock, flags);
+ list_add(&bb->node, &info->bounce_buffers);
+ write_unlock_irqrestore(&info->lock, flags);
+out:
+ return bb;
+}
+EXPORT_SYMBOL(dmabounce_alloc_buffer);
+
+/**
+ * dmabounce_free_buffer() - free a coherent DMA bounce buffer
+ * @info: This dmabounce_info.
+ * @bb: The coherent DMA bounce buffer to free.
+ *
+ * Free a previously allocated coherent DMA bounce buffer @bb from its
+ * associated dmabounce_info structure @info.
+ *
+ * The coherent DMA bounce buffer @bb must have been previously allocated
+ * using dmabounce_alloc_buffer().
+ */
+void
+dmabounce_free_buffer(struct dmabounce_info *info, struct dmabounce_buffer *bb)
+{
+ unsigned long flags;
+
+ dev_dbg(info->dev, "%s(buf=%p)\n", __func__, bb->buf);
+
+ write_lock_irqsave(&info->lock, flags);
+ list_del(&bb->node);
+ write_unlock_irqrestore(&info->lock, flags);
+
+ if (bb->pool)
+ dma_pool_free(bb->pool->pool, bb->bounce_buf,
+ bb->bounce_buf_dma);
+ else {
+ dma_free_coherent(info->dev, bb->size, bb->bounce_buf,
+ bb->bounce_buf_dma);
+ }
+
+ kfree(bb);
+}
+EXPORT_SYMBOL(dmabounce_free_buffer);
+
+/**
+ * dmabounce_find_buffer() - locate an existing coherent DMA bounce buffer
+ * @info: This dmabounce_info.
+ * @bounce_buf_dma: DMA handle for the bounce buffer to find.
+ * @size: Size of the bounce buffer to find.
+ * @dir: Direction of DMA transfer for the buffer to find.
+ *
+ * Finds a previously allocated coherent DMA bounce buffer associated
+ * to a dmabounce_info structure @info.
+ *
+ * The coherent DMA bounce buffer searched must have the given
+ * @bounce_buf_dma DMA handle, @size size and DMA direction @dir.
+ * If @size is zero, the searched buffer can have any size.
+ *
+ * If no matching coherent DMA bounce buffer is found the function returns
+ * NULL. Otherwise, the matching buffer is returned.
+ */
+struct dmabounce_buffer *
+dmabounce_find_buffer(struct dmabounce_info *info, dma_addr_t bounce_buf_dma,
+ size_t size, enum dma_data_direction dir)
+{
+ struct dmabounce_buffer *bb, *needle = NULL;
+ unsigned long flags;
+
+ read_lock_irqsave(&info->lock, flags);
+
+ list_for_each_entry(bb, &info->bounce_buffers, node) {
+ if (bb->bounce_buf_dma == bounce_buf_dma) {
+ /* we should get a perfect match here */
+ BUG_ON((size && bb->size != size) || bb->dir != dir);
+
+ needle = bb;
+ break;
+ }
+ }
+
+ read_unlock_irqrestore(&info->lock, flags);
+ return needle;
+}
+EXPORT_SYMBOL(dmabounce_find_buffer);
+
+static int dmabounce_init_pool(struct dmabounce_pool *pool,
+ struct device *dev, const char *name,
+ size_t size, size_t align, size_t boundary)
+{
+ pool->size = size;
+#ifdef CONFIG_DMABOUNCE_STATS
+ pool->allocs = 0;
+#endif
+ pool->pool = dma_pool_create(name, dev, size, align, boundary);
+
+ return pool->pool ? 0 : -ENOMEM;
+}
+
+/**
+ * dmabounce_info_alloc() - allocate a dmabounce_info structure
+ * @dev: Device for which coherent memory allocations are done.
+ * @small_buffer_size: Buffer size for allocations from the small pool.
+ * @large_buffer_size: Buffer size for allocations from the large pool.
+ * @align: Alignment for pool based allocations.
+ * @boundary: Boundary for pool based allocations.
+ *
+ * Use this function to allocate a dmabounce_info structure.
+ * A dmabounce_info structure can be used to manage a set of related
+ * coherent DMA bounce buffers.
+ *
+ * Memory for the coherent DMA bounce buffers will be allocated for the
+ * specified device @dev.
+ * If @small_buffer_size or @large_buffer_size are non-zero, allocations
+ * will be tried from the closest associated DMA pool which has a size
+ * greater or equal than the specified @size size. Allocations from DMA
+ * pools will honor the alignment and boundary crossing restrictions
+ * specified in @align and @boundary.
+ * Otherwise, allocations will be performed from non-pool coherent memory.
+ *
+ * If the dmabounce_info structure cannot be allocated the function
+ * returns NULL. Otherwise, the allocated dmabounce_info structure is returned.
+ */
+struct dmabounce_info *
+dmabounce_info_alloc(struct device *dev,
+ size_t small_buffer_size, size_t large_buffer_size,
+ size_t align, size_t boundary)
+{
+ struct dmabounce_info *info;
+ int error;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ dev_err(dev, "%s: allocation error\n", __func__);
+ goto out;
+ }
+
+ if (small_buffer_size) {
+ error = dmabounce_init_pool(&info->small, dev,
+ "small_dmabounce_pool",
+ small_buffer_size, align, boundary);
+ if (error) {
+ dev_err(dev, "error %d allocating DMA pool for %zu"
+ " byte objects\n", error, small_buffer_size);
+ goto err_alloc_small;
+ }
+ }
+
+ if (large_buffer_size) {
+ error = dmabounce_init_pool(&info->large, dev,
+ "large_dmabounce_pool",
+ large_buffer_size, align, boundary);
+ if (error) {
+ dev_err(dev, "error %d allocating DMA pool for %zu"
+ " byte objects\n", error, large_buffer_size);
+ goto err_alloc_large;
+ }
+ }
+
+ info->dev = dev;
+ INIT_LIST_HEAD(&info->bounce_buffers);
+ rwlock_init(&info->lock);
+
+ DMABOUNCE_DO_STATS(info, total_allocs = 0);
+ DMABOUNCE_DO_STATS(info, map_op_count = 0);
+ DMABOUNCE_DO_STATS(info, bounce_count = 0);
+ goto out;
+
+err_alloc_large:
+ dma_pool_destroy(info->small.pool);
+err_alloc_small:
+ kfree(info);
+ info = NULL;
+out:
+ return info;
+}
+EXPORT_SYMBOL(dmabounce_info_alloc);
+
+/**
+ * dmabounce_info_free() - free a dmabounce_info
+ * @info: This dmabounce_info.
+ *
+ * Free a previously allocated dmabounce_info structure @info.
+ *
+ * The dmabounce_info structure @info must have been previously allocated
+ * using dmabounce_info_alloc().
+ */
+void dmabounce_info_free(struct dmabounce_info *info)
+{
+ if (!list_empty(&info->bounce_buffers)) {
+ dev_err(info->dev, "freeing dmabounce with pending buffers!\n");
+ BUG();
+ }
+
+ if (info->small.pool)
+ dma_pool_destroy(info->small.pool);
+ if (info->large.pool)
+ dma_pool_destroy(info->large.pool);
+
+ kfree(info);
+}
+EXPORT_SYMBOL(dmabounce_info_free);
+
+/**
+ * dmabounce_info_register() - register a dmabounce_info for a device
+ * @dev: Device for which the dmabounce_info is registered.
+ * @info: dmabounce_info to register.
+ *
+ * Use this function to register a given dmabounce_info @info into a
+ * device @dev.
+ *
+ * A device can only have one dmabounce_info registered with.
+ * The same dmabounce_info may be registered for many devices.
+ */
+int dmabounce_info_register(struct device *dev, struct dmabounce_info *info)
+{
+ if (dev->archdata.dmabounce)
+ return -EBUSY;
+
+ dev->archdata.dmabounce = info;
+
+#ifdef CONFIG_DMABOUNCE_STATS
+ info->attr_res = device_create_file(dev, &dev_attr_dmabounce_stats);
+#endif
+
+ dev_info(dev, pr_fmt("device registered\n"));
+ return 0;
+}
+EXPORT_SYMBOL(dmabounce_info_register);
+
+/**
+ * dmabounce_info_unregister() - unregister the dmabounce_info from a device
+ * @dev: Device for which the dmabounce_info is unregistered.
+ *
+ * Use this function to unregister a previously registered dmabounce_info
+ * from a device @dev.
+ */
+void dmabounce_info_unregister(struct device *dev)
+{
+#ifdef CONFIG_DMABOUNCE_STATS
+ struct dmabounce_info *info = dev->archdata.dmabounce;
+
+ if (info && info->attr_res == 0)
+ device_remove_file(dev, &dev_attr_dmabounce_stats);
+#endif
+ dev->archdata.dmabounce = NULL;
+
+ dev_info(dev, pr_fmt("device unregistered\n"));
+}
+EXPORT_SYMBOL(dmabounce_info_unregister);
+
+MODULE_AUTHOR(DRV_AUTHOR);
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_LICENSE("GPL");
+
+/* FIXME remove later when arch/arm/common/dmabounce.c is updated */
+#endif /* !CONFIG_ARM */
--
1.6.3.3
^ permalink raw reply related
* [RFC PATCH v2 5/9] arm: use generic dmabounce support
From: Albert Herranz @ 2010-02-28 14:07 UTC (permalink / raw)
To: linuxppc-dev, linux-arm-kernel, linux-usb; +Cc: Albert Herranz
In-Reply-To: <1267366082-15248-1-git-send-email-albert_herranz@yahoo.es>
Update ARM dmabounce to use the generic dmabounce support.
Signed-off-by: Albert Herranz <albert_herranz@yahoo.es>
---
arch/arm/Kconfig | 4 +-
arch/arm/common/Kconfig | 6 +-
arch/arm/common/dmabounce.c | 376 +++++++---------------------------------
arch/arm/include/asm/device.h | 2 +-
include/linux/dmabounce.h | 6 -
lib/dmabounce.c | 6 -
6 files changed, 70 insertions(+), 330 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 184a6bd..25fe1d0 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -417,7 +417,7 @@ config ARCH_IXP4XX
select GENERIC_GPIO
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
- select DMABOUNCE if PCI
+ select HAVE_DMABOUNCE if PCI
help
Support for Intel's IXP4XX (XScale) family of processors.
@@ -974,7 +974,7 @@ config PCI_HOST_ITE8152
bool
depends on PCI && MACH_ARMCORE
default y
- select DMABOUNCE
+ select HAVE_DMABOUNCE
source "drivers/pci/Kconfig"
diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
index 4efbb9d..01869a5 100644
--- a/arch/arm/common/Kconfig
+++ b/arch/arm/common/Kconfig
@@ -20,11 +20,7 @@ config ICST307
config SA1111
bool
- select DMABOUNCE if !ARCH_PXA
-
-config DMABOUNCE
- bool
- select ZONE_DMA
+ select HAVE_DMABOUNCE if !ARCH_PXA
config TIMER_ACORN
bool
diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c
index cc32c1e..a114ee1 100644
--- a/arch/arm/common/dmabounce.c
+++ b/arch/arm/common/dmabounce.c
@@ -31,203 +31,38 @@
#include <linux/dmapool.h>
#include <linux/list.h>
#include <linux/scatterlist.h>
+#include <linux/dmabounce.h>
#include <asm/cacheflush.h>
-#undef STATS
-
-#ifdef STATS
-#define DO_STATS(X) do { X ; } while (0)
-#else
-#define DO_STATS(X) do { } while (0)
-#endif
-
-/* ************************************************** */
-
-struct safe_buffer {
- struct list_head node;
-
- /* original request */
- void *ptr;
- size_t size;
- int direction;
-
- /* safe buffer info */
- struct dmabounce_pool *pool;
- void *safe;
- dma_addr_t safe_dma_addr;
-};
-
-struct dmabounce_pool {
- unsigned long size;
- struct dma_pool *pool;
-#ifdef STATS
- unsigned long allocs;
-#endif
-};
-
-struct dmabounce_device_info {
- struct device *dev;
- struct list_head safe_buffers;
-#ifdef STATS
- unsigned long total_allocs;
- unsigned long map_op_count;
- unsigned long bounce_count;
- int attr_res;
-#endif
- struct dmabounce_pool small;
- struct dmabounce_pool large;
-
- rwlock_t lock;
-};
-
-#ifdef STATS
-static ssize_t dmabounce_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
- return sprintf(buf, "%lu %lu %lu %lu %lu %lu\n",
- device_info->small.allocs,
- device_info->large.allocs,
- device_info->total_allocs - device_info->small.allocs -
- device_info->large.allocs,
- device_info->total_allocs,
- device_info->map_op_count,
- device_info->bounce_count);
-}
-
-static DEVICE_ATTR(dmabounce_stats, 0400, dmabounce_show, NULL);
-#endif
-
-
-/* allocate a 'safe' buffer and keep track of it */
-static inline struct safe_buffer *
-alloc_safe_buffer(struct dmabounce_device_info *device_info, void *ptr,
- size_t size, enum dma_data_direction dir)
-{
- struct safe_buffer *buf;
- struct dmabounce_pool *pool;
- struct device *dev = device_info->dev;
- unsigned long flags;
-
- dev_dbg(dev, "%s(ptr=%p, size=%d, dir=%d)\n",
- __func__, ptr, size, dir);
-
- if (size <= device_info->small.size) {
- pool = &device_info->small;
- } else if (size <= device_info->large.size) {
- pool = &device_info->large;
- } else {
- pool = NULL;
- }
-
- buf = kmalloc(sizeof(struct safe_buffer), GFP_ATOMIC);
- if (buf == NULL) {
- dev_warn(dev, "%s: kmalloc failed\n", __func__);
- return NULL;
- }
-
- buf->ptr = ptr;
- buf->size = size;
- buf->direction = dir;
- buf->pool = pool;
-
- if (pool) {
- buf->safe = dma_pool_alloc(pool->pool, GFP_ATOMIC,
- &buf->safe_dma_addr);
- } else {
- buf->safe = dma_alloc_coherent(dev, size, &buf->safe_dma_addr,
- GFP_ATOMIC);
- }
-
- if (buf->safe == NULL) {
- dev_warn(dev,
- "%s: could not alloc dma memory (size=%d)\n",
- __func__, size);
- kfree(buf);
- return NULL;
- }
-
-#ifdef STATS
- if (pool)
- pool->allocs++;
- device_info->total_allocs++;
-#endif
-
- write_lock_irqsave(&device_info->lock, flags);
- list_add(&buf->node, &device_info->safe_buffers);
- write_unlock_irqrestore(&device_info->lock, flags);
-
- return buf;
-}
-
-/* determine if a buffer is from our "safe" pool */
-static inline struct safe_buffer *
-find_safe_buffer(struct dmabounce_device_info *device_info, dma_addr_t safe_dma_addr)
-{
- struct safe_buffer *b, *rb = NULL;
- unsigned long flags;
-
- read_lock_irqsave(&device_info->lock, flags);
-
- list_for_each_entry(b, &device_info->safe_buffers, node)
- if (b->safe_dma_addr == safe_dma_addr) {
- rb = b;
- break;
- }
-
- read_unlock_irqrestore(&device_info->lock, flags);
- return rb;
-}
-
-static inline void
-free_safe_buffer(struct dmabounce_device_info *device_info, struct safe_buffer *buf)
-{
- unsigned long flags;
-
- dev_dbg(device_info->dev, "%s(buf=%p)\n", __func__, buf);
-
- write_lock_irqsave(&device_info->lock, flags);
-
- list_del(&buf->node);
-
- write_unlock_irqrestore(&device_info->lock, flags);
-
- if (buf->pool)
- dma_pool_free(buf->pool->pool, buf->safe, buf->safe_dma_addr);
- else
- dma_free_coherent(device_info->dev, buf->size, buf->safe,
- buf->safe_dma_addr);
-
- kfree(buf);
-}
-
-/* ************************************************** */
-
-static struct safe_buffer *find_safe_buffer_dev(struct device *dev,
- dma_addr_t dma_addr, const char *where)
+static struct dmabounce_buffer *
+find_safe_buffer_dev(struct device *dev, dma_addr_t dma_addr, size_t size,
+ enum dma_data_direction dir, const char *where)
{
if (!dev || !dev->archdata.dmabounce)
return NULL;
if (dma_mapping_error(dev, dma_addr)) {
if (dev)
dev_err(dev, "Trying to %s invalid mapping\n", where);
- else
- pr_err("unknown device: Trying to %s invalid mapping\n", where);
+ else {
+ pr_err("unknown device: "
+ "Trying to %s invalid mapping\n", where);
+ }
return NULL;
}
- return find_safe_buffer(dev->archdata.dmabounce, dma_addr);
+ return dmabounce_find_buffer(dev->archdata.dmabounce, dma_addr,
+ size, dir);
}
static inline dma_addr_t map_single(struct device *dev, void *ptr, size_t size,
enum dma_data_direction dir)
{
- struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
+ struct dmabounce_info *info = dev->archdata.dmabounce;
dma_addr_t dma_addr;
int needs_bounce = 0;
- if (device_info)
- DO_STATS ( device_info->map_op_count++ );
+ if (info)
+ DMABOUNCE_DO_STATS(info, map_op_count++);
dma_addr = virt_to_dma(dev, ptr);
@@ -248,11 +83,11 @@ static inline dma_addr_t map_single(struct device *dev, void *ptr, size_t size,
needs_bounce = (dma_addr | (dma_addr + size - 1)) & ~mask;
}
- if (device_info && (needs_bounce || dma_needs_bounce(dev, dma_addr, size))) {
- struct safe_buffer *buf;
+ if (info && (needs_bounce || dma_needs_bounce(dev, dma_addr, size))) {
+ struct dmabounce_buffer *bb;
- buf = alloc_safe_buffer(device_info, ptr, size, dir);
- if (buf == 0) {
+ bb = dmabounce_alloc_buffer(info, ptr, size, dir, GFP_ATOMIC);
+ if (!bb) {
dev_err(dev, "%s: unable to map unsafe buffer %p!\n",
__func__, ptr);
return 0;
@@ -260,18 +95,17 @@ static inline dma_addr_t map_single(struct device *dev, void *ptr, size_t size,
dev_dbg(dev,
"%s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)\n",
- __func__, buf->ptr, virt_to_dma(dev, buf->ptr),
- buf->safe, buf->safe_dma_addr);
+ __func__, bb->buf, virt_to_dma(dev, bb->buf),
+ bb->bounce_buf, bb->bounce_buf_dma);
if ((dir == DMA_TO_DEVICE) ||
(dir == DMA_BIDIRECTIONAL)) {
dev_dbg(dev, "%s: copy unsafe %p to safe %p, size %d\n",
- __func__, ptr, buf->safe, size);
- memcpy(buf->safe, ptr, size);
+ __func__, ptr, bb->bounce_buf, size);
+ memcpy(bb->bounce_buf, ptr, size);
}
- ptr = buf->safe;
- dma_addr = buf->safe_dma_addr;
+ dma_addr = bb->bounce_buf_dma;
} else {
/*
* We don't need to sync the DMA buffer since
@@ -286,26 +120,25 @@ static inline dma_addr_t map_single(struct device *dev, void *ptr, size_t size,
static inline void unmap_single(struct device *dev, dma_addr_t dma_addr,
size_t size, enum dma_data_direction dir)
{
- struct safe_buffer *buf = find_safe_buffer_dev(dev, dma_addr, "unmap");
-
- if (buf) {
- BUG_ON(buf->size != size);
- BUG_ON(buf->direction != dir);
+ struct dmabounce_buffer *bb;
+ bb = find_safe_buffer_dev(dev, dma_addr, size, dir, "unmap");
+ if (bb) {
dev_dbg(dev,
- "%s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)\n",
- __func__, buf->ptr, virt_to_dma(dev, buf->ptr),
- buf->safe, buf->safe_dma_addr);
+ "%s: unsafe buffer %p (dma=%#x) mapped to"
+ " %p (dma=%#x)\n",
+ __func__, bb->buf, virt_to_dma(dev, bb->buf),
+ bb->bounce_buf, bb->bounce_buf_dma);
- DO_STATS(dev->archdata.dmabounce->bounce_count++);
+ DMABOUNCE_DO_STATS(dev->archdata.dmabounce, bounce_count++);
if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) {
- void *ptr = buf->ptr;
+ void *ptr = bb->buf;
dev_dbg(dev,
"%s: copy back safe %p to unsafe %p size %d\n",
- __func__, buf->safe, ptr, size);
- memcpy(ptr, buf->safe, size);
+ __func__, bb->bounce_buf, ptr, size);
+ memcpy(ptr, bb->bounce_buf, size);
/*
* Since we may have written to a page cache page,
@@ -314,7 +147,7 @@ static inline void unmap_single(struct device *dev, dma_addr_t dma_addr,
*/
__cpuc_flush_dcache_area(ptr, size);
}
- free_safe_buffer(dev->archdata.dmabounce, buf);
+ dmabounce_free_buffer(dev->archdata.dmabounce, bb);
}
}
@@ -391,27 +224,25 @@ EXPORT_SYMBOL(dma_unmap_page);
int dmabounce_sync_for_cpu(struct device *dev, dma_addr_t addr,
unsigned long off, size_t sz, enum dma_data_direction dir)
{
- struct safe_buffer *buf;
+ struct dmabounce_buffer *bb;
dev_dbg(dev, "%s(dma=%#x,off=%#lx,sz=%zx,dir=%x)\n",
__func__, addr, off, sz, dir);
- buf = find_safe_buffer_dev(dev, addr, __func__);
- if (!buf)
+ bb = find_safe_buffer_dev(dev, addr, 0, dir, __func__);
+ if (!bb)
return 1;
- BUG_ON(buf->direction != dir);
-
dev_dbg(dev, "%s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)\n",
- __func__, buf->ptr, virt_to_dma(dev, buf->ptr),
- buf->safe, buf->safe_dma_addr);
+ __func__, bb->buf, virt_to_dma(dev, bb->buf),
+ bb->bounce_buf, bb->bounce_buf_dma);
- DO_STATS(dev->archdata.dmabounce->bounce_count++);
+ DMABOUNCE_DO_STATS(dev->archdata.dmabounce, bounce_count++);
if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) {
dev_dbg(dev, "%s: copy back safe %p to unsafe %p size %d\n",
- __func__, buf->safe + off, buf->ptr + off, sz);
- memcpy(buf->ptr + off, buf->safe + off, sz);
+ __func__, bb->bounce_buf + off, bb->buf + off, sz);
+ memcpy(bb->buf + off, bb->bounce_buf + off, sz);
}
return 0;
}
@@ -420,135 +251,60 @@ EXPORT_SYMBOL(dmabounce_sync_for_cpu);
int dmabounce_sync_for_device(struct device *dev, dma_addr_t addr,
unsigned long off, size_t sz, enum dma_data_direction dir)
{
- struct safe_buffer *buf;
+ struct dmabounce_buffer *bb;
dev_dbg(dev, "%s(dma=%#x,off=%#lx,sz=%zx,dir=%x)\n",
__func__, addr, off, sz, dir);
- buf = find_safe_buffer_dev(dev, addr, __func__);
- if (!buf)
+ bb = find_safe_buffer_dev(dev, addr, 0, dir, __func__);
+ if (!bb)
return 1;
- BUG_ON(buf->direction != dir);
-
dev_dbg(dev, "%s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)\n",
- __func__, buf->ptr, virt_to_dma(dev, buf->ptr),
- buf->safe, buf->safe_dma_addr);
+ __func__, bb->buf, virt_to_dma(dev, bb->buf),
+ bb->bounce_buf, bb->bounce_buf_dma);
- DO_STATS(dev->archdata.dmabounce->bounce_count++);
+ DMABOUNCE_DO_STATS(dev->archdata.dmabounce, bounce_count++);
if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) {
dev_dbg(dev, "%s: copy out unsafe %p to safe %p, size %d\n",
- __func__,buf->ptr + off, buf->safe + off, sz);
- memcpy(buf->safe + off, buf->ptr + off, sz);
+ __func__, bb->buf + off, bb->bounce_buf + off, sz);
+ memcpy(bb->bounce_buf + off, bb->buf + off, sz);
}
return 0;
}
EXPORT_SYMBOL(dmabounce_sync_for_device);
-static int dmabounce_init_pool(struct dmabounce_pool *pool, struct device *dev,
- const char *name, unsigned long size)
-{
- pool->size = size;
- DO_STATS(pool->allocs = 0);
- pool->pool = dma_pool_create(name, dev, size,
- 0 /* byte alignment */,
- 0 /* no page-crossing issues */);
-
- return pool->pool ? 0 : -ENOMEM;
-}
-
int dmabounce_register_dev(struct device *dev, unsigned long small_buffer_size,
unsigned long large_buffer_size)
{
- struct dmabounce_device_info *device_info;
- int ret;
-
- device_info = kmalloc(sizeof(struct dmabounce_device_info), GFP_ATOMIC);
- if (!device_info) {
- dev_err(dev,
- "Could not allocated dmabounce_device_info\n");
- return -ENOMEM;
- }
-
- ret = dmabounce_init_pool(&device_info->small, dev,
- "small_dmabounce_pool", small_buffer_size);
- if (ret) {
- dev_err(dev,
- "dmabounce: could not allocate DMA pool for %ld byte objects\n",
- small_buffer_size);
- goto err_free;
- }
-
- if (large_buffer_size) {
- ret = dmabounce_init_pool(&device_info->large, dev,
- "large_dmabounce_pool",
- large_buffer_size);
- if (ret) {
- dev_err(dev,
- "dmabounce: could not allocate DMA pool for %ld byte objects\n",
- large_buffer_size);
- goto err_destroy;
- }
- }
-
- device_info->dev = dev;
- INIT_LIST_HEAD(&device_info->safe_buffers);
- rwlock_init(&device_info->lock);
-
-#ifdef STATS
- device_info->total_allocs = 0;
- device_info->map_op_count = 0;
- device_info->bounce_count = 0;
- device_info->attr_res = device_create_file(dev, &dev_attr_dmabounce_stats);
-#endif
-
- dev->archdata.dmabounce = device_info;
-
- dev_info(dev, "dmabounce: registered device\n");
-
- return 0;
-
- err_destroy:
- dma_pool_destroy(device_info->small.pool);
- err_free:
- kfree(device_info);
- return ret;
+ struct dmabounce_info *info;
+ int error = -ENOMEM;
+
+ info = dmabounce_info_alloc(dev, small_buffer_size, large_buffer_size,
+ 0, 0);
+ if (!info)
+ goto out;
+ error = dmabounce_info_register(dev, info);
+ if (error)
+ dmabounce_info_free(info);
+out:
+ return error;
}
EXPORT_SYMBOL(dmabounce_register_dev);
void dmabounce_unregister_dev(struct device *dev)
{
- struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
-
- dev->archdata.dmabounce = NULL;
+ struct dmabounce_info *info = dev->archdata.dmabounce;
- if (!device_info) {
+ if (!info) {
dev_warn(dev,
"Never registered with dmabounce but attempting"
"to unregister!\n");
return;
}
-
- if (!list_empty(&device_info->safe_buffers)) {
- dev_err(dev,
- "Removing from dmabounce with pending buffers!\n");
- BUG();
- }
-
- if (device_info->small.pool)
- dma_pool_destroy(device_info->small.pool);
- if (device_info->large.pool)
- dma_pool_destroy(device_info->large.pool);
-
-#ifdef STATS
- if (device_info->attr_res == 0)
- device_remove_file(dev, &dev_attr_dmabounce_stats);
-#endif
-
- kfree(device_info);
-
- dev_info(dev, "dmabounce: device unregistered\n");
+ dmabounce_info_unregister(dev);
+ dmabounce_info_free(info);
}
EXPORT_SYMBOL(dmabounce_unregister_dev);
diff --git a/arch/arm/include/asm/device.h b/arch/arm/include/asm/device.h
index 9f390ce..709d501 100644
--- a/arch/arm/include/asm/device.h
+++ b/arch/arm/include/asm/device.h
@@ -8,7 +8,7 @@
struct dev_archdata {
#ifdef CONFIG_DMABOUNCE
- struct dmabounce_device_info *dmabounce;
+ struct dmabounce_info *dmabounce;
#endif
};
diff --git a/include/linux/dmabounce.h b/include/linux/dmabounce.h
index d60dc04..dc3ad5b 100644
--- a/include/linux/dmabounce.h
+++ b/include/linux/dmabounce.h
@@ -1,9 +1,6 @@
#ifndef _LINUX_DMABOUNCE_H
#define _LINUX_DMABOUNCE_H
-/* FIXME remove later when arch/arm/common/dmabounce.c is updated */
-#ifndef CONFIG_ARM
-
#ifdef CONFIG_DMABOUNCE
#include <linux/dmapool.h>
@@ -71,7 +68,4 @@ extern void dmabounce_info_unregister(struct device *dev);
#endif /* CONFIG_DMABOUNCE */
-/* FIXME remove later when arch/arm/common/dmabounce.c is updated */
-#endif /* !CONFIG_ARM */
-
#endif /* _LINUX_DMABOUNCE_H */
diff --git a/lib/dmabounce.c b/lib/dmabounce.c
index 620d314..7cb4740 100644
--- a/lib/dmabounce.c
+++ b/lib/dmabounce.c
@@ -18,9 +18,6 @@
* version 2 as published by the Free Software Foundation.
*/
-/* FIXME remove later when arch/arm/common/dmabounce.c is updated */
-#ifndef CONFIG_ARM
-
#define DRV_MODULE_NAME "dmabounce"
#define DRV_DESCRIPTION "Generic DMA bounce buffer functions"
#define DRV_AUTHOR "Christopher Hoover <ch@hpl.hp.com>, " \
@@ -390,6 +387,3 @@ EXPORT_SYMBOL(dmabounce_info_unregister);
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_LICENSE("GPL");
-
-/* FIXME remove later when arch/arm/common/dmabounce.c is updated */
-#endif /* !CONFIG_ARM */
--
1.6.3.3
^ permalink raw reply related
* [RFC PATCH v2 6/9] powerpc: add optional per-device dmabounce support
From: Albert Herranz @ 2010-02-28 14:07 UTC (permalink / raw)
To: linuxppc-dev, linux-arm-kernel, linux-usb; +Cc: Albert Herranz
In-Reply-To: <1267366082-15248-1-git-send-email-albert_herranz@yahoo.es>
Add support for the PowerPC architecture for using the generic dmabounce
support on a per-device basis.
Signed-off-by: Albert Herranz <albert_herranz@yahoo.es>
---
arch/powerpc/include/asm/device.h | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h
index 6d94d27..b9a47e8 100644
--- a/arch/powerpc/include/asm/device.h
+++ b/arch/powerpc/include/asm/device.h
@@ -28,6 +28,9 @@ struct dev_archdata {
#ifdef CONFIG_SWIOTLB
dma_addr_t max_direct_dma_addr;
#endif
+#ifdef CONFIG_DMABOUNCE
+ struct dmabounce_info *dmabounce;
+#endif
};
static inline void dev_archdata_set_node(struct dev_archdata *ad,
--
1.6.3.3
^ permalink raw reply related
* [RFC PATCH v2 7/9] wii: add mem2 dma mapping ops
From: Albert Herranz @ 2010-02-28 14:08 UTC (permalink / raw)
To: linuxppc-dev, linux-arm-kernel, linux-usb; +Cc: Albert Herranz
In-Reply-To: <1267366082-15248-1-git-send-email-albert_herranz@yahoo.es>
Some of the devices in the "Hollywood" chipset of the Nintendo Wii video
game console have restrictions performing DMA transfers to the first
contiguous RAM region (known as MEM1).
For example, up to 3 bytes of the last word of a DMA transfer of a
non-32 bit aligned length to MEM1 may be lost.
Such restrictions do not apply when using the second contiguous RAM
region (known as MEM2).
Add a set of DMA mapping operations which said devices can use to make
sure that DMA transfers are always performed to/from memory buffers
within MEM2.
Signed-off-by: Albert Herranz <albert_herranz@yahoo.es>
QUICKFIX: wii-dma
Signed-off-by: Albert Herranz <albert_herranz@yahoo.es>
---
arch/powerpc/boot/wii.c | 34 ++
arch/powerpc/include/asm/wii.h | 25 ++
arch/powerpc/platforms/embedded6xx/Kconfig | 1 +
arch/powerpc/platforms/embedded6xx/Makefile | 2 +-
arch/powerpc/platforms/embedded6xx/wii-dma.c | 557 ++++++++++++++++++++++++++
5 files changed, 618 insertions(+), 1 deletions(-)
create mode 100644 arch/powerpc/include/asm/wii.h
create mode 100755 arch/powerpc/platforms/embedded6xx/wii-dma.c
diff --git a/arch/powerpc/boot/wii.c b/arch/powerpc/boot/wii.c
index 2ebaec0..f884006 100644
--- a/arch/powerpc/boot/wii.c
+++ b/arch/powerpc/boot/wii.c
@@ -30,6 +30,9 @@ BSS_STACK(8192);
#define MEM2_TOP (0x10000000 + 64*1024*1024)
#define FIRMWARE_DEFAULT_SIZE (12*1024*1024)
+#define MEM2_DMA_BASE_PROP "linux,wii-mem2-dma-base"
+#define MEM2_DMA_SIZE_PROP "linux,wii-mem2-dma-size"
+#define MEM2_DMA_DEFAULT_SIZE (1*1024*1024)
struct mipc_infohdr {
char magic[3];
@@ -101,6 +104,30 @@ out:
}
+static void mem2_fixups(u32 *top, u32 *reg)
+{
+ void *chosen;
+ u32 dma_base, dma_size;
+ int len;
+
+ chosen = finddevice("/chosen");
+ if (!chosen)
+ fatal("Can't find chosen node\n");
+
+ len = getprop(chosen, MEM2_DMA_SIZE_PROP, &dma_size, sizeof(dma_size));
+ if (len != sizeof(dma_size))
+ dma_size = MEM2_DMA_DEFAULT_SIZE;
+ if (dma_size > reg[3])
+ dma_size = reg[3];
+ setprop_val(chosen, MEM2_DMA_SIZE_PROP, dma_size);
+
+ *top -= dma_size;
+ dma_base = *top;
+ setprop_val(chosen, MEM2_DMA_BASE_PROP, dma_base);
+
+ printf("mem2_dma: %08X@%08X\n", dma_size, dma_base);
+}
+
static void platform_fixups(void)
{
void *mem;
@@ -127,9 +154,16 @@ static void platform_fixups(void)
mem2_boundary = MEM2_TOP - FIRMWARE_DEFAULT_SIZE;
}
+ mem2_fixups(&mem2_boundary, reg);
+
if (mem2_boundary > reg[2] && mem2_boundary < reg[2] + reg[3]) {
reg[3] = mem2_boundary - reg[2];
printf("top of MEM2 @ %08X\n", reg[2] + reg[3]);
+ /*
+ * Find again the memory node as it may have changed its
+ * position after adding some non-existing properties.
+ */
+ mem = finddevice("/memory");
setprop(mem, "reg", reg, sizeof(reg));
}
diff --git a/arch/powerpc/include/asm/wii.h b/arch/powerpc/include/asm/wii.h
new file mode 100644
index 0000000..bb83c32
--- /dev/null
+++ b/arch/powerpc/include/asm/wii.h
@@ -0,0 +1,25 @@
+/*
+ * arch/powerpc/include/asm/wii.h
+ *
+ * Nintendo Wii board-specific definitions
+ * Copyright (C) 2010 The GameCube Linux Team
+ * Copyright (C) 2010 Albert Herranz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#ifndef __ASM_POWERPC_WII_H
+#define __ASM_POWERPC_WII_H
+
+/*
+ * DMA operations for the Nintendo Wii.
+ */
+extern struct dma_map_ops wii_mem2_dma_ops;
+
+extern int wii_set_mem2_dma_constraints(struct device *dev);
+extern void wii_clear_mem2_dma_constraints(struct device *dev);
+
+#endif /* __ASM_POWERPC_WII_H */
diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig
index fe77ab2..4d33755 100644
--- a/arch/powerpc/platforms/embedded6xx/Kconfig
+++ b/arch/powerpc/platforms/embedded6xx/Kconfig
@@ -120,6 +120,7 @@ config WII
depends on EMBEDDED6xx
select GAMECUBE_COMMON
select HAVE_GENERIC_DMA_COHERENT
+ select HAVE_DMABOUNCE
help
Select WII if configuring for the Nintendo Wii.
More information at: <http://gc-linux.sourceforge.net/>
diff --git a/arch/powerpc/platforms/embedded6xx/Makefile b/arch/powerpc/platforms/embedded6xx/Makefile
index 66c23e4..4d4c776 100644
--- a/arch/powerpc/platforms/embedded6xx/Makefile
+++ b/arch/powerpc/platforms/embedded6xx/Makefile
@@ -10,4 +10,4 @@ obj-$(CONFIG_PPC_C2K) += c2k.o
obj-$(CONFIG_USBGECKO_UDBG) += usbgecko_udbg.o
obj-$(CONFIG_GAMECUBE_COMMON) += flipper-pic.o
obj-$(CONFIG_GAMECUBE) += gamecube.o
-obj-$(CONFIG_WII) += wii.o hlwd-pic.o
+obj-$(CONFIG_WII) += wii.o hlwd-pic.o wii-dma.o
diff --git a/arch/powerpc/platforms/embedded6xx/wii-dma.c b/arch/powerpc/platforms/embedded6xx/wii-dma.c
new file mode 100755
index 0000000..d0d2c1f
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/wii-dma.c
@@ -0,0 +1,557 @@
+/*
+ * arch/powerpc/platforms/embedded6xx/wii-dma.c
+ *
+ * DMA functions for the Nintendo Wii video game console.
+ * Copyright (C) 2010 The GameCube Linux Team
+ * Copyright (C) 2010 Albert Herranz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+#undef BOUNCE_ALL
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmabounce.h>
+#include <linux/of.h>
+#include <linux/lmb.h>
+#include <asm/wii.h>
+
+#include "mm/mmu_decl.h"
+
+#define MEM2_DMA_BASE_PROP "linux,wii-mem2-dma-base"
+#define MEM2_DMA_SIZE_PROP "linux,wii-mem2-dma-size"
+
+#define MEM2_DMA_MAPPING_ERROR ((~(dma_addr_t)0)-1)
+
+#define MEM2_BOUNCE_ALIGN 32 /* cache line size */
+#define MEM2_ACCESS_ALIGN 4
+
+#define __align_ofs(x, size) ((unsigned long)(x)&((size)-1))
+#define __align_up(x, size) (((unsigned long)(x)+((size)-1))&(~((size)-1)))
+#define __align_down(x, size) (((unsigned long)(x))&(~((size)-1)))
+
+/* get the offset from the previous aligned access boundary */
+#define mem2_align_ofs(x) __align_ofs((x), MEM2_ACCESS_ALIGN)
+/* adjust to the previous aligned access boundary */
+#define mem2_align_down(x) __align_down((x), MEM2_ACCESS_ALIGN)
+/* adjust to the next aligned bouncing boundary */
+#define mem2_align_up(x) __align_up((x), MEM2_BOUNCE_ALIGN)
+
+/*
+ * The Nintendo Wii video game console is a NOT_COHERENT_CACHE
+ * platform that is unable to safely perform non-32 bit uncached writes
+ * to RAM because the byte enables are not connected to the bus.
+ * Thus, in this platform, "coherent" DMA buffers cannot be directly used
+ * by the kernel code unless it guarantees that all write accesses
+ * to said buffers are done in 32 bit chunks.
+ *
+ * In addition, some of the devices in the "Hollywood" chipset have a
+ * similar restriction regarding DMA transfers: those with non-32bit
+ * aligned lengths only work when performed to/from the second contiguous
+ * region of memory (known as MEM2).
+ *
+ * To solve these issues a specific set of dma mapping operations is made
+ * available for devices requiring it. When enabled, the kernel will make
+ * sure that DMA buffers sitting in MEM1 get bounced to/from coherent DMA
+ * buffers allocated from MEM2, and that the actual bouncing is done by
+ * using 32-bit accesses to coherent memory.
+ *
+ * Bouncing is performed with the help of the generic dmabounce support.
+ */
+
+/*
+ * Copies @len bytes from the coherent memory region starting at
+ * @src + @offset to the memory region starting at @dst + @offset.
+ * The source coherent memory region length is guaranteed to be
+ * 32-bit aligned _and_ equal or greater than @len.
+ *
+ * Reads from coherent memory have no access restrictions.
+ */
+static inline void *memcpy_from_coherent(void *dst, void *src,
+ unsigned long offset, size_t len)
+{
+ memcpy(dst + offset, src + offset, len);
+ return dst;
+}
+
+/*
+ * Copies @len bytes from the memory region starting at @src + @offset
+ * to the coherent memory region starting at @dst + @offset.
+ * The destination coherent memory region length is guaranteed to be
+ * 32-bit aligned _and_ equal or greater than @len.
+ *
+ * Because of the write access restrictions, all writes to the
+ * destination coherent memory region must be performed in 32-bit chunks.
+ */
+static void *memcpy_to_coherent(void *dst, void *src,
+ unsigned long offset, size_t len)
+{
+ u32 *p4, *q4, v4;
+ u8 *p1, *q1;
+ size_t chunk_delta, chunk_size;
+
+ if (!len)
+ return dst;
+
+ /* first copy the unaligned prefix, if available */
+ q4 = dst + offset;
+ p4 = src + offset;
+ chunk_size = 0;
+ chunk_delta = mem2_align_ofs(q4);
+ if (chunk_delta) {
+ chunk_size = min(len, MEM2_ACCESS_ALIGN - chunk_delta);
+ q4 = (u32 *)mem2_align_down(q4);
+ v4 = *q4;
+ q1 = (u8 *)&v4;
+ memcpy(q1 + chunk_delta, p4, chunk_size);
+ *q4++ = v4;
+ p4 = src + offset + chunk_size;
+ len -= chunk_size;
+ }
+
+ /* second, perform the aligned central copy */
+ while (len >= 4) {
+ *q4++ = *p4++;
+ len -= 4;
+ }
+
+ /* finally, copy the unaligned trailing chunk if needed */
+ p1 = (u8 *)p4;
+ v4 = *q4;
+ q1 = (u8 *)&v4;
+ switch (len) {
+ case 3:
+ *q4 = p1[0] << 24 | p1[1] << 16 | p1[2] << 8 | q1[3];
+ break;
+ case 2:
+ *q4 = p1[0] << 24 | p1[1] << 16 | q1[2] << 8 | q1[3];
+ break;
+ case 1:
+ *q4 = p1[0] << 24 | q1[1] << 16 | q1[2] << 8 | q1[3];
+ break;
+ default:
+ break;
+ }
+ return dst;
+}
+
+/*
+ * Determines if a given DMA region specified by @dma_handle and @size
+ * requires bouncing.
+ *
+ * Bouncing is required if the DMA region falls within MEM1.
+ */
+static int mem2_needs_dmabounce(dma_addr_t dma_handle)
+{
+#ifndef BOUNCE_ALL
+ return dma_handle < wii_hole_start;
+#else
+ return 1;
+#endif
+}
+
+static int mem2_mapping_error(struct device *dev, dma_addr_t dma_handle)
+{
+ return dma_handle == MEM2_DMA_MAPPING_ERROR;
+}
+
+/*
+ * Use the dma_direct_ops hooks for allocating and freeing coherent memory.
+ * Idem for checking if a device supports a specific DMA mask.
+ */
+
+static void *mem2_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t gfp)
+{
+ return dma_direct_ops.alloc_coherent(dev, size, dma_handle, gfp);
+}
+
+static void mem2_free_coherent(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dma_handle)
+{
+ return dma_direct_ops.free_coherent(dev, size, vaddr, dma_handle);
+}
+
+static int mem2_dma_supported(struct device *dev, u64 mask)
+{
+ return dma_direct_ops.dma_supported(dev, mask);
+}
+
+/*
+ * Maps (part of) a page so it can be safely accessed by a device.
+ *
+ * Calls the corresponding dma_direct_ops hook if the page region falls
+ * within MEM2.
+ * Otherwise, a bounce buffer allocated from MEM2 coherent memory is used.
+ */
+static dma_addr_t mem2_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size,
+ enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ struct dmabounce_info *info = dev->archdata.dmabounce;
+ void *buf = page_address(page) + offset;
+ dma_addr_t dma_handle = phys_to_dma(dev, page_to_phys(page) + offset);
+ size_t up_size = mem2_align_up(size);
+ struct dmabounce_buffer *bb;
+
+ BUG_ON(!info);
+ DMABOUNCE_DO_STATS(info, map_op_count++);
+
+ if (!mem2_needs_dmabounce(dma_handle)) {
+ dma_handle = dma_direct_ops.map_page(dev, page, offset, size,
+ dir, attrs);
+ goto out;
+ }
+
+ bb = dmabounce_alloc_buffer(info, buf, up_size, dir, GFP_ATOMIC);
+ if (!bb) {
+ pr_debug("%s: dmabounce_alloc_buffer error\n", __func__);
+ dma_handle = MEM2_DMA_MAPPING_ERROR;
+ goto out;
+ }
+
+ if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)
+ memcpy_to_coherent(bb->bounce_buf, bb->buf, 0, size);
+ dma_handle = bb->bounce_buf_dma;
+out:
+ return dma_handle;
+}
+
+/*
+ * Unmaps (part of) a page previously mapped.
+ *
+ * Calls the corresponding dma_direct_ops hook if the DMA region associated
+ * to the dma handle @dma_handle wasn't bounced.
+ * Otherwise, the associated bounce buffer is de-bounced.
+ */
+static void mem2_unmap_page(struct device *dev, dma_addr_t dma_handle,
+ size_t size, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ struct dmabounce_info *info = dev->archdata.dmabounce;
+ size_t up_size = mem2_align_up(size);
+ struct dmabounce_buffer *bb;
+
+ BUG_ON(!info);
+
+ bb = dmabounce_find_buffer(info, dma_handle, up_size, dir);
+ if (!bb) {
+ dma_direct_ops.unmap_page(dev, dma_handle, size, dir, attrs);
+ return;
+ }
+
+ if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) {
+ DMABOUNCE_DO_STATS(info, bounce_count++);
+ memcpy_from_coherent(bb->buf, bb->bounce_buf, 0, size);
+ __dma_sync(bb->buf, size, DMA_BIDIRECTIONAL);
+ }
+ dmabounce_free_buffer(info, bb);
+}
+
+/*
+ * Unmaps a scatter/gather list by unmapping each entry.
+ */
+void mem2_unmap_sg(struct device *dev, struct scatterlist *sgl, int nents,
+ enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgl, sg, nents, i)
+ mem2_unmap_page(dev, sg->dma_address, sg->length, dir, attrs);
+}
+
+/*
+ * Maps a scatter/gather list by mapping each entry.
+ */
+static int mem2_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
+ enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgl, sg, nents, i) {
+ sg->dma_length = sg->length;
+ sg->dma_address = mem2_map_page(dev, sg_page(sg), sg->offset,
+ sg->length, dir, attrs);
+ if (mem2_mapping_error(dev, sg->dma_address)) {
+ mem2_unmap_sg(dev, sgl, i, dir, attrs);
+ nents = 0;
+ sgl[nents].dma_length = 0;
+ pr_debug("%s: mem2_map_page error\n", __func__);
+ break;
+ }
+ }
+ return nents;
+}
+
+/*
+ * The sync functions synchronize streaming mode DMA translations
+ * making physical memory consistent before/after a DMA transfer.
+ *
+ * They call the corresponding dma_direct_ops hook if the DMA region
+ * associated to the dma handle @dma_handle wasn't bounced.
+ * Otherwise, original DMA buffers and their matching bounce buffers are put
+ * in sync.
+ * Also, for correctness, original DMA buffers are also made consistent.
+ */
+
+static void mem2_sync_range_for_cpu(struct device *dev, dma_addr_t dma_handle,
+ unsigned long offset, size_t size,
+ enum dma_data_direction dir)
+{
+ struct dmabounce_info *info = dev->archdata.dmabounce;
+ struct dmabounce_buffer *bb;
+
+ BUG_ON(!info);
+
+ dev_dbg(dev, "%s(dma=%#x,off=%#lx,sz=%zx,dir=%x)\n",
+ __func__, dma_handle, offset, size, dir);
+
+ bb = dmabounce_find_buffer(info, dma_handle, 0, dir);
+ if (!bb) {
+ dma_direct_ops.sync_single_range_for_cpu(dev, dma_handle,
+ offset, size, dir);
+ return;
+ }
+ BUG_ON(offset + size > bb->size);
+
+ DMABOUNCE_DO_STATS(info, bounce_count++);
+
+ if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) {
+ dev_dbg(dev, "%s: copy back bounce %p to buf %p, size %d\n",
+ __func__, bb->bounce_buf + offset, bb->buf + offset,
+ size);
+ memcpy_from_coherent(bb->buf, bb->bounce_buf, offset, size);
+ __dma_sync(bb->buf + offset, size, DMA_BIDIRECTIONAL);
+ }
+}
+
+static void mem2_sync_range_for_device(struct device *dev,
+ dma_addr_t dma_handle,
+ unsigned long offset, size_t size,
+ enum dma_data_direction dir)
+{
+ struct dmabounce_info *info = dev->archdata.dmabounce;
+ struct dmabounce_buffer *bb;
+
+ BUG_ON(!info);
+
+ dev_dbg(dev, "%s(dma=%#x,off=%#lx,sz=%zx,dir=%x)\n",
+ __func__, dma_handle, offset, size, dir);
+
+ bb = dmabounce_find_buffer(info, dma_handle, 0, dir);
+ if (!bb) {
+ WARN_ON(1);
+ dma_direct_ops.sync_single_range_for_device(dev, dma_handle,
+ offset, size, dir);
+ return;
+ }
+ BUG_ON(offset + size > bb->size);
+
+ DMABOUNCE_DO_STATS(info, bounce_count++);
+
+ if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) {
+ dev_dbg(dev, "%s: copy out buf %p to bounce %p, size %d\n",
+ __func__, bb->buf + offset, bb->bounce_buf + offset,
+ size);
+ memcpy_to_coherent(bb->bounce_buf, bb->buf, offset, size);
+ }
+}
+
+static void mem2_sync_sg_for_cpu(struct device *dev,
+ struct scatterlist *sgl, int nents,
+ enum dma_data_direction dir)
+{
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgl, sg, nents, i) {
+ mem2_sync_range_for_cpu(dev, sg_dma_address(sg), sg->offset,
+ sg_dma_len(sg), dir);
+ }
+}
+
+static void mem2_sync_sg_for_device(struct device *dev,
+ struct scatterlist *sgl, int nents,
+ enum dma_data_direction dir)
+{
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgl, sg, nents, i) {
+ mem2_sync_range_for_device(dev, sg_dma_address(sg), sg->offset,
+ sg_dma_len(sg), dir);
+ }
+}
+
+/*
+ * The mem2_dma "device".
+ *
+ * This device "owns" a pool of coherent MEM2 memory that can be shared among
+ * several devices requiring MEM2 DMA buffers, instead of dedicating specific
+ * pools for each device.
+ *
+ * A device can use the shared coherent MEM2 memory pool by:
+ * - allocating a dmabounce_info struct associated to the mem2_dma device
+ * - registering this dmabounce_info with the device
+ * - making wii_mem2_dma_ops the default DMA operations for the device
+ */
+
+struct mem2_dma {
+ struct platform_device *pdev;
+ struct dmabounce_info *info;
+
+ dma_addr_t dma_base;
+ void *base;
+ size_t size;
+};
+
+static struct mem2_dma mem2_dma_instance;
+
+static inline struct mem2_dma *mem2_dma_get_instance(void)
+{
+ return &mem2_dma_instance;
+}
+
+static int __init mem2_dma_init(dma_addr_t dma_base, size_t size)
+{
+ struct mem2_dma *mem2_dma = mem2_dma_get_instance();
+ const int flags = DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE;
+ struct device *dev;
+ int error = 0;
+
+ mem2_dma->pdev = platform_device_register_simple("mem2_dma",
+ 0, NULL, 0);
+ if (IS_ERR(mem2_dma->pdev)) {
+ error = PTR_ERR(mem2_dma->pdev);
+ pr_err("error %d registering platform device\n", error);
+ goto err_pdev_register;
+ }
+ dev = &mem2_dma->pdev->dev;
+
+ if (!dma_declare_coherent_memory(dev, dma_base, dma_base,
+ size, flags)) {
+ dev_err(dev, "error declaring coherent memory %zu@%Lx\n",
+ size, (unsigned long long)dma_base);
+ error = -EBUSY;
+ goto err_declare_coherent;
+ }
+ mem2_dma->dma_base = dma_base;
+ mem2_dma->size = size;
+ dev_info(dev, "using %zu KiB at 0x%Lx\n", size / 1024,
+ (unsigned long long)dma_base);
+ goto out;
+
+err_declare_coherent:
+ platform_device_unregister(mem2_dma->pdev);
+err_pdev_register:
+ mem2_dma->pdev = NULL;
+out:
+ return error;
+}
+
+static int __init mem2_dma_setup(void)
+{
+ const dma_addr_t *dma_base;
+ const size_t *dma_size;
+ int error = -ENODEV;
+
+ dma_base = of_get_property(of_chosen, MEM2_DMA_BASE_PROP, NULL);
+ if (!dma_base) {
+ pr_err("can't find %s property\n", MEM2_DMA_BASE_PROP);
+ goto out;
+ }
+
+ dma_size = of_get_property(of_chosen, MEM2_DMA_SIZE_PROP, NULL);
+ if (!dma_size) {
+ pr_err("can't find %s property\n", MEM2_DMA_SIZE_PROP);
+ goto out;
+ }
+
+ error = mem2_dma_init(*dma_base, *dma_size);
+ if (error)
+ pr_err("error %d during setup\n", error);
+out:
+ return error;
+}
+arch_initcall(mem2_dma_setup);
+
+/**
+ * wii_mem2_dma_dev() - returns the device "owning" the shared MEM2 DMA region
+ *
+ * Use this function to retrieve the device for which the shared pool of
+ * coherent MEM2 memory has been registered.
+ */
+static struct device *wii_mem2_dma_dev(void)
+{
+ struct mem2_dma *mem2_dma = mem2_dma_get_instance();
+ BUG_ON(!mem2_dma->pdev);
+ return &mem2_dma->pdev->dev;
+}
+
+/**
+ * wii_set_mem2_dma_constraints() - forces device to use MEM2 DMA buffers only
+ * @dev: device for which DMA constraints are defined
+ *
+ * Instructs device @dev to always use MEM2 DMA buffers for DMA transfers.
+ */
+int wii_set_mem2_dma_constraints(struct device *dev)
+{
+ struct dmabounce_info *info;
+ int error = -ENOMEM;
+
+ info = dmabounce_info_alloc(wii_mem2_dma_dev(), 0, 0, 4, 0);
+ if (!info)
+ goto out;
+ error = dmabounce_info_register(dev, info);
+ if (error)
+ goto out;
+ set_dma_ops(dev, &wii_mem2_dma_ops);
+out:
+ return error;
+}
+EXPORT_SYMBOL(wii_set_mem2_dma_constraints);
+
+/**
+ * wii_clear_mem2_dma_constraints() - clears device MEM2 DMA constraints
+ * @dev: device for which DMA constraints are cleared
+ *
+ * Instructs device @dev to stop using MEM2 DMA buffers for DMA transfers.
+ * Must be called to undo wii_set_mem2_dma_constraints().
+ */
+void wii_clear_mem2_dma_constraints(struct device *dev)
+{
+ struct dmabounce_info *info = dev->archdata.dmabounce;
+
+ if (info) {
+ dmabounce_info_unregister(dev);
+ dmabounce_info_free(info);
+ set_dma_ops(dev, &dma_direct_ops);
+ }
+}
+EXPORT_SYMBOL(wii_clear_mem2_dma_constraints);
+
+/*
+ * Set of DMA operations for devices requiring MEM2 DMA buffers.
+ */
+struct dma_map_ops wii_mem2_dma_ops = {
+ .alloc_coherent = mem2_alloc_coherent,
+ .free_coherent = mem2_free_coherent,
+ .map_sg = mem2_map_sg,
+ .unmap_sg = mem2_unmap_sg,
+ .map_page = mem2_map_page,
+ .unmap_page = mem2_unmap_page,
+ .sync_single_range_for_cpu = mem2_sync_range_for_cpu,
+ .sync_single_range_for_device = mem2_sync_range_for_device,
+ .sync_sg_for_cpu = mem2_sync_sg_for_cpu,
+ .sync_sg_for_device = mem2_sync_sg_for_device,
+ .dma_supported = mem2_dma_supported,
+ .mapping_error = mem2_mapping_error,
+};
--
1.6.3.3
^ permalink raw reply related
* [RFC PATCH v2 8/9] USB: add HCD_NO_COHERENT_MEM host controller driver flag
From: Albert Herranz @ 2010-02-28 14:08 UTC (permalink / raw)
To: linuxppc-dev, linux-arm-kernel, linux-usb; +Cc: Albert Herranz
In-Reply-To: <1267366082-15248-1-git-send-email-albert_herranz@yahoo.es>
The HCD_NO_COHERENT_MEM USB host controller driver flag can be enabled
to instruct the USB stack to avoid allocating coherent memory for USB
buffers.
This flag is useful to overcome some esoteric memory access restrictions
found in some platforms.
For example, the Nintendo Wii video game console is a NOT_COHERENT_CACHE
platform that is unable to safely perform non-32 bit uncached writes
to RAM because the byte enables are not connected to the bus.
Thus, in that platform, "coherent" DMA buffers cannot be directly used
by the kernel code unless it guarantees that all write accesses
to said buffers are done in 32 bit chunks (which is not the case in the
USB subsystem).
To avoid this unwanted behaviour HCD_NO_COHERENT_MEM can be enabled at
the HCD controller, causing USB buffer allocations to be satisfied from
normal kernel memory. In this case, the USB stack will make sure that
the buffers get properly mapped/unmapped for DMA transfers using the DMA
streaming mapping API.
Note that other parts of the USB stack may also use coherent memory,
like for example the hardware descriptors used in the EHCI controllers.
This needs to be checked and addressed separately. In the EHCI example,
hardware descriptors are accessed in 32-bit (or 64-bit) chunks, causing
no problems in the described scenario.
Signed-off-by: Albert Herranz <albert_herranz@yahoo.es>
---
drivers/usb/core/buffer.c | 15 ++++++++-----
drivers/usb/core/hcd.c | 50 +++++++++++++++++++++++++++++++++++++-------
drivers/usb/core/hcd.h | 13 ++++++-----
3 files changed, 58 insertions(+), 20 deletions(-)
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c
index 3ba2fff..fede7a0 100644
--- a/drivers/usb/core/buffer.c
+++ b/drivers/usb/core/buffer.c
@@ -53,8 +53,9 @@ int hcd_buffer_create(struct usb_hcd *hcd)
char name[16];
int i, size;
- if (!hcd->self.controller->dma_mask &&
- !(hcd->driver->flags & HCD_LOCAL_MEM))
+ if ((!hcd->self.controller->dma_mask &&
+ !(hcd->driver->flags & HCD_LOCAL_MEM)) ||
+ (hcd->driver->flags & HCD_NO_COHERENT_MEM))
return 0;
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
@@ -109,8 +110,9 @@ void *hcd_buffer_alloc(
int i;
/* some USB hosts just use PIO */
- if (!bus->controller->dma_mask &&
- !(hcd->driver->flags & HCD_LOCAL_MEM)) {
+ if ((!bus->controller->dma_mask &&
+ !(hcd->driver->flags & HCD_LOCAL_MEM)) ||
+ (hcd->driver->flags & HCD_NO_COHERENT_MEM)) {
*dma = ~(dma_addr_t) 0;
return kmalloc(size, mem_flags);
}
@@ -135,8 +137,9 @@ void hcd_buffer_free(
if (!addr)
return;
- if (!bus->controller->dma_mask &&
- !(hcd->driver->flags & HCD_LOCAL_MEM)) {
+ if ((!bus->controller->dma_mask &&
+ !(hcd->driver->flags & HCD_LOCAL_MEM)) ||
+ (hcd->driver->flags & HCD_NO_COHERENT_MEM)) {
kfree(addr);
return;
}
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 80995ef..c2596c5 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1260,6 +1260,34 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle,
*dma_handle = 0;
}
+static int urb_needs_setup_dma_map(struct usb_hcd *hcd, struct urb *urb)
+{
+ return !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP) ||
+ ((hcd->driver->flags & HCD_NO_COHERENT_MEM) &&
+ urb->setup_dma == ~(dma_addr_t)0);
+}
+
+static int urb_needs_setup_dma_unmap(struct usb_hcd *hcd, struct urb *urb)
+{
+ return !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP) ||
+ ((hcd->driver->flags & HCD_NO_COHERENT_MEM) &&
+ urb->setup_dma && urb->setup_dma != ~(dma_addr_t)0);
+}
+
+static int urb_needs_transfer_dma_map(struct usb_hcd *hcd, struct urb *urb)
+{
+ return !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) ||
+ ((hcd->driver->flags & HCD_NO_COHERENT_MEM) &&
+ urb->transfer_dma == ~(dma_addr_t)0);
+}
+
+static int urb_needs_transfer_dma_unmap(struct usb_hcd *hcd, struct urb *urb)
+{
+ return !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) ||
+ ((hcd->driver->flags & HCD_NO_COHERENT_MEM) &&
+ urb->transfer_dma && urb->transfer_dma != ~(dma_addr_t)0);
+}
+
static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags)
{
@@ -1275,7 +1303,7 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
return 0;
if (usb_endpoint_xfer_control(&urb->ep->desc)
- && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) {
+ && urb_needs_setup_dma_map(hcd, urb)) {
if (hcd->self.uses_dma) {
urb->setup_dma = dma_map_single(
hcd->self.controller,
@@ -1296,7 +1324,7 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
if (ret == 0 && urb->transfer_buffer_length != 0
- && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
+ && urb_needs_transfer_dma_map(hcd, urb)) {
if (hcd->self.uses_dma) {
urb->transfer_dma = dma_map_single (
hcd->self.controller,
@@ -1334,12 +1362,15 @@ static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
return;
if (usb_endpoint_xfer_control(&urb->ep->desc)
- && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) {
- if (hcd->self.uses_dma)
+ && urb_needs_setup_dma_unmap(hcd, urb)) {
+ if (hcd->self.uses_dma) {
dma_unmap_single(hcd->self.controller, urb->setup_dma,
sizeof(struct usb_ctrlrequest),
DMA_TO_DEVICE);
- else if (hcd->driver->flags & HCD_LOCAL_MEM)
+ if ((hcd->driver->flags & HCD_NO_COHERENT_MEM) &&
+ (urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
+ urb->setup_dma = ~(dma_addr_t)0;
+ } else if (hcd->driver->flags & HCD_LOCAL_MEM)
hcd_free_coherent(urb->dev->bus, &urb->setup_dma,
(void **)&urb->setup_packet,
sizeof(struct usb_ctrlrequest),
@@ -1348,13 +1379,16 @@ static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
if (urb->transfer_buffer_length != 0
- && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
- if (hcd->self.uses_dma)
+ && urb_needs_transfer_dma_unmap(hcd, urb)) {
+ if (hcd->self.uses_dma) {
dma_unmap_single(hcd->self.controller,
urb->transfer_dma,
urb->transfer_buffer_length,
dir);
- else if (hcd->driver->flags & HCD_LOCAL_MEM)
+ if ((hcd->driver->flags & HCD_NO_COHERENT_MEM) &&
+ (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
+ urb->transfer_dma = ~(dma_addr_t)0;
+ } else if (hcd->driver->flags & HCD_LOCAL_MEM)
hcd_free_coherent(urb->dev->bus, &urb->transfer_dma,
&urb->transfer_buffer,
urb->transfer_buffer_length,
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index bbe2b92..cde42f3 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -183,12 +183,13 @@ struct hc_driver {
irqreturn_t (*irq) (struct usb_hcd *hcd);
int flags;
-#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */
-#define HCD_LOCAL_MEM 0x0002 /* HC needs local memory */
-#define HCD_USB11 0x0010 /* USB 1.1 */
-#define HCD_USB2 0x0020 /* USB 2.0 */
-#define HCD_USB3 0x0040 /* USB 3.0 */
-#define HCD_MASK 0x0070
+#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */
+#define HCD_LOCAL_MEM 0x0002 /* HC needs local memory */
+#define HCD_NO_COHERENT_MEM 0x0004 /* HC avoids use of "coherent" mem */
+#define HCD_USB11 0x0010 /* USB 1.1 */
+#define HCD_USB2 0x0020 /* USB 2.0 */
+#define HCD_USB3 0x0040 /* USB 3.0 */
+#define HCD_MASK 0x0070
/* called to init HCD and root hub */
int (*reset) (struct usb_hcd *hcd);
--
1.6.3.3
^ 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