Esempio n. 1
0
/*
 * It's common for several clients to have framebuffer open simultaneously.
 * e.g. both fbcon and X. Makes things interesting.
 * Assumes caller is holding info->lock (for open and release at least)
 */
static int udl_fb_open(struct fb_info *info, int user)
{
	struct udl_fbdev *ufbdev = info->par;
	struct drm_device *dev = ufbdev->ufb.base.dev;
	struct udl_device *udl = dev->dev_private;

	/* If the USB device is gone, we don't accept new opens */
	if (drm_device_is_unplugged(udl->ddev))
		return -ENODEV;

	ufbdev->fb_count++;

#ifdef CONFIG_DRM_FBDEV_EMULATION
	if (fb_defio && (info->fbdefio == NULL)) {
		/* enable defio at last moment if not disabled by client */

		struct fb_deferred_io *fbdefio;

		fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL);

		if (fbdefio) {
			fbdefio->delay = DL_DEFIO_WRITE_DELAY;
			fbdefio->deferred_io = drm_fb_helper_deferred_io;
		}

		info->fbdefio = fbdefio;
		fb_deferred_io_init(info);
	}
#endif

	pr_notice("open /dev/fb%d user=%d fb_info=%p count=%d\n",
		  info->node, user, info, ufbdev->fb_count);

	return 0;
}
static int hecubafb_probe(struct platform_device *dev)
{
	struct fb_info *info;
	struct hecuba_board *board;
	int retval = -ENOMEM;
	int videomemorysize;
	unsigned char *videomemory;
	struct hecubafb_par *par;

	/* pick up board specific routines */
	board = dev->dev.platform_data;
	if (!board)
		return -EINVAL;

	/* try to count device specific driver, if can't, platform recalls */
	if (!try_module_get(board->owner))
		return -ENODEV;

	videomemorysize = (DPY_W*DPY_H)/8;

	videomemory = vzalloc(videomemorysize);
	if (!videomemory)
		goto err_videomem_alloc;

	info = framebuffer_alloc(sizeof(struct hecubafb_par), &dev->dev);
	if (!info)
		goto err_fballoc;

	info->screen_base = (char __force __iomem *)videomemory;
	info->fbops = &hecubafb_ops;

	info->var = hecubafb_var;
	info->fix = hecubafb_fix;
	info->fix.smem_len = videomemorysize;
	par = info->par;
	par->info = info;
	par->board = board;
	par->send_command = apollo_send_command;
	par->send_data = apollo_send_data;

	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;

	info->fbdefio = &hecubafb_defio;
	fb_deferred_io_init(info);

	retval = register_framebuffer(info);
	if (retval < 0)
		goto err_fbreg;
	platform_set_drvdata(dev, info);

	printk(KERN_INFO
	       "fb%d: Hecuba frame buffer device, using %dK of video memory\n",
	       info->node, videomemorysize >> 10);

	/* this inits the dpy */
	retval = par->board->init(par);
	if (retval < 0)
		goto err_fbreg;

	return 0;
err_fbreg:
	framebuffer_release(info);
err_fballoc:
	vfree(videomemory);
err_videomem_alloc:
	module_put(board->owner);
	return retval;
}
Esempio n. 3
0
static int qxlfb_create(struct qxl_fbdev *qfbdev,
			struct drm_fb_helper_surface_size *sizes)
{
	struct qxl_device *qdev = qfbdev->qdev;
	struct fb_info *info;
	struct drm_framebuffer *fb = NULL;
	struct drm_mode_fb_cmd2 mode_cmd;
	struct drm_gem_object *gobj = NULL;
	struct qxl_bo *qbo = NULL;
	int ret;
	int size;
	int bpp = sizes->surface_bpp;
	int depth = sizes->surface_depth;
	void *shadow;

	mode_cmd.width = sizes->surface_width;
	mode_cmd.height = sizes->surface_height;

	mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 1) / 8), 64);
	mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth);

	ret = qxlfb_create_pinned_object(qfbdev, &mode_cmd, &gobj);
	if (ret < 0)
		return ret;

	qbo = gem_to_qxl_bo(gobj);
	QXL_INFO(qdev, "%s: %dx%d %d\n", __func__, mode_cmd.width,
		 mode_cmd.height, mode_cmd.pitches[0]);

	shadow = vmalloc(mode_cmd.pitches[0] * mode_cmd.height);
	/* TODO: what's the usual response to memory allocation errors? */
	BUG_ON(!shadow);
	QXL_INFO(qdev,
	"surface0 at gpu offset %lld, mmap_offset %lld (virt %p, shadow %p)\n",
		 qxl_bo_gpu_offset(qbo),
		 qxl_bo_mmap_offset(qbo),
		 qbo->kptr,
		 shadow);
	size = mode_cmd.pitches[0] * mode_cmd.height;

	info = drm_fb_helper_alloc_fbi(&qfbdev->helper);
	if (IS_ERR(info)) {
		ret = PTR_ERR(info);
		goto out_unref;
	}

	info->par = qfbdev;

	qxl_framebuffer_init(qdev->ddev, &qfbdev->qfb, &mode_cmd, gobj,
			     &qxlfb_fb_funcs);

	fb = &qfbdev->qfb.base;

	/* setup helper with fb data */
	qfbdev->helper.fb = fb;

	qfbdev->shadow = shadow;
	strcpy(info->fix.id, "qxldrmfb");

	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);

	info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
	info->fbops = &qxlfb_ops;

	/*
	 * TODO: using gobj->size in various places in this function. Not sure
	 * what the difference between the different sizes is.
	 */
	info->fix.smem_start = qdev->vram_base; /* TODO - correct? */
	info->fix.smem_len = gobj->size;
	info->screen_base = qfbdev->shadow;
	info->screen_size = gobj->size;

	drm_fb_helper_fill_var(info, &qfbdev->helper, sizes->fb_width,
			       sizes->fb_height);

	/* setup aperture base/size for vesafb takeover */
	info->apertures->ranges[0].base = qdev->ddev->mode_config.fb_base;
	info->apertures->ranges[0].size = qdev->vram_size;

	info->fix.mmio_start = 0;
	info->fix.mmio_len = 0;

	if (info->screen_base == NULL) {
		ret = -ENOSPC;
		goto out_destroy_fbi;
	}

	info->fbdefio = &qxl_defio;
	fb_deferred_io_init(info);

	qdev->fbdev_info = info;
	qdev->fbdev_qfb = &qfbdev->qfb;
	DRM_INFO("fb mappable at 0x%lX, size %lu\n",  info->fix.smem_start, (unsigned long)info->screen_size);
	DRM_INFO("fb: depth %d, pitch %d, width %d, height %d\n", fb->depth, fb->pitches[0], fb->width, fb->height);
	return 0;

out_destroy_fbi:
	drm_fb_helper_release_fbi(&qfbdev->helper);
out_unref:
	if (qbo) {
		ret = qxl_bo_reserve(qbo, false);
		if (likely(ret == 0)) {
			qxl_bo_kunmap(qbo);
			qxl_bo_unpin(qbo);
			qxl_bo_unreserve(qbo);
		}
	}
	if (fb && ret) {
		drm_gem_object_unreference_unlocked(gobj);
		drm_framebuffer_cleanup(fb);
		kfree(fb);
	}
	drm_gem_object_unreference_unlocked(gobj);
	return ret;
}
Esempio n. 4
0
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{
	struct sh_mobile_lcdc_chan *ch;
	struct sh_mobile_lcdc_board_cfg	*board_cfg;
	unsigned long tmp;
	int bpp = 0;
	unsigned long ldddsr;
	int k, m, ret;

	/* enable clocks before accessing the hardware */
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		if (priv->ch[k].enabled) {
			sh_mobile_lcdc_clk_on(priv);
			if (!bpp)
				bpp = priv->ch[k].info->var.bits_per_pixel;
		}
	}

	/* reset */
	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
	lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);

	/* enable LCDC channels */
	tmp = lcdc_read(priv, _LDCNT2R);
	tmp |= priv->ch[0].enabled;
	tmp |= priv->ch[1].enabled;
	lcdc_write(priv, _LDCNT2R, tmp);

	/* read data from external memory, avoid using the BEU for now */
	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU);

	/* stop the lcdc first */
	sh_mobile_lcdc_start_stop(priv, 0);

	/* configure clocks */
	tmp = priv->lddckr;
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];

		if (!priv->ch[k].enabled)
			continue;

		m = ch->cfg.clock_divider;
		if (!m)
			continue;

		if (m == 1)
			m = 1 << 6;
		tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);

		/* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider denominator */
		lcdc_write_chan(ch, LDDCKPAT1R, 0);
		lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
	}

	lcdc_write(priv, _LDDCKR, tmp);

	/* start dotclock again */
	lcdc_write(priv, _LDDCKSTPR, 0);
	lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);

	/* interrupts are disabled to begin with */
	lcdc_write(priv, _LDINTR, 0);

	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];

		if (!ch->enabled)
			continue;

		sh_mobile_lcdc_geometry(ch);

		/* power supply */
		lcdc_write_chan(ch, LDPMR, 0);

		board_cfg = &ch->cfg.board_cfg;
		if (board_cfg->setup_sys) {
			ret = board_cfg->setup_sys(board_cfg->board_data,
						ch, &sh_mobile_lcdc_sys_bus_ops);
			if (ret)
				return ret;
		}
	}

	/* word and long word swap */
	ldddsr = lcdc_read(priv, _LDDDSR);
	if  (priv->ch[0].info->var.nonstd)
		lcdc_write(priv, _LDDDSR, ldddsr | 7);
	else {
		switch (bpp) {
		case 16:
			lcdc_write(priv, _LDDDSR, ldddsr | 6);
			break;
		case 24:
			lcdc_write(priv, _LDDDSR, ldddsr | 7);
			break;
		case 32:
			lcdc_write(priv, _LDDDSR, ldddsr | 4);
			break;
		}
	}

	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		unsigned long base_addr_y;
		unsigned long base_addr_c = 0;
		int pitch;
		ch = &priv->ch[k];

		if (!priv->ch[k].enabled)
			continue;

		/* set bpp format in PKF[4:0] */
		tmp = lcdc_read_chan(ch, LDDFR);
		tmp &= ~0x0003031f;
		if (ch->info->var.nonstd) {
			tmp |= (ch->info->var.nonstd << 16);
			switch (ch->info->var.bits_per_pixel) {
			case 12:
				break;
			case 16:
				tmp |= (0x1 << 8);
				break;
			case 24:
				tmp |= (0x2 << 8);
				break;
			}
		} else {
			switch (ch->info->var.bits_per_pixel) {
			case 16:
				tmp |= 0x03;
				break;
			case 24:
				tmp |= 0x0b;
				break;
			case 32:
				break;
			}
		}
		lcdc_write_chan(ch, LDDFR, tmp);

		base_addr_y = ch->info->fix.smem_start;
		base_addr_c = base_addr_y +
				ch->info->var.xres *
				ch->info->var.yres_virtual;
		pitch = ch->info->fix.line_length;

		/* test if we can enable meram */
		if (ch->cfg.meram_cfg && priv->meram_dev &&
				priv->meram_dev->ops) {
			struct sh_mobile_meram_cfg *cfg;
			struct sh_mobile_meram_info *mdev;
			unsigned long icb_addr_y, icb_addr_c;
			int icb_pitch;
			int pf;

			cfg = ch->cfg.meram_cfg;
			mdev = priv->meram_dev;
			/* we need to de-init configured ICBs before we
			 * we can re-initialize them.
			 */
			if (ch->meram_enabled)
				mdev->ops->meram_unregister(mdev, cfg);

			ch->meram_enabled = 0;

			if (ch->info->var.nonstd) {
				if (ch->info->var.bits_per_pixel == 24)
					pf = SH_MOBILE_MERAM_PF_NV24;
				else
					pf = SH_MOBILE_MERAM_PF_NV;
			} else {
				pf = SH_MOBILE_MERAM_PF_RGB;
			}

			ret = mdev->ops->meram_register(mdev, cfg, pitch,
						ch->info->var.yres,
						pf,
						base_addr_y,
						base_addr_c,
						&icb_addr_y,
						&icb_addr_c,
						&icb_pitch);
			if (!ret)  {
				/* set LDSA1R value */
				base_addr_y = icb_addr_y;
				pitch = icb_pitch;

				/* set LDSA2R value if required */
				if (base_addr_c)
					base_addr_c = icb_addr_c;

				ch->meram_enabled = 1;
			}
		}

		/* point out our frame buffer */
		lcdc_write_chan(ch, LDSA1R, base_addr_y);
		if (ch->info->var.nonstd)
			lcdc_write_chan(ch, LDSA2R, base_addr_c);

		/* set line size */
		lcdc_write_chan(ch, LDMLSR, pitch);

		/* setup deferred io if SYS bus */
		tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
		if (ch->ldmt1r_value & (1 << 12) && tmp) {
			ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
			ch->defio.delay = msecs_to_jiffies(tmp);
			ch->info->fbdefio = &ch->defio;
			fb_deferred_io_init(ch->info);

			/* one-shot mode */
			lcdc_write_chan(ch, LDSM1R, 1);

			/* enable "Frame End Interrupt Enable" bit */
			lcdc_write(priv, _LDINTR, LDINTR_FE);

		} else {
			/* continuous read mode */
			lcdc_write_chan(ch, LDSM1R, 0);
		}
	}

	/* display output */
	lcdc_write(priv, _LDCNT1R, LCDC_ENABLE);

	/* start the lcdc */
	sh_mobile_lcdc_start_stop(priv, 1);
	priv->started = 1;

	/* tell the board code to enable the panel */
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];
		if (!ch->enabled)
			continue;

		board_cfg = &ch->cfg.board_cfg;
		if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
			board_cfg->display_on(board_cfg->board_data, ch->info);
			module_put(board_cfg->owner);
		}

		if (ch->bl) {
			ch->bl->props.power = FB_BLANK_UNBLANK;
			backlight_update_status(ch->bl);
		}
	}

	return 0;
}
Esempio n. 5
0
static int ws_eink_spi_probe(struct spi_device *spi)
{
	struct fb_info *info;
	int retval = 0;
	struct waveshare_eink_platform_data *pdata;
	const struct spi_device_id *spi_id;
	const struct waveshare_eink_device_properties *dev_props;
	struct ws_eink_fb_par *par;
	int vmem_size;

	pdata = spi->dev.platform_data;
	if (!pdata) {
		dev_err(&spi->dev, "Required platform data was not provided");
		return -EINVAL;
	}

	spi_id = spi_get_device_id(spi);
	if (!spi_id) {
		dev_err(&spi->dev, "device id not supported!\n");
		return -EINVAL;
	}

	dev_props = (const struct waveshare_eink_device_properties *)
		spi_id->driver_data;
	if (!dev_props) {
		dev_err(&spi->dev, "device definition lacks driver_data\n");
		return -EINVAL;
	}

	info = framebuffer_alloc(sizeof(struct ws_eink_fb_par), &spi->dev);
	if (!info)
		return -ENOMEM;

	vmem_size = dev_props->width * dev_props->height * dev_props->bpp / 8;
	info->screen_base = vzalloc(vmem_size);
	if (!info->screen_base) {
		retval = -ENOMEM;
		goto screen_base_fail;
	}

	info->fbops = &ws_eink_ops;

	WARN_ON(strlcpy(info->fix.id, "waveshare_eink", sizeof(info->fix.id)) >=
		sizeof(info->fix.id));
	info->fix.type		= FB_TYPE_PACKED_PIXELS;
	info->fix.visual	= FB_VISUAL_PSEUDOCOLOR;
	info->fix.smem_len	= vmem_size;
	info->fix.xpanstep	= 0;
	info->fix.ypanstep	= 0;
	info->fix.ywrapstep	= 0;
	info->fix.line_length	= dev_props->width * dev_props->bpp / 8;

	info->var.xres			= dev_props->width;
	info->var.yres			= dev_props->height;
	info->var.xres_virtual		= dev_props->width;
	info->var.yres_virtual		= dev_props->height;
	info->var.bits_per_pixel	= dev_props->bpp;

	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;

	info->fbdefio = &ws_eink_defio;
	fb_deferred_io_init(info);

	par		= info->par;
	par->info	= info;
	par->spi	= spi;
	par->props	= dev_props;
	par->rst	= pdata->rst_gpio;
	par->dc		= pdata->dc_gpio;
	par->busy	= pdata->busy_gpio;
	par->ssbuf	= vzalloc(vmem_size);
	if (!par->ssbuf) {
		retval = -ENOMEM;
		goto ssbuf_alloc_fail;
	}

	retval = register_framebuffer(info);
	if (retval < 0) {
		dev_err(&spi->dev, "framebuffer registration failed");
		goto fbreg_fail;
	}

	spi_set_drvdata(spi, info);

	retval = ws_eink_init_display(par);
	if (retval) {
		dev_err(&spi->dev, "display initialization failed");
		goto disp_init_fail;
	}

	dev_dbg(&spi->dev,
		"fb%d: %s frame buffer device,\n\tusing %d KiB of video memory\n",
		info->node, info->fix.id, vmem_size);

	return 0;

disp_init_fail:
	framebuffer_release(info);
fbreg_fail:
	vfree(par->ssbuf);
ssbuf_alloc_fail:
	vfree(info->screen_base);
screen_base_fail:
	vfree(info->screen_base);
	return retval;
}
Esempio n. 6
0
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{
	struct sh_mobile_lcdc_chan *ch;
	struct sh_mobile_lcdc_board_cfg	*board_cfg;
	unsigned long tmp;
	int bpp = 0;
	int k, m;
	int ret = 0;

	/* enable clocks before accessing the hardware */
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		if (priv->ch[k].enabled) {
			sh_mobile_lcdc_clk_on(priv);
			if (!bpp)
				bpp = priv->ch[k].info->var.bits_per_pixel;
		}
	}

	/* reset */
	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
	lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);

	/* enable LCDC channels */
	tmp = lcdc_read(priv, _LDCNT2R);
	tmp |= priv->ch[0].enabled;
	tmp |= priv->ch[1].enabled;
	lcdc_write(priv, _LDCNT2R, tmp);

	/* read data from external memory, avoid using the BEU for now */
	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU);

	/* stop the lcdc first */
	sh_mobile_lcdc_start_stop(priv, 0);

	/* configure clocks */
	tmp = priv->lddckr;
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];

		if (!priv->ch[k].enabled)
			continue;

		m = ch->cfg.clock_divider;
		if (!m)
			continue;

		if (m == 1)
			m = 1 << 6;
		tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);

		/* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider denominator */
		lcdc_write_chan(ch, LDDCKPAT1R, 0);
		lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
	}

	lcdc_write(priv, _LDDCKR, tmp);

	/* start dotclock again */
	lcdc_write(priv, _LDDCKSTPR, 0);
	lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);

	/* interrupts are disabled to begin with */
	lcdc_write(priv, _LDINTR, 0);

	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];

		if (!ch->enabled)
			continue;

		sh_mobile_lcdc_geometry(ch);

		/* power supply */
		lcdc_write_chan(ch, LDPMR, 0);

		board_cfg = &ch->cfg.board_cfg;
		if (board_cfg->setup_sys)
			ret = board_cfg->setup_sys(board_cfg->board_data, ch,
						   &sh_mobile_lcdc_sys_bus_ops);
		if (ret)
			return ret;
	}

	/* word and long word swap */
	switch (bpp) {
	case 16:
		lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);
		break;
	case 24:
		lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 7);
		break;
	case 32:
		lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 4);
		break;
	}

	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];

		if (!priv->ch[k].enabled)
			continue;

		/* set bpp format in PKF[4:0] */
		tmp = lcdc_read_chan(ch, LDDFR);
		tmp &= ~0x0001001f;
		switch (ch->info->var.bits_per_pixel) {
		case 16:
			tmp |= 0x03;
			break;
		case 24:
			tmp |= 0x0b;
			break;
		case 32:
			break;
		}
		lcdc_write_chan(ch, LDDFR, tmp);

		/* point out our frame buffer */
		lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start);

		/* set line size */
		lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length);

		/* setup deferred io if SYS bus */
		tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
		if (ch->ldmt1r_value & (1 << 12) && tmp) {
			ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
			ch->defio.delay = msecs_to_jiffies(tmp);
			ch->info->fbdefio = &ch->defio;
			fb_deferred_io_init(ch->info);

			/* one-shot mode */
			lcdc_write_chan(ch, LDSM1R, 1);

			/* enable "Frame End Interrupt Enable" bit */
			lcdc_write(priv, _LDINTR, LDINTR_FE);

		} else {
			/* continuous read mode */
			lcdc_write_chan(ch, LDSM1R, 0);
		}
	}

	/* display output */
	lcdc_write(priv, _LDCNT1R, LCDC_ENABLE);

	/* start the lcdc */
	sh_mobile_lcdc_start_stop(priv, 1);
	priv->started = 1;

	/* tell the board code to enable the panel */
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];
		if (!ch->enabled)
			continue;

		board_cfg = &ch->cfg.board_cfg;
		if (try_module_get(board_cfg->owner) && board_cfg->display_on) {
			board_cfg->display_on(board_cfg->board_data, ch->info);
			module_put(board_cfg->owner);
		}
	}

	return 0;
}
Esempio n. 7
0
static int __devinit st7735fb_probe (struct spi_device *spi)
{
	int chip = spi_get_device_id(spi)->driver_data;
	struct st7735fb_platform_data *pdata = spi->dev.platform_data;
	int vmem_size = WIDTH*HEIGHT*BPP/8;
	u8 *vmem;
	struct fb_info *info;
	struct st7735fb_par *par;
	int retval = -ENOMEM;

	if (chip != ST7735_DISPLAY_AF_TFT18) {
		pr_err("%s: only the %s device is supported\n", DRVNAME,
			to_spi_driver(spi->dev.driver)->id_table->name);
		return -EINVAL;
	}

	if (!pdata) {
		pr_err("%s: platform data required for rst and dc info\n",
			DRVNAME);
		return -EINVAL;
	}

	vmem = vzalloc(vmem_size);
	if (!vmem)
		return retval;

	info = framebuffer_alloc(sizeof(struct st7735fb_par), &spi->dev);
	if (!info)
		goto fballoc_fail;

	info->screen_base = (u8 __force __iomem *)vmem;
	info->fbops = &st7735fb_ops;
	info->fix = st7735fb_fix;
	info->fix.smem_len = vmem_size;
	info->var = st7735fb_var;
	/* Choose any packed pixel format as long as it's RGB565 */
	info->var.red.offset = 11;
	info->var.red.length = 5;
	info->var.green.offset = 5;
	info->var.green.length = 6;
	info->var.blue.offset = 0;
	info->var.blue.length = 5;
	info->var.transp.offset = 0;
	info->var.transp.length = 0;
	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
	info->fbdefio = &st7735fb_defio;
	fb_deferred_io_init(info);

	par = info->par;
	par->info = info;
	par->spi = spi;
	par->rst = pdata->rst_gpio;
	par->dc = pdata->dc_gpio;

#ifdef __LITTLE_ENDIAN
	/* Allocate swapped shadow buffer */
	vmem = vzalloc(vmem_size);
	if (!vmem)
		return retval;
	par->ssbuf = vmem;
#endif

	retval = register_framebuffer(info);
	if (retval < 0)
		goto fbreg_fail;

	spi_set_drvdata(spi, info);

	retval = st7735fb_init_display(par);
	if (retval < 0)
		goto init_fail;

	printk(KERN_INFO
		"fb%d: %s frame buffer device,\n\tusing %d KiB of video memory\n",
		info->node, info->fix.id, vmem_size);

	return 0;


	/* TODO: release gpios on fail */
init_fail:
	spi_set_drvdata(spi, NULL);

fbreg_fail:
	framebuffer_release(info);

fballoc_fail:
	vfree(vmem);

	return retval;
}
Esempio n. 8
0
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{
	struct sh_mobile_lcdc_chan *ch;
	struct fb_videomode *lcd_cfg;
	struct sh_mobile_lcdc_board_cfg	*board_cfg;
	unsigned long tmp;
	int k, m;
	int ret = 0;

	
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
		if (priv->ch[k].enabled)
			sh_mobile_lcdc_clk_on(priv);

	
	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
	lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);

	
	tmp = lcdc_read(priv, _LDCNT2R);
	tmp |= priv->ch[0].enabled;
	tmp |= priv->ch[1].enabled;
	lcdc_write(priv, _LDCNT2R, tmp);

	
	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU);

	
	sh_mobile_lcdc_start_stop(priv, 0);

	
	tmp = priv->lddckr;
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];

		if (!priv->ch[k].enabled)
			continue;

		m = ch->cfg.clock_divider;
		if (!m)
			continue;

		if (m == 1)
			m = 1 << 6;
		tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);

		lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000);
		lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
	}

	lcdc_write(priv, _LDDCKR, tmp);

	
	lcdc_write(priv, _LDDCKSTPR, 0);
	lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);

	
	lcdc_write(priv, _LDINTR, 0);

	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];
		lcd_cfg = &ch->cfg.lcd_cfg;

		if (!ch->enabled)
			continue;

		tmp = ch->ldmt1r_value;
		tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;
		tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0;
		lcdc_write_chan(ch, LDMT1R, tmp);

		
		lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
		lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);

		
		tmp = lcd_cfg->xres + lcd_cfg->hsync_len;
		tmp += lcd_cfg->left_margin;
		tmp += lcd_cfg->right_margin;
		tmp /= 8; 
		tmp |= (lcd_cfg->xres / 8) << 16; 
		lcdc_write_chan(ch, LDHCNR, tmp);

		tmp = lcd_cfg->xres;
		tmp += lcd_cfg->right_margin;
		tmp /= 8; 
		tmp |= (lcd_cfg->hsync_len / 8) << 16; 
		lcdc_write_chan(ch, LDHSYNR, tmp);

		
		lcdc_write_chan(ch, LDPMR, 0);

		
		tmp = lcd_cfg->yres + lcd_cfg->vsync_len;
		tmp += lcd_cfg->upper_margin;
		tmp += lcd_cfg->lower_margin; 
		tmp |= lcd_cfg->yres << 16; 
		lcdc_write_chan(ch, LDVLNR, tmp);

		tmp = lcd_cfg->yres;
		tmp += lcd_cfg->lower_margin; 
		tmp |= lcd_cfg->vsync_len << 16; 
		lcdc_write_chan(ch, LDVSYNR, tmp);

		board_cfg = &ch->cfg.board_cfg;
		if (board_cfg->setup_sys)
			ret = board_cfg->setup_sys(board_cfg->board_data, ch,
						   &sh_mobile_lcdc_sys_bus_ops);
		if (ret)
			return ret;
	}

	
	lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);

	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];

		if (!priv->ch[k].enabled)
			continue;

		
		tmp = lcdc_read_chan(ch, LDDFR);
		tmp &= ~(0x0001001f);
		tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0;
		lcdc_write_chan(ch, LDDFR, tmp);

		
		lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start);

		
		lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length);

		
		tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
		if (ch->ldmt1r_value & (1 << 12) && tmp) {
			ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
			ch->defio.delay = msecs_to_jiffies(tmp);
			ch->info->fbdefio = &ch->defio;
			fb_deferred_io_init(ch->info);

			
			lcdc_write_chan(ch, LDSM1R, 1);

			
			lcdc_write(priv, _LDINTR, LDINTR_FE);

		} else {
			
			lcdc_write_chan(ch, LDSM1R, 0);
		}
	}

	
	lcdc_write(priv, _LDCNT1R, LCDC_ENABLE);

	
	sh_mobile_lcdc_start_stop(priv, 1);
	priv->started = 1;

	
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];
		if (!ch->enabled)
			continue;

		board_cfg = &ch->cfg.board_cfg;
		if (board_cfg->display_on)
			board_cfg->display_on(board_cfg->board_data);
	}

	return 0;
}
Esempio n. 9
0
static int __devinit st7585_probe(struct spi_device *spi)
{
	struct st7585_data *drvdata;
	int ret;

	drvdata = devm_kzalloc(&spi->dev, sizeof(*drvdata), GFP_KERNEL);
	if (!drvdata)
		return -ENOMEM;

	drvdata->spi = spi;
	spin_lock_init(&drvdata->lock);
	spi_set_drvdata(spi, drvdata);

	spi->mode = 3;
	spi->bits_per_word = 9;
	spi->max_speed_hz = 10000000;
	ret = spi_setup(spi);

	if (ret)
		return ret;

	spisend(drvdata, 0x21);
	spisend(drvdata, 0x90);
	spisend(drvdata, 0x20);
	spisend(drvdata, 0x0c);
	spisend(drvdata, 0x8e);

	drvdata->info = framebuffer_alloc(0, &spi->dev);
	if (!drvdata->info) {
		return -ENOMEM;
	}

	st7585_fix.smem_start = (unsigned long)drvdata->vram;
 	st7585_fix.smem_len = VRAM_LEN;

	drvdata->info->par = drvdata;
	drvdata->info->flags = FBINFO_DEFAULT;
	drvdata->info->var = st7585_var;
	drvdata->info->fix = st7585_fix;
	drvdata->info->monspecs.hfmin = 0;
	drvdata->info->monspecs.hfmax = 0;
	drvdata->info->monspecs.vfmin = 10000;
	drvdata->info->monspecs.vfmax = 10000;
	drvdata->info->monspecs.dpms = 0;
	drvdata->info->fbops = &st7585fb_ops;
	drvdata->info->screen_base = drvdata->vram;
	drvdata->info->fbdefio = &st7585fb_defio;
	fb_deferred_io_init(drvdata->info);

        ret = register_framebuffer(drvdata->info);
	
	if (ret < 0) {
		framebuffer_release(drvdata->info);
		return -EINVAL;
	}

        printk(KERN_INFO "fb%d: %s frame buffer device\n",
	       drvdata->info->node, drvdata->info->fix.id);
	

	return 0;
}
Esempio n. 10
0
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{
	struct sh_mobile_lcdc_chan *ch;
	struct fb_videomode *lcd_cfg;
	struct sh_mobile_lcdc_board_cfg	*board_cfg;
	unsigned long tmp;
	int k, m;
	int ret = 0;

	/* enable clocks before accessing the hardware */
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
		if (priv->ch[k].enabled)
			sh_mobile_lcdc_clk_on(priv);

	/* reset */
	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
	lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);

	/* enable LCDC channels */
	tmp = lcdc_read(priv, _LDCNT2R);
	tmp |= priv->ch[0].enabled;
	tmp |= priv->ch[1].enabled;
	lcdc_write(priv, _LDCNT2R, tmp);

	/* read data from external memory, avoid using the BEU for now */
	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU);

	/* stop the lcdc first */
	sh_mobile_lcdc_start_stop(priv, 0);

	/* configure clocks */
	tmp = priv->lddckr;
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];

		if (!priv->ch[k].enabled)
			continue;

		m = ch->cfg.clock_divider;
		if (!m)
			continue;

		if (m == 1)
			m = 1 << 6;
		tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);

		lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000);
		lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
	}

	lcdc_write(priv, _LDDCKR, tmp);

	/* start dotclock again */
	lcdc_write(priv, _LDDCKSTPR, 0);
	lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);

	/* interrupts are disabled to begin with */
	lcdc_write(priv, _LDINTR, 0);

	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];
		lcd_cfg = &ch->cfg.lcd_cfg;

		if (!ch->enabled)
			continue;

		tmp = ch->ldmt1r_value;
		tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;
		tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0;
		lcdc_write_chan(ch, LDMT1R, tmp);

		/* setup SYS bus */
		lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
		lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);

		/* horizontal configuration */
		tmp = lcd_cfg->xres + lcd_cfg->hsync_len;
		tmp += lcd_cfg->left_margin;
		tmp += lcd_cfg->right_margin;
		tmp /= 8; /* HTCN */
		tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */
		lcdc_write_chan(ch, LDHCNR, tmp);

		tmp = lcd_cfg->xres;
		tmp += lcd_cfg->right_margin;
		tmp /= 8; /* HSYNP */
		tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */
		lcdc_write_chan(ch, LDHSYNR, tmp);

		/* power supply */
		lcdc_write_chan(ch, LDPMR, 0);

		/* vertical configuration */
		tmp = lcd_cfg->yres + lcd_cfg->vsync_len;
		tmp += lcd_cfg->upper_margin;
		tmp += lcd_cfg->lower_margin; /* VTLN */
		tmp |= lcd_cfg->yres << 16; /* VDLN */
		lcdc_write_chan(ch, LDVLNR, tmp);

		tmp = lcd_cfg->yres;
		tmp += lcd_cfg->lower_margin; /* VSYNP */
		tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */
		lcdc_write_chan(ch, LDVSYNR, tmp);

		board_cfg = &ch->cfg.board_cfg;
		if (board_cfg->setup_sys)
			ret = board_cfg->setup_sys(board_cfg->board_data, ch,
						   &sh_mobile_lcdc_sys_bus_ops);
		if (ret)
			return ret;
	}

	/* word and long word swap */
	lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);

	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];

		if (!priv->ch[k].enabled)
			continue;

		/* set bpp format in PKF[4:0] */
		tmp = lcdc_read_chan(ch, LDDFR);
		tmp &= ~(0x0001001f);
		tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0;
		lcdc_write_chan(ch, LDDFR, tmp);

		/* point out our frame buffer */
		lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start);

		/* set line size */
		lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length);

		/* setup deferred io if SYS bus */
		tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
		if (ch->ldmt1r_value & (1 << 12) && tmp) {
			ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
			ch->defio.delay = msecs_to_jiffies(tmp);
			ch->info->fbdefio = &ch->defio;
			fb_deferred_io_init(ch->info);

			/* one-shot mode */
			lcdc_write_chan(ch, LDSM1R, 1);

			/* enable "Frame End Interrupt Enable" bit */
			lcdc_write(priv, _LDINTR, LDINTR_FE);

		} else {
			/* continuous read mode */
			lcdc_write_chan(ch, LDSM1R, 0);
		}
	}

	/* display output */
	lcdc_write(priv, _LDCNT1R, LCDC_ENABLE);

	/* start the lcdc */
	sh_mobile_lcdc_start_stop(priv, 1);
	priv->started = 1;

	/* tell the board code to enable the panel */
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];
		if (!ch->enabled)
			continue;

		board_cfg = &ch->cfg.board_cfg;
		if (board_cfg->display_on)
			board_cfg->display_on(board_cfg->board_data);
	}

	return 0;
}
Esempio n. 11
0
static int memlcd_spi_probe(struct spi_device *spi)
{
	memlcd_priv	*priv = NULL;
	int ret = -EINVAL;

	/* allocate private data */
	priv = (memlcd_priv*)kzalloc(sizeof(memlcd_priv),GFP_KERNEL);
	if(priv == NULL) {
		dev_err(&spi->dev, "Allocate private data failed!");
		return -ENOMEM;
	}
	dev_set_drvdata(&spi->dev,priv);
	priv->spi = spi;

	/* configure spi */
	spi->bits_per_word = 8;
	spi->max_speed_hz = 2000000;
	ret = spi_setup(spi);
	if (ret < 0) {
		dev_err(&spi->dev, "Configure spi host phase failed!");
		goto free_priv;
	}

	/* allocate framebuffer */
	priv->info = framebuffer_alloc(0, &spi->dev);
	if(priv->info == NULL) {
		dev_err(&spi->dev, "Allocate framebuffer failed!");
		ret = -ENOMEM;
		goto free_priv;
	}

	/* allocate spi data transfer buffer */
	priv->spi_buf = kzalloc(
	    LS027B7DH01_HEIGHT * sizeof(memlcd_line_update) + 2, GFP_KERNEL);
	if(priv->spi_buf == NULL) {
		dev_err(&spi->dev,"Allocate data for spi transfers failed!");
		ret = -ENOMEM;
		goto free_fb;
	}

	/* allocate video memory */
	priv->video_memory = kzalloc(LS027B7DH01_SCREEN_SIZE, GFP_KERNEL);
	if(priv->video_memory == NULL) {
		dev_err(&spi->dev, "Allocate video memory failed!");
		ret = -ENOMEM;
		goto free_spi_data;
	}

	/* configure framebuffer device */
	priv->info->par = priv;
	priv->info->screen_base = (char __iomem *)priv->video_memory;
	priv->info->screen_size = LS027B7DH01_SCREEN_SIZE;
	priv->info->fbops = &ls027b7dh01_ops;
	priv->info->fix = ls027b7dh01_fix;
	priv->info->fix.smem_start = (unsigned long)priv->video_memory;
	priv->info->fix.smem_len = LS027B7DH01_SCREEN_SIZE;
	priv->info->var = ls027b7dh01_var;
	priv->info->fbdefio = &ls027b7dh01_defio;
	priv->info->pseudo_palette = NULL;
	priv->info->flags = FBINFO_FLAG_DEFAULT;

	/* init deferred io structure */
	fb_deferred_io_init(priv->info);

	/* register framebuffer */
	ret = register_framebuffer(priv->info);
	if(ret < 0) {
		dev_err(&spi->dev,"Register framebuffer failed!");
		goto free_fb;
	}
	dev_info(&spi->dev,"Framebuffer device registered successfully!");
	return 0;
free_spi_data:
	kfree(priv->spi_buf);
free_fb:
	kfree(priv->info);
free_priv:
	kfree(priv);
	return ret;
}
Esempio n. 12
0
int ili9341_probe_spi(struct spi_device *spi)
{
	struct device *dev = &spi->dev;
	struct ili9341 *ili;
	struct fb_info *info;
	int ret = 0;

	/* verify we where given some information */
	
	ili = devm_kzalloc(&spi->dev, sizeof(struct ili9341), GFP_KERNEL);
	if (ili == NULL) {
		dev_err(dev, "no memory for device\n");
		return -ENOMEM;
	}
	ili->dev = dev;
	spi->mode = SPI_MODE_0;
	spi_setup(spi);

	spi_set_drvdata(spi, ili);
	dev_info(&spi->dev, "spi registered, item=0x%p\n", (void *)ili);

	ili->gpiodc = 16;
	gpio_request(ili->gpiodc, "ili9341 dc pin");
	gpio_direction_output(ili->gpiodc, 0);
	dev_info(&spi->dev, "gpio %i registered\n", ili->gpiodc);

	ili->gpiorst = 12;
	gpio_request(ili->gpiorst, "ili9341 reset pin");
	gpio_direction_output(ili->gpiorst, 0);
	dev_info(&spi->dev, "gpio %i registered\n", ili->gpiorst);

	ili->spi = spi;
	
	info = framebuffer_alloc(sizeof(struct ili9341), &spi->dev);
	if (!info) {
		ret = -ENOMEM;
		dev_err(&spi->dev,
			"%s: unable to framebuffer_alloc\n", __func__);
		goto out_item;
	}
	info->pseudo_palette = &ili->pseudo_palette;
	ili->info = info;
	info->par = ili;
	info->dev = &spi->dev;
	info->fbops = &ili9341_fbops;
	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
	info->fix = ili9341_fix;
	info->var = ili9341_var;

	ret = ili9341_video_alloc(ili);
	if (ret) {
		dev_err(&spi->dev,
			"%s: unable to ili9341_video_alloc\n", __func__);
		goto out_info;
	}
	info->screen_base = (char __iomem *)ili->info->fix.smem_start;

	ret = ili9341_pages_alloc(ili);
	if (ret < 0) {
		dev_err(&spi->dev,
			"%s: unable to ili9341_pages_init\n", __func__);
		goto out_video;
	}

	info->fbdefio = &ili9341_defio;
	fb_deferred_io_init(info);

	ret = register_framebuffer(info);
	if (ret < 0) {
		dev_err(&spi->dev,
			"%s: unable to register_frambuffer\n", __func__);
		goto out_pages;
	}
	
	ili9341_init_chip(ili);
	ili9341_update_all(ili);

	return ret;

out_pages:
	ili9341_pages_free(ili);
out_video:
	ili9341_video_free(ili);
out_info:
	framebuffer_release(info);
out_item:
	kfree(ili);
	return ret;
}
Esempio n. 13
0
static int __devinit st7586fb_probe (struct spi_device *spi)
{
	int chip = spi_get_device_id(spi)->driver_data;
	struct st7586fb_platform_data *pdata = spi->dev.platform_data;
	int vmem_size = (WIDTH + 2) / 3 * HEIGHT;
	u8 *vmem;
	struct fb_info *info;
	struct st7586fb_par *par;
	int retval = -ENOMEM;

	if (chip != ST7586_DISPLAY_LEGO_EV3) {
		pr_err("%s: only the %s device is supported\n", DRVNAME,
			to_spi_driver(spi->dev.driver)->id_table->name);
		return -EINVAL;
	}

	if (!pdata) {
		pr_err("%s: platform data required for rst and a0 info\n",
			DRVNAME);
		return -EINVAL;
	}

	vmem = (u8 *)kmalloc(vmem_size, GFP_KERNEL);
	if (!vmem)
		return retval;

	// Zero memory for easy detection if any new data has been written to screenbuffer
	memset(vmem, 0, vmem_size);

	info = framebuffer_alloc(sizeof(struct st7586fb_par), &spi->dev);
	if (!info)
		goto fballoc_fail;

	info->screen_base = vmem;
	info->fbops = &st7586fb_ops;
	info->fix = st7586fb_fix;
	info->fix.smem_start = virt_to_phys(vmem);
	info->fix.smem_len = vmem_size;
	info->var = st7586fb_var;
	info->var.red.offset = 0;
	info->var.red.length = 1;
	info->var.green.offset = 0;
	info->var.green.length = 1;
	info->var.blue.offset = 0;
	info->var.blue.length = 1;
	info->var.transp.offset = 0;
	info->var.transp.length = 0;
	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
	info->fbdefio = &st7586fb_defio;
	fb_deferred_io_init(info);

	par = info->par;
	par->info = info;
	par->spi = spi;
	par->rst = pdata->rst_gpio;
	par->a0 = pdata->a0_gpio;
	par->cs = pdata->cs_gpio;
	par->buf = kmalloc(1, GFP_KERNEL);
	INIT_DELAYED_WORK(&par->dwork, st7586fb_deferred_work);

	retval = register_framebuffer(info);
	if (retval < 0)
		goto fbreg_fail;

	spi_set_drvdata(spi, info);

	retval = st7586fb_request_gpios(par);

	// Move initing of display to ioctl call to avoid dispaly flickering
	// as dispaly has already been inited in uboot
	// retval |= st7586fb_init_display(par);
	if (retval < 0)
		goto init_fail;

	printk(KERN_INFO
		"fb%d: %s frame buffer device, using %d.%d KiB of video memory\n",
		info->node, info->fix.id, vmem_size / 1024, vmem_size % 1024 * 10 / 1024);

	return 0;

init_fail:
	spi_set_drvdata(spi, NULL);

fbreg_fail:
	kfree(par->buf);
	framebuffer_release(info);

fballoc_fail:
	kfree(vmem);

	return retval;
}