Commit 9e6b99bd authored by Geert Uytterhoeven's avatar Geert Uytterhoeven Committed by Paul Mackerras

[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: default avatarGeert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Signed-off-by: default avatarGeoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent 13a5e30c
......@@ -107,7 +107,7 @@ static void ps3_panic(char *str)
while(1);
}
#ifdef CONFIG_FB_PS3
#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE)
static void prealloc(struct ps3_prealloc *p)
{
if (!p->size)
......@@ -129,6 +129,7 @@ struct ps3_prealloc ps3fb_videomemory = {
.size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024,
.align = 1024*1024 /* the GPU requires 1 MiB alignment */
};
EXPORT_SYMBOL_GPL(ps3fb_videomemory);
#define prealloc_ps3fb_videomemory() prealloc(&ps3fb_videomemory)
static int __init early_parse_ps3fb(char *p)
......
......@@ -1790,8 +1790,8 @@ config FB_IBM_GXT4500
adaptor, found on some IBM System P (pSeries) machines.
config FB_PS3
bool "PS3 GPU framebuffer driver"
depends on (FB = y) && PS3_PS3AV
tristate "PS3 GPU framebuffer driver"
depends on FB && PS3_PS3AV
select FB_SYS_FILLRECT
select FB_SYS_COPYAREA
select FB_SYS_IMAGEBLIT
......
......@@ -27,7 +27,6 @@
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/console.h>
#include <linux/ioctl.h>
#include <linux/notifier.h>
......@@ -46,6 +45,9 @@
#include <asm/ps3fb.h>
#include <asm/ps3.h>
#define DEVICE_NAME "ps3fb"
#ifdef PS3FB_DEBUG
#define DPRINTK(fmt, args...) printk("%s: " fmt, __func__ , ##args)
#else
......@@ -126,7 +128,6 @@ struct gpu_driver_info {
struct ps3fb_priv {
unsigned int irq_no;
void *dev;
u64 context_handle, memory_handle;
void *xdr_ea;
......@@ -298,10 +299,9 @@ static const struct fb_videomode ps3fb_modedb[] = {
#define FB_OFF(i) (GPU_OFFSET - VP_OFF(i) % GPU_OFFSET)
static int ps3fb_mode;
module_param(ps3fb_mode, bool, 0);
static char *mode_option __initdata;
module_param(ps3fb_mode, int, 0);
static char *mode_option __devinitdata;
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);
void ps3fb_flip_ctl(int on)
void ps3fb_flip_ctl(int on, void *data)
{
struct ps3fb_priv *priv = data;
if (on)
atomic_dec_if_positive(&ps3fb.ext_flip);
atomic_dec_if_positive(&priv->ext_flip);
else
atomic_inc(&ps3fb.ext_flip);
atomic_inc(&priv->ext_flip);
}
EXPORT_SYMBOL_GPL(ps3fb_flip_ctl);
/*
* ioctl
......@@ -851,37 +851,9 @@ static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr)
return IRQ_HANDLED;
}
#ifndef MODULE
static int __init ps3fb_setup(char *options)
{
char *this_opt;
int mode = 0;
if (!options || !*options)
return 0; /* no options */
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)
static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo,
struct ps3_system_bus_device *dev)
{
int error;
......@@ -897,7 +869,6 @@ static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev)
return -EINVAL;
}
ps3fb.dev = dev;
error = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet,
&ps3fb.irq_no);
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,
"ps3fb vsync", ps3fb.dev);
DEVICE_NAME, dev);
if (error) {
printk(KERN_ERR "%s: request_irq failed %d\n", __func__,
error);
......@@ -966,16 +937,45 @@ static struct fb_ops ps3fb_ops = {
};
static struct fb_fix_screeninfo ps3fb_fix __initdata = {
.id = "PS3 FB",
.id = DEVICE_NAME,
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR,
.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;
int retval = -ENOMEM;
u32 xres, yres;
u64 ddr_lpar = 0;
u64 lpar_dma_control = 0;
u64 lpar_driver_info = 0;
......@@ -986,6 +986,30 @@ static int __init ps3fb_probe(struct platform_device *dev)
unsigned long offset;
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 */
status = lv1_gpu_memory_allocate(DDR_SIZE, 0, 0, 0, 0,
&ps3fb.memory_handle, &ddr_lpar);
......@@ -1029,7 +1053,7 @@ static int __init ps3fb_probe(struct platform_device *dev)
* leakage into userspace
*/
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)
goto err_free_irq;
......@@ -1061,19 +1085,20 @@ static int __init ps3fb_probe(struct platform_device *dev)
if (retval < 0)
goto err_fb_dealloc;
platform_set_drvdata(dev, info);
dev->core.driver_data = info;
printk(KERN_INFO
"fb%d: PS3 frame buffer device, using %ld KiB of video memory\n",
info->node, ps3fb_videomemory.size >> 10);
task = kthread_run(ps3fbd, info, "ps3fbd");
task = kthread_run(ps3fbd, info, DEVICE_NAME);
if (IS_ERR(task)) {
retval = PTR_ERR(task);
goto err_unregister_framebuffer;
}
ps3fb.task = task;
ps3av_register_flip_ctl(ps3fb_flip_ctl, &ps3fb);
return 0;
......@@ -1084,7 +1109,7 @@ err_fb_dealloc:
err_framebuffer_release:
framebuffer_release(info);
err_free_irq:
free_irq(ps3fb.irq_no, ps3fb.dev);
free_irq(ps3fb.irq_no, dev);
ps3_irq_plug_destroy(ps3fb.irq_no);
err_iounmap_dinfo:
iounmap((u8 __iomem *)ps3fb.dinfo);
......@@ -1096,26 +1121,30 @@ err:
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 */
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;
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;
if (info) {
unregister_framebuffer(info);
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
}
ps3av_register_flip_ctl(NULL, NULL);
if (ps3fb.task) {
struct task_struct *task = ps3fb.task;
ps3fb.task = NULL;
kthread_stop(task);
}
if (ps3fb.irq_no) {
free_irq(ps3fb.irq_no, ps3fb.dev);
free_irq(ps3fb.irq_no, dev);
ps3_irq_plug_destroy(ps3fb.irq_no);
}
iounmap((u8 __iomem *)ps3fb.dinfo);
......@@ -1128,134 +1157,69 @@ void ps3fb_cleanup(void)
if (status)
DPRINTK("lv1_gpu_memory_free failed: %d\n", status);
ps3av_dev_close();
}
EXPORT_SYMBOL_GPL(ps3fb_cleanup);
ps3_close_hv_device(dev);
DPRINTK(" <- %s:%d\n", __func__, __LINE__);
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;
}
static struct platform_driver ps3fb_driver = {
static struct ps3_system_bus_driver ps3fb_driver = {
.match_id = PS3_MATCH_ID_GRAPHICS,
.core.name = DEVICE_NAME,
.core.owner = THIS_MODULE,
.probe = ps3fb_probe,
.remove = ps3fb_remove,
.remove = ps3fb_shutdown,
.shutdown = ps3fb_shutdown,
.driver = { .name = "ps3fb" }
};
static struct platform_device ps3fb_device = {
.name = "ps3fb",
.id = 0,
.dev = { .release = ps3fb_platform_release }
};
int ps3fb_set_sync(void)
static int __init ps3fb_setup(void)
{
int status;
char *options;
#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
#ifdef MODULE
return 0;
}
EXPORT_SYMBOL_GPL(ps3fb_set_sync);
static int __init ps3fb_init(void)
{
int error;
#ifndef MODULE
int mode;
char *option = NULL;
if (fb_get_options("ps3fb", &option))
goto err;
#endif
if (!ps3fb_videomemory.address)
goto err;
error = ps3av_dev_open();
if (error) {
printk(KERN_ERR "%s: ps3av_dev_open failed\n", __func__);
goto err;
}
if (fb_get_options(DEVICE_NAME, &options))
return -ENXIO;
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;
if (!options || !*options)
return 0;
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;
while (1) {
char *this_opt = strsep(&options, ",");
error = platform_driver_register(&ps3fb_driver);
if (!error) {
error = platform_device_register(&ps3fb_device);
if (error)
platform_driver_unregister(&ps3fb_driver);
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;
}
return 0;
}
ps3fb_set_sync();
return error;
err:
static int __init ps3fb_init(void)
{
if (!ps3fb_videomemory.address || ps3fb_setup())
return -ENXIO;
}
module_init(ps3fb_init);
return ps3_system_bus_driver_register(&ps3fb_driver);
}
#ifdef MODULE
static void __exit ps3fb_exit(void)
{
platform_device_unregister(&ps3fb_device);
platform_driver_unregister(&ps3fb_driver);
DPRINTK(" -> %s:%d\n", __func__, __LINE__);
ps3_system_bus_driver_unregister(&ps3fb_driver);
DPRINTK(" <- %s:%d\n", __func__, __LINE__);
}
module_init(ps3fb_init);
module_exit(ps3fb_exit);
MODULE_LICENSE("GPL");
#endif /* MODULE */
MODULE_DESCRIPTION("PS3 GPU Frame Buffer Driver");
MODULE_AUTHOR("Sony Computer Entertainment Inc.");
MODULE_ALIAS(PS3_MODULE_ALIAS_GRAPHICS);
......@@ -41,16 +41,4 @@ struct ps3fb_ioctl_res {
__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_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment