static int ps3fb_probe()

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;
}