[POWERPC] PS3: Frame buffer system-bus rework

Convert the ps3fb device from a platform device to a PS3 system bus device.
Fix the remove and shutdown methods to support kexec and to make ps3fb a
loadable module.

Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
Geert Uytterhoeven 2007-06-16 08:05:38 +10:00 committed by Paul Mackerras
parent 13a5e30cf7
commit 9e6b99bd44
4 changed files with 137 additions and 184 deletions

View File

@ -107,7 +107,7 @@ static void ps3_panic(char *str)
while(1); while(1);
} }
#ifdef CONFIG_FB_PS3 #if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE)
static void prealloc(struct ps3_prealloc *p) static void prealloc(struct ps3_prealloc *p)
{ {
if (!p->size) if (!p->size)
@ -125,10 +125,11 @@ static void prealloc(struct ps3_prealloc *p)
} }
struct ps3_prealloc ps3fb_videomemory = { struct ps3_prealloc ps3fb_videomemory = {
.name = "ps3fb videomemory", .name = "ps3fb videomemory",
.size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024, .size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024,
.align = 1024*1024 /* the GPU requires 1 MiB alignment */ .align = 1024*1024 /* the GPU requires 1 MiB alignment */
}; };
EXPORT_SYMBOL_GPL(ps3fb_videomemory);
#define prealloc_ps3fb_videomemory() prealloc(&ps3fb_videomemory) #define prealloc_ps3fb_videomemory() prealloc(&ps3fb_videomemory)
static int __init early_parse_ps3fb(char *p) static int __init early_parse_ps3fb(char *p)

View File

@ -1790,8 +1790,8 @@ config FB_IBM_GXT4500
adaptor, found on some IBM System P (pSeries) machines. adaptor, found on some IBM System P (pSeries) machines.
config FB_PS3 config FB_PS3
bool "PS3 GPU framebuffer driver" tristate "PS3 GPU framebuffer driver"
depends on (FB = y) && PS3_PS3AV depends on FB && PS3_PS3AV
select FB_SYS_FILLRECT select FB_SYS_FILLRECT
select FB_SYS_COPYAREA select FB_SYS_COPYAREA
select FB_SYS_IMAGEBLIT select FB_SYS_IMAGEBLIT

View File

@ -27,7 +27,6 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/console.h> #include <linux/console.h>
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/notifier.h> #include <linux/notifier.h>
@ -46,6 +45,9 @@
#include <asm/ps3fb.h> #include <asm/ps3fb.h>
#include <asm/ps3.h> #include <asm/ps3.h>
#define DEVICE_NAME "ps3fb"
#ifdef PS3FB_DEBUG #ifdef PS3FB_DEBUG
#define DPRINTK(fmt, args...) printk("%s: " fmt, __func__ , ##args) #define DPRINTK(fmt, args...) printk("%s: " fmt, __func__ , ##args)
#else #else
@ -126,7 +128,6 @@ struct gpu_driver_info {
struct ps3fb_priv { struct ps3fb_priv {
unsigned int irq_no; unsigned int irq_no;
void *dev;
u64 context_handle, memory_handle; u64 context_handle, memory_handle;
void *xdr_ea; void *xdr_ea;
@ -171,7 +172,7 @@ static const struct ps3fb_res_table ps3fb_res[] = {
{ 0, 0, 0, 0 , 0} }; { 0, 0, 0, 0 , 0} };
/* default resolution */ /* default resolution */
#define GPU_RES_INDEX 0 /* 720 x 480 */ #define GPU_RES_INDEX 0 /* 720 x 480 */
static const struct fb_videomode ps3fb_modedb[] = { static const struct fb_videomode ps3fb_modedb[] = {
/* 60 Hz broadcast modes (modes "1" to "5") */ /* 60 Hz broadcast modes (modes "1" to "5") */
@ -298,10 +299,9 @@ static const struct fb_videomode ps3fb_modedb[] = {
#define FB_OFF(i) (GPU_OFFSET - VP_OFF(i) % GPU_OFFSET) #define FB_OFF(i) (GPU_OFFSET - VP_OFF(i) % GPU_OFFSET)
static int ps3fb_mode; static int ps3fb_mode;
module_param(ps3fb_mode, bool, 0); module_param(ps3fb_mode, int, 0);
static char *mode_option __initdata;
static char *mode_option __devinitdata;
static int ps3fb_get_res_table(u32 xres, u32 yres) static int ps3fb_get_res_table(u32 xres, u32 yres)
{ {
@ -681,15 +681,15 @@ int ps3fb_wait_for_vsync(u32 crtc)
EXPORT_SYMBOL_GPL(ps3fb_wait_for_vsync); EXPORT_SYMBOL_GPL(ps3fb_wait_for_vsync);
void ps3fb_flip_ctl(int on) void ps3fb_flip_ctl(int on, void *data)
{ {
struct ps3fb_priv *priv = data;
if (on) if (on)
atomic_dec_if_positive(&ps3fb.ext_flip); atomic_dec_if_positive(&priv->ext_flip);
else else
atomic_inc(&ps3fb.ext_flip); atomic_inc(&priv->ext_flip);
} }
EXPORT_SYMBOL_GPL(ps3fb_flip_ctl);
/* /*
* ioctl * ioctl
@ -851,37 +851,9 @@ static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
#ifndef MODULE
static int __init ps3fb_setup(char *options)
{
char *this_opt;
int mode = 0;
if (!options || !*options) static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo,
return 0; /* no options */ struct ps3_system_bus_device *dev)
while ((this_opt = strsep(&options, ",")) != NULL) {
if (!*this_opt)
continue;
if (!strncmp(this_opt, "mode:", 5))
mode = simple_strtoul(this_opt + 5, NULL, 0);
else
mode_option = this_opt;
}
return mode;
}
#endif /* MODULE */
/*
* Initialisation
*/
static void ps3fb_platform_release(struct device *device)
{
/* This is called when the reference count goes to zero. */
}
static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev)
{ {
int error; int error;
@ -897,7 +869,6 @@ static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev)
return -EINVAL; return -EINVAL;
} }
ps3fb.dev = dev;
error = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet, error = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet,
&ps3fb.irq_no); &ps3fb.irq_no);
if (error) { if (error) {
@ -907,7 +878,7 @@ static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev)
} }
error = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, IRQF_DISABLED, error = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, IRQF_DISABLED,
"ps3fb vsync", ps3fb.dev); DEVICE_NAME, dev);
if (error) { if (error) {
printk(KERN_ERR "%s: request_irq failed %d\n", __func__, printk(KERN_ERR "%s: request_irq failed %d\n", __func__,
error); error);
@ -966,16 +937,45 @@ static struct fb_ops ps3fb_ops = {
}; };
static struct fb_fix_screeninfo ps3fb_fix __initdata = { static struct fb_fix_screeninfo ps3fb_fix __initdata = {
.id = "PS3 FB", .id = DEVICE_NAME,
.type = FB_TYPE_PACKED_PIXELS, .type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR, .visual = FB_VISUAL_TRUECOLOR,
.accel = FB_ACCEL_NONE, .accel = FB_ACCEL_NONE,
}; };
static int __init ps3fb_probe(struct platform_device *dev) static int ps3fb_set_sync(void)
{
int status;
#ifdef HEAD_A
status = lv1_gpu_context_attribute(0x0,
L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0);
if (status) {
printk(KERN_ERR "%s: lv1_gpu_context_attribute DISPLAY_SYNC "
"failed: %d\n", __func__, status);
return -1;
}
#endif
#ifdef HEAD_B
status = lv1_gpu_context_attribute(0x0,
L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0);
if (status) {
printk(KERN_ERR "%s: lv1_gpu_context_attribute DISPLAY_MODE "
"failed: %d\n", __func__, status);
return -1;
}
#endif
return 0;
}
static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev)
{ {
struct fb_info *info; struct fb_info *info;
int retval = -ENOMEM; int retval = -ENOMEM;
u32 xres, yres;
u64 ddr_lpar = 0; u64 ddr_lpar = 0;
u64 lpar_dma_control = 0; u64 lpar_dma_control = 0;
u64 lpar_driver_info = 0; u64 lpar_driver_info = 0;
@ -986,6 +986,30 @@ static int __init ps3fb_probe(struct platform_device *dev)
unsigned long offset; unsigned long offset;
struct task_struct *task; struct task_struct *task;
status = ps3_open_hv_device(dev);
if (status) {
printk(KERN_ERR "%s: ps3_open_hv_device failed\n", __func__);
goto err;
}
if (!ps3fb_mode)
ps3fb_mode = ps3av_get_mode();
DPRINTK("ps3av_mode:%d\n", ps3fb_mode);
if (ps3fb_mode > 0 &&
!ps3av_video_mode2res(ps3fb_mode, &xres, &yres)) {
ps3fb.res_index = ps3fb_get_res_table(xres, yres);
DPRINTK("res_index:%d\n", ps3fb.res_index);
} else
ps3fb.res_index = GPU_RES_INDEX;
atomic_set(&ps3fb.f_count, -1); /* fbcon opens ps3fb */
atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */
init_waitqueue_head(&ps3fb.wait_vsync);
ps3fb.num_frames = 1;
ps3fb_set_sync();
/* get gpu context handle */ /* get gpu context handle */
status = lv1_gpu_memory_allocate(DDR_SIZE, 0, 0, 0, 0, status = lv1_gpu_memory_allocate(DDR_SIZE, 0, 0, 0, 0,
&ps3fb.memory_handle, &ddr_lpar); &ps3fb.memory_handle, &ddr_lpar);
@ -1029,7 +1053,7 @@ static int __init ps3fb_probe(struct platform_device *dev)
* leakage into userspace * leakage into userspace
*/ */
memset(ps3fb.xdr_ea, 0, ps3fb_videomemory.size); memset(ps3fb.xdr_ea, 0, ps3fb_videomemory.size);
info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev); info = framebuffer_alloc(sizeof(u32) * 16, &dev->core);
if (!info) if (!info)
goto err_free_irq; goto err_free_irq;
@ -1061,19 +1085,20 @@ static int __init ps3fb_probe(struct platform_device *dev)
if (retval < 0) if (retval < 0)
goto err_fb_dealloc; goto err_fb_dealloc;
platform_set_drvdata(dev, info); dev->core.driver_data = info;
printk(KERN_INFO printk(KERN_INFO
"fb%d: PS3 frame buffer device, using %ld KiB of video memory\n", "fb%d: PS3 frame buffer device, using %ld KiB of video memory\n",
info->node, ps3fb_videomemory.size >> 10); info->node, ps3fb_videomemory.size >> 10);
task = kthread_run(ps3fbd, info, "ps3fbd"); task = kthread_run(ps3fbd, info, DEVICE_NAME);
if (IS_ERR(task)) { if (IS_ERR(task)) {
retval = PTR_ERR(task); retval = PTR_ERR(task);
goto err_unregister_framebuffer; goto err_unregister_framebuffer;
} }
ps3fb.task = task; ps3fb.task = task;
ps3av_register_flip_ctl(ps3fb_flip_ctl, &ps3fb);
return 0; return 0;
@ -1084,7 +1109,7 @@ err_fb_dealloc:
err_framebuffer_release: err_framebuffer_release:
framebuffer_release(info); framebuffer_release(info);
err_free_irq: err_free_irq:
free_irq(ps3fb.irq_no, ps3fb.dev); free_irq(ps3fb.irq_no, dev);
ps3_irq_plug_destroy(ps3fb.irq_no); ps3_irq_plug_destroy(ps3fb.irq_no);
err_iounmap_dinfo: err_iounmap_dinfo:
iounmap((u8 __iomem *)ps3fb.dinfo); iounmap((u8 __iomem *)ps3fb.dinfo);
@ -1096,26 +1121,30 @@ err:
return retval; return retval;
} }
static void ps3fb_shutdown(struct platform_device *dev) static int ps3fb_shutdown(struct ps3_system_bus_device *dev)
{
ps3fb_flip_ctl(0); /* flip off */
ps3fb.dinfo->irq.mask = 0;
free_irq(ps3fb.irq_no, ps3fb.dev);
ps3_irq_plug_destroy(ps3fb.irq_no);
iounmap((u8 __iomem *)ps3fb.dinfo);
}
void ps3fb_cleanup(void)
{ {
int status; int status;
struct fb_info *info = dev->core.driver_data;
DPRINTK(" -> %s:%d\n", __func__, __LINE__);
ps3fb_flip_ctl(0, &ps3fb); /* flip off */
ps3fb.dinfo->irq.mask = 0;
if (info) {
unregister_framebuffer(info);
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
}
ps3av_register_flip_ctl(NULL, NULL);
if (ps3fb.task) { if (ps3fb.task) {
struct task_struct *task = ps3fb.task; struct task_struct *task = ps3fb.task;
ps3fb.task = NULL; ps3fb.task = NULL;
kthread_stop(task); kthread_stop(task);
} }
if (ps3fb.irq_no) { if (ps3fb.irq_no) {
free_irq(ps3fb.irq_no, ps3fb.dev); free_irq(ps3fb.irq_no, dev);
ps3_irq_plug_destroy(ps3fb.irq_no); ps3_irq_plug_destroy(ps3fb.irq_no);
} }
iounmap((u8 __iomem *)ps3fb.dinfo); iounmap((u8 __iomem *)ps3fb.dinfo);
@ -1128,134 +1157,69 @@ void ps3fb_cleanup(void)
if (status) if (status)
DPRINTK("lv1_gpu_memory_free failed: %d\n", status); DPRINTK("lv1_gpu_memory_free failed: %d\n", status);
ps3av_dev_close(); ps3_close_hv_device(dev);
} DPRINTK(" <- %s:%d\n", __func__, __LINE__);
EXPORT_SYMBOL_GPL(ps3fb_cleanup);
static int ps3fb_remove(struct platform_device *dev)
{
struct fb_info *info = platform_get_drvdata(dev);
if (info) {
unregister_framebuffer(info);
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
}
ps3fb_cleanup();
return 0; return 0;
} }
static struct platform_driver ps3fb_driver = { static struct ps3_system_bus_driver ps3fb_driver = {
.probe = ps3fb_probe, .match_id = PS3_MATCH_ID_GRAPHICS,
.remove = ps3fb_remove, .core.name = DEVICE_NAME,
.shutdown = ps3fb_shutdown, .core.owner = THIS_MODULE,
.driver = { .name = "ps3fb" } .probe = ps3fb_probe,
.remove = ps3fb_shutdown,
.shutdown = ps3fb_shutdown,
}; };
static struct platform_device ps3fb_device = { static int __init ps3fb_setup(void)
.name = "ps3fb",
.id = 0,
.dev = { .release = ps3fb_platform_release }
};
int ps3fb_set_sync(void)
{ {
int status; char *options;
#ifdef HEAD_A #ifdef MODULE
status = lv1_gpu_context_attribute(0x0, return 0;
L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0);
if (status) {
printk(KERN_ERR
"%s: lv1_gpu_context_attribute DISPLAY_SYNC failed: %d\n",
__func__, status);
return -1;
}
#endif #endif
#ifdef HEAD_B
status = lv1_gpu_context_attribute(0x0,
L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0);
if (status) { if (fb_get_options(DEVICE_NAME, &options))
printk(KERN_ERR return -ENXIO;
"%s: lv1_gpu_context_attribute DISPLAY_MODE failed: %d\n",
__func__, status); if (!options || !*options)
return -1; return 0;
while (1) {
char *this_opt = strsep(&options, ",");
if (!this_opt)
break;
if (!*this_opt)
continue;
if (!strncmp(this_opt, "mode:", 5))
ps3fb_mode = simple_strtoul(this_opt + 5, NULL, 0);
else
mode_option = this_opt;
} }
#endif
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(ps3fb_set_sync);
static int __init ps3fb_init(void) static int __init ps3fb_init(void)
{ {
int error; if (!ps3fb_videomemory.address || ps3fb_setup())
#ifndef MODULE return -ENXIO;
int mode;
char *option = NULL;
if (fb_get_options("ps3fb", &option)) return ps3_system_bus_driver_register(&ps3fb_driver);
goto err; }
#endif
if (!ps3fb_videomemory.address) static void __exit ps3fb_exit(void)
goto err; {
DPRINTK(" -> %s:%d\n", __func__, __LINE__);
error = ps3av_dev_open(); ps3_system_bus_driver_unregister(&ps3fb_driver);
if (error) { DPRINTK(" <- %s:%d\n", __func__, __LINE__);
printk(KERN_ERR "%s: ps3av_dev_open failed\n", __func__);
goto err;
}
ps3fb_mode = ps3av_get_mode();
DPRINTK("ps3av_mode:%d\n", ps3fb_mode);
#ifndef MODULE
mode = ps3fb_setup(option); /* check boot option */
if (mode)
ps3fb_mode = mode;
#endif
if (ps3fb_mode > 0) {
u32 xres, yres;
ps3av_video_mode2res(ps3fb_mode, &xres, &yres);
ps3fb.res_index = ps3fb_get_res_table(xres, yres);
DPRINTK("res_index:%d\n", ps3fb.res_index);
} else
ps3fb.res_index = GPU_RES_INDEX;
atomic_set(&ps3fb.f_count, -1); /* fbcon opens ps3fb */
atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */
init_waitqueue_head(&ps3fb.wait_vsync);
ps3fb.num_frames = 1;
error = platform_driver_register(&ps3fb_driver);
if (!error) {
error = platform_device_register(&ps3fb_device);
if (error)
platform_driver_unregister(&ps3fb_driver);
}
ps3fb_set_sync();
return error;
err:
return -ENXIO;
} }
module_init(ps3fb_init); module_init(ps3fb_init);
#ifdef MODULE
static void __exit ps3fb_exit(void)
{
platform_device_unregister(&ps3fb_device);
platform_driver_unregister(&ps3fb_driver);
}
module_exit(ps3fb_exit); module_exit(ps3fb_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
#endif /* MODULE */ MODULE_DESCRIPTION("PS3 GPU Frame Buffer Driver");
MODULE_AUTHOR("Sony Computer Entertainment Inc.");
MODULE_ALIAS(PS3_MODULE_ALIAS_GRAPHICS);

View File

@ -41,16 +41,4 @@ struct ps3fb_ioctl_res {
__u32 num_frames; /* num of frame buffers */ __u32 num_frames; /* num of frame buffers */
}; };
#ifdef __KERNEL__
#ifdef CONFIG_FB_PS3
extern void ps3fb_flip_ctl(int on);
extern void ps3fb_cleanup(void);
#else
static inline void ps3fb_flip_ctl(int on) {}
static inline void ps3fb_cleanup(void) {}
#endif
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_PS3FB_H_ */ #endif /* _ASM_POWERPC_PS3FB_H_ */