in fbdev/ps3fb.c [963:1209]
static int ps3fb_probe(struct ps3_system_bus_device *dev)
{
struct fb_info *info;
struct ps3fb_par *par;
int retval;
u64 ddr_lpar = 0;
u64 lpar_dma_control = 0;
u64 lpar_driver_info = 0;
u64 lpar_reports = 0;
u64 lpar_reports_size = 0;
u64 xdr_lpar;
struct gpu_driver_info *dinfo;
void *fb_start;
int status;
struct task_struct *task;
unsigned long max_ps3fb_size;
if (ps3fb_videomemory.size < GPU_CMD_BUF_SIZE) {
dev_err(&dev->core, "%s: Not enough video memory\n", __func__);
return -ENOMEM;
}
retval = ps3_open_hv_device(dev);
if (retval) {
dev_err(&dev->core, "%s: ps3_open_hv_device failed\n",
__func__);
goto err;
}
if (!ps3fb_mode)
ps3fb_mode = ps3av_get_mode();
dev_dbg(&dev->core, "ps3fb_mode: %d\n", ps3fb_mode);
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);
#ifdef HEAD_A
status = lv1_gpu_display_sync(0x0, 0, L1GPU_DISPLAY_SYNC_VSYNC);
if (status) {
dev_err(&dev->core, "%s: lv1_gpu_display_sync failed: %d\n",
__func__, status);
retval = -ENODEV;
goto err_close_device;
}
#endif
#ifdef HEAD_B
status = lv1_gpu_display_sync(0x0, 1, L1GPU_DISPLAY_SYNC_VSYNC);
if (status) {
dev_err(&dev->core, "%s: lv1_gpu_display_sync failed: %d\n",
__func__, status);
retval = -ENODEV;
goto err_close_device;
}
#endif
max_ps3fb_size = ALIGN(GPU_IOIF, 256*1024*1024) - GPU_IOIF;
if (ps3fb_videomemory.size > max_ps3fb_size) {
dev_info(&dev->core, "Limiting ps3fb mem size to %lu bytes\n",
max_ps3fb_size);
ps3fb_videomemory.size = max_ps3fb_size;
}
/* get gpu context handle */
status = lv1_gpu_memory_allocate(ps3fb_videomemory.size, 0, 0, 0, 0,
&ps3fb.memory_handle, &ddr_lpar);
if (status) {
dev_err(&dev->core, "%s: lv1_gpu_memory_allocate failed: %d\n",
__func__, status);
retval = -ENOMEM;
goto err_close_device;
}
dev_dbg(&dev->core, "ddr:lpar:0x%llx\n", ddr_lpar);
status = lv1_gpu_context_allocate(ps3fb.memory_handle, 0,
&ps3fb.context_handle,
&lpar_dma_control, &lpar_driver_info,
&lpar_reports, &lpar_reports_size);
if (status) {
dev_err(&dev->core,
"%s: lv1_gpu_context_allocate failed: %d\n", __func__,
status);
retval = -ENOMEM;
goto err_gpu_memory_free;
}
/* vsync interrupt */
dinfo = (void __force *)ioremap(lpar_driver_info, 128 * 1024);
if (!dinfo) {
dev_err(&dev->core, "%s: ioremap failed\n", __func__);
retval = -ENOMEM;
goto err_gpu_context_free;
}
ps3fb.dinfo = dinfo;
dev_dbg(&dev->core, "version_driver:%x\n", dinfo->version_driver);
dev_dbg(&dev->core, "irq outlet:%x\n", dinfo->irq.irq_outlet);
dev_dbg(&dev->core, "version_gpu: %x memory_size: %x ch: %x "
"core_freq: %d mem_freq:%d\n", dinfo->version_gpu,
dinfo->memory_size, dinfo->hardware_channel,
dinfo->nvcore_frequency/1000000,
dinfo->memory_frequency/1000000);
if (dinfo->version_driver != GPU_DRIVER_INFO_VERSION) {
dev_err(&dev->core, "%s: version_driver err:%x\n", __func__,
dinfo->version_driver);
retval = -EINVAL;
goto err_iounmap_dinfo;
}
retval = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet,
&ps3fb.irq_no);
if (retval) {
dev_err(&dev->core, "%s: ps3_alloc_irq failed %d\n", __func__,
retval);
goto err_iounmap_dinfo;
}
retval = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt,
0, DEVICE_NAME, &dev->core);
if (retval) {
dev_err(&dev->core, "%s: request_irq failed %d\n", __func__,
retval);
goto err_destroy_plug;
}
dinfo->irq.mask = (1 << GPU_INTR_STATUS_VSYNC_1) |
(1 << GPU_INTR_STATUS_FLIP_1);
/* Clear memory to prevent kernel info leakage into userspace */
memset(ps3fb_videomemory.address, 0, ps3fb_videomemory.size);
xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb_videomemory.address));
status = lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF,
xdr_lpar, ps3fb_videomemory.size,
CBE_IOPTE_PP_W | CBE_IOPTE_PP_R |
CBE_IOPTE_M);
if (status) {
dev_err(&dev->core, "%s: lv1_gpu_context_iomap failed: %d\n",
__func__, status);
retval = -ENXIO;
goto err_free_irq;
}
dev_dbg(&dev->core, "video:%p ioif:%lx lpar:%llx size:%lx\n",
ps3fb_videomemory.address, GPU_IOIF, xdr_lpar,
ps3fb_videomemory.size);
status = lv1_gpu_fb_setup(ps3fb.context_handle, xdr_lpar,
GPU_CMD_BUF_SIZE, GPU_IOIF);
if (status) {
dev_err(&dev->core, "%s: lv1_gpu_fb_setup failed: %d\n",
__func__, status);
retval = -ENXIO;
goto err_context_unmap;
}
info = framebuffer_alloc(sizeof(struct ps3fb_par), &dev->core);
if (!info) {
retval = -ENOMEM;
goto err_context_fb_close;
}
par = info->par;
par->mode_id = ~ps3fb_mode; /* != ps3fb_mode, to trigger change */
par->new_mode_id = ps3fb_mode;
par->num_frames = 1;
info->fbops = &ps3fb_ops;
info->fix = ps3fb_fix;
/*
* The GPU command buffer is at the start of video memory
* As we don't use the full command buffer, we can put the actual
* frame buffer at offset GPU_FB_START and save some precious XDR
* memory
*/
fb_start = ps3fb_videomemory.address + GPU_FB_START;
info->screen_base = (char __force __iomem *)fb_start;
info->fix.smem_start = __pa(fb_start);
info->fix.smem_len = ps3fb_videomemory.size - GPU_FB_START;
info->pseudo_palette = par->pseudo_palette;
info->flags = FBINFO_DEFAULT | FBINFO_READS_FAST |
FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
retval = fb_alloc_cmap(&info->cmap, 256, 0);
if (retval < 0)
goto err_framebuffer_release;
if (!fb_find_mode(&info->var, info, mode_option, ps3fb_modedb,
ARRAY_SIZE(ps3fb_modedb),
ps3fb_vmode(par->new_mode_id), 32)) {
retval = -EINVAL;
goto err_fb_dealloc;
}
fb_videomode_to_modelist(ps3fb_modedb, ARRAY_SIZE(ps3fb_modedb),
&info->modelist);
retval = register_framebuffer(info);
if (retval < 0)
goto err_fb_dealloc;
ps3_system_bus_set_drvdata(dev, info);
dev_info(info->device, "%s %s, using %u KiB of video memory\n",
dev_driver_string(info->dev), dev_name(info->dev),
info->fix.smem_len >> 10);
task = kthread_run(ps3fbd, info, DEVICE_NAME);
if (IS_ERR(task)) {
retval = PTR_ERR(task);
goto err_unregister_framebuffer;
}
ps3fb.task = task;
return 0;
err_unregister_framebuffer:
unregister_framebuffer(info);
err_fb_dealloc:
fb_dealloc_cmap(&info->cmap);
err_framebuffer_release:
framebuffer_release(info);
err_context_fb_close:
lv1_gpu_fb_close(ps3fb.context_handle);
err_context_unmap:
lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, xdr_lpar,
ps3fb_videomemory.size, CBE_IOPTE_M);
err_free_irq:
free_irq(ps3fb.irq_no, &dev->core);
err_destroy_plug:
ps3_irq_plug_destroy(ps3fb.irq_no);
err_iounmap_dinfo:
iounmap((u8 __force __iomem *)ps3fb.dinfo);
err_gpu_context_free:
lv1_gpu_context_free(ps3fb.context_handle);
err_gpu_memory_free:
lv1_gpu_memory_free(ps3fb.memory_handle);
err_close_device:
ps3_close_hv_device(dev);
err:
return retval;
}