Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4c5b1fb8 authored by Florian Tobias Schandinat's avatar Florian Tobias Schandinat
Browse files

Merge branch 'for-next' of git://linuxtv.org/pinchartl/fbdev into fbdev-next

parents a2c81bc1 a4aa25f6
Loading
Loading
Loading
Loading
+107 −102
Original line number Diff line number Diff line
@@ -161,7 +161,7 @@ enum sh_mobile_lcdc_overlay_mode {
 * @dma_handle: Frame buffer DMA address
 * @base_addr_y: Overlay base address (RGB or luma component)
 * @base_addr_c: Overlay base address (chroma component)
 * @pan_offset: Current pan offset in bytes
 * @pan_y_offset: Panning linear offset in bytes (luma component)
 * @format: Current pixelf format
 * @xres: Horizontal visible resolution
 * @xres_virtual: Horizontal total resolution
@@ -191,7 +191,7 @@ struct sh_mobile_lcdc_overlay {
	dma_addr_t dma_handle;
	unsigned long base_addr_y;
	unsigned long base_addr_c;
	unsigned long pan_offset;
	unsigned long pan_y_offset;

	const struct sh_mobile_lcdc_format_info *format;
	unsigned int xres;
@@ -873,8 +873,8 @@ static void sh_mobile_lcdc_overlay_setup(struct sh_mobile_lcdc_overlay *ovl)
	}

	ovl->base_addr_y = ovl->dma_handle;
	ovl->base_addr_c = ovl->base_addr_y + ovl->xres
			   * ovl->yres_virtual;
	ovl->base_addr_c = ovl->dma_handle
			 + ovl->xres_virtual * ovl->yres_virtual;

	switch (ovl->mode) {
	case LCDC_OVERLAY_BLEND:
@@ -1104,27 +1104,25 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
	/* Compute frame buffer base address and pitch for each channel. */
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		int pixelformat;
		void *meram;
		void *cache;

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

		ch->base_addr_y = ch->dma_handle;
		ch->base_addr_c = ch->base_addr_y + ch->xres * ch->yres_virtual;
		ch->base_addr_c = ch->dma_handle
				+ ch->xres_virtual * ch->yres_virtual;
		ch->line_size = ch->pitch;

		/* Enable MERAM if possible. */
		if (mdev == NULL || mdev->ops == NULL ||
		    ch->cfg->meram_cfg == NULL)
		if (mdev == NULL || ch->cfg->meram_cfg == NULL)
			continue;

		/* we need to de-init configured ICBs before we can
		 * re-initialize them.
		 */
		if (ch->meram) {
			mdev->ops->meram_unregister(mdev, ch->meram);
			ch->meram = NULL;
		/* Free the allocated MERAM cache. */
		if (ch->cache) {
			sh_mobile_meram_cache_free(mdev, ch->cache);
			ch->cache = NULL;
		}

		switch (ch->format->fourcc) {
@@ -1146,14 +1144,14 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
			break;
		}

		meram = mdev->ops->meram_register(mdev, ch->cfg->meram_cfg,
		cache = sh_mobile_meram_cache_alloc(mdev, ch->cfg->meram_cfg,
					ch->pitch, ch->yres, pixelformat,
					&ch->line_size);
		if (!IS_ERR(meram)) {
			mdev->ops->meram_update(mdev, meram,
		if (!IS_ERR(cache)) {
			sh_mobile_meram_cache_update(mdev, cache,
					ch->base_addr_y, ch->base_addr_c,
					&ch->base_addr_y, &ch->base_addr_c);
			ch->meram = meram;
			ch->cache = cache;
		}
	}

@@ -1223,12 +1221,10 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)

		sh_mobile_lcdc_display_off(ch);

		/* disable the meram */
		if (ch->meram) {
			struct sh_mobile_meram_info *mdev;
			mdev = priv->meram_dev;
			mdev->ops->meram_unregister(mdev, ch->meram);
			ch->meram = 0;
		/* Free the MERAM cache. */
		if (ch->cache) {
			sh_mobile_meram_cache_free(priv->meram_dev, ch->cache);
			ch->cache = 0;
		}

	}
@@ -1498,7 +1494,7 @@ static const struct fb_fix_screeninfo sh_mobile_lcdc_overlay_fix = {
	.type =		FB_TYPE_PACKED_PIXELS,
	.visual =	FB_VISUAL_TRUECOLOR,
	.accel =	FB_ACCEL_NONE,
	.xpanstep =	0,
	.xpanstep =	1,
	.ypanstep =	1,
	.ywrapstep =	0,
	.capabilities =	FB_CAP_FOURCC,
@@ -1510,44 +1506,44 @@ static int sh_mobile_lcdc_overlay_pan(struct fb_var_screeninfo *var,
	struct sh_mobile_lcdc_overlay *ovl = info->par;
	unsigned long base_addr_y;
	unsigned long base_addr_c;
	unsigned long pan_offset;
	unsigned long y_offset;
	unsigned long c_offset;

	if (!ovl->format->yuv)
		pan_offset = var->yoffset * ovl->pitch
			   + var->xoffset * (ovl->format->bpp / 8);
	else
		pan_offset = var->yoffset * ovl->pitch + var->xoffset;
	if (!ovl->format->yuv) {
		y_offset = (var->yoffset * ovl->xres_virtual + var->xoffset)
			 * ovl->format->bpp / 8;
		c_offset = 0;
	} else {
		unsigned int xsub = ovl->format->bpp < 24 ? 2 : 1;
		unsigned int ysub = ovl->format->bpp < 16 ? 2 : 1;

	if (pan_offset == ovl->pan_offset)
		return 0;	/* No change, do nothing */
		y_offset = var->yoffset * ovl->xres_virtual + var->xoffset;
		c_offset = var->yoffset / ysub * ovl->xres_virtual * 2 / xsub
			 + var->xoffset * 2 / xsub;
	}

	/* Set the source address for the next refresh */
	base_addr_y = ovl->dma_handle + pan_offset;
	/* If the Y offset hasn't changed, the C offset hasn't either. There's
	 * nothing to do in that case.
	 */
	if (y_offset == ovl->pan_y_offset)
		return 0;

	ovl->base_addr_y = base_addr_y;
	ovl->base_addr_c = base_addr_y;

	if (ovl->format->yuv) {
		/* Set Y offset */
		c_offset = var->yoffset * ovl->pitch
			 * (ovl->format->bpp - 8) / 8;
		base_addr_c = ovl->dma_handle
			    + ovl->xres * ovl->yres_virtual
	/* Set the source address for the next refresh */
	base_addr_y = ovl->dma_handle + y_offset;
	base_addr_c = ovl->dma_handle + ovl->xres_virtual * ovl->yres_virtual
		    + c_offset;
		/* Set X offset */
		if (ovl->format->fourcc == V4L2_PIX_FMT_NV24)
			base_addr_c += 2 * var->xoffset;
		else
			base_addr_c += var->xoffset;

	ovl->base_addr_y = base_addr_y;
	ovl->base_addr_c = base_addr_c;
	}
	ovl->pan_y_offset = y_offset;

	lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index));

	lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y);
	lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c);

	ovl->pan_offset = pan_offset;
	lcdc_write(ovl->channel->lcdc, LDBCR,
		   LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index));

	return 0;
}
@@ -1585,9 +1581,9 @@ static int sh_mobile_lcdc_overlay_set_par(struct fb_info *info)
	ovl->yres_virtual = info->var.yres_virtual;

	if (ovl->format->yuv)
		ovl->pitch = info->var.xres;
		ovl->pitch = info->var.xres_virtual;
	else
		ovl->pitch = info->var.xres * ovl->format->bpp / 8;
		ovl->pitch = info->var.xres_virtual * ovl->format->bpp / 8;

	sh_mobile_lcdc_overlay_setup(ovl);

@@ -1719,9 +1715,14 @@ sh_mobile_lcdc_overlay_fb_init(struct sh_mobile_lcdc_overlay *ovl)
	else
		info->fix.visual = FB_VISUAL_TRUECOLOR;

	if (ovl->format->fourcc == V4L2_PIX_FMT_NV12 ||
	    ovl->format->fourcc == V4L2_PIX_FMT_NV21)
	switch (ovl->format->fourcc) {
	case V4L2_PIX_FMT_NV16:
	case V4L2_PIX_FMT_NV61:
		info->fix.ypanstep = 2;
	case V4L2_PIX_FMT_NV12:
	case V4L2_PIX_FMT_NV21:
		info->fix.xpanstep = 2;
	}

	/* Initialize variable screen information. */
	var = &info->var;
@@ -1776,7 +1777,7 @@ static const struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
	.type =		FB_TYPE_PACKED_PIXELS,
	.visual =	FB_VISUAL_TRUECOLOR,
	.accel =	FB_ACCEL_NONE,
	.xpanstep =	0,
	.xpanstep =	1,
	.ypanstep =	1,
	.ywrapstep =	0,
	.capabilities =	FB_CAP_FOURCC,
@@ -1809,58 +1810,53 @@ static int sh_mobile_lcdc_pan(struct fb_var_screeninfo *var,
	struct sh_mobile_lcdc_chan *ch = info->par;
	struct sh_mobile_lcdc_priv *priv = ch->lcdc;
	unsigned long ldrcntr;
	unsigned long new_pan_offset;
	unsigned long base_addr_y, base_addr_c;
	unsigned long y_offset;
	unsigned long c_offset;

	if (!ch->format->yuv)
		new_pan_offset = var->yoffset * ch->pitch
			       + var->xoffset * (ch->format->bpp / 8);
	else
		new_pan_offset = var->yoffset * ch->pitch + var->xoffset;
	if (!ch->format->yuv) {
		y_offset = (var->yoffset * ch->xres_virtual + var->xoffset)
			 * ch->format->bpp / 8;
		c_offset = 0;
	} else {
		unsigned int xsub = ch->format->bpp < 24 ? 2 : 1;
		unsigned int ysub = ch->format->bpp < 16 ? 2 : 1;

	if (new_pan_offset == ch->pan_offset)
		return 0;	/* No change, do nothing */
		y_offset = var->yoffset * ch->xres_virtual + var->xoffset;
		c_offset = var->yoffset / ysub * ch->xres_virtual * 2 / xsub
			 + var->xoffset * 2 / xsub;
	}

	ldrcntr = lcdc_read(priv, _LDRCNTR);
	/* If the Y offset hasn't changed, the C offset hasn't either. There's
	 * nothing to do in that case.
	 */
	if (y_offset == ch->pan_y_offset)
		return 0;

	/* Set the source address for the next refresh */
	base_addr_y = ch->dma_handle + new_pan_offset;
	if (ch->format->yuv) {
		/* Set y offset */
		c_offset = var->yoffset * ch->pitch
			 * (ch->format->bpp - 8) / 8;
		base_addr_c = ch->dma_handle + ch->xres * ch->yres_virtual
	base_addr_y = ch->dma_handle + y_offset;
	base_addr_c = ch->dma_handle + ch->xres_virtual * ch->yres_virtual
		    + c_offset;
		/* Set x offset */
		if (ch->format->fourcc == V4L2_PIX_FMT_NV24)
			base_addr_c += 2 * var->xoffset;
		else
			base_addr_c += var->xoffset;
	}

	if (ch->meram) {
		struct sh_mobile_meram_info *mdev;

		mdev = priv->meram_dev;
		mdev->ops->meram_update(mdev, ch->meram,
	if (ch->cache)
		sh_mobile_meram_cache_update(priv->meram_dev, ch->cache,
					     base_addr_y, base_addr_c,
					     &base_addr_y, &base_addr_c);
	}

	ch->base_addr_y = base_addr_y;
	ch->base_addr_c = base_addr_c;
	ch->pan_y_offset = y_offset;

	lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
	if (ch->format->yuv)
		lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);

	ldrcntr = lcdc_read(priv, _LDRCNTR);
	if (lcdc_chan_is_sublcd(ch))
		lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS);
	else
		lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_MRS);

	ch->pan_offset = new_pan_offset;

	sh_mobile_lcdc_deferred_io_touch(info);

@@ -2033,9 +2029,9 @@ static int sh_mobile_lcdc_set_par(struct fb_info *info)
	ch->yres_virtual = info->var.yres_virtual;

	if (ch->format->yuv)
		ch->pitch = info->var.xres;
		ch->pitch = info->var.xres_virtual;
	else
		ch->pitch = info->var.xres * ch->format->bpp / 8;
		ch->pitch = info->var.xres_virtual * ch->format->bpp / 8;

	ret = sh_mobile_lcdc_start(ch->lcdc);
	if (ret < 0)
@@ -2218,19 +2214,24 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
	else
		info->fix.visual = FB_VISUAL_TRUECOLOR;

	if (ch->format->fourcc == V4L2_PIX_FMT_NV12 ||
	    ch->format->fourcc == V4L2_PIX_FMT_NV21)
	switch (ch->format->fourcc) {
	case V4L2_PIX_FMT_NV16:
	case V4L2_PIX_FMT_NV61:
		info->fix.ypanstep = 2;
	case V4L2_PIX_FMT_NV12:
	case V4L2_PIX_FMT_NV21:
		info->fix.xpanstep = 2;
	}

	/* Initialize variable screen information using the first mode as
	 * default. The default Y virtual resolution is twice the panel size to
	 * allow for double-buffering.
	 * default.
	 */
	var = &info->var;
	fb_videomode_to_var(var, mode);
	var->width = ch->cfg->panel_cfg.width;
	var->height = ch->cfg->panel_cfg.height;
	var->yres_virtual = var->yres * 2;
	var->xres_virtual = ch->xres_virtual;
	var->yres_virtual = ch->yres_virtual;
	var->activate = FB_ACTIVATE_NOW;

	/* Use the legacy API by default for RGB formats, and the FOURCC API
@@ -2453,8 +2454,11 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
	}

	for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
		if (priv->ch[i].bl)
			sh_mobile_lcdc_bl_remove(priv->ch[i].bl);
		struct sh_mobile_lcdc_chan *ch = &priv->ch[i];

		if (ch->bl)
			sh_mobile_lcdc_bl_remove(ch->bl);
		mutex_destroy(&ch->open_lock);
	}

	if (priv->dot_clk) {
@@ -2545,9 +2549,9 @@ sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv,
	ovl->yres_virtual = ovl->yres * 2;

	if (!format->yuv)
		ovl->pitch = ovl->xres * format->bpp / 8;
		ovl->pitch = ovl->xres_virtual * format->bpp / 8;
	else
		ovl->pitch = ovl->xres;
		ovl->pitch = ovl->xres_virtual;

	/* Allocate frame buffer memory. */
	ovl->fb_size = ovl->cfg->max_xres * ovl->cfg->max_yres
@@ -2625,7 +2629,9 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
		num_modes = cfg->num_modes;
	}

	/* Use the first mode as default. */
	/* Use the first mode as default. The default Y virtual resolution is
	 * twice the panel size to allow for double-buffering.
	 */
	ch->format = format;
	ch->xres = mode->xres;
	ch->xres_virtual = mode->xres;
@@ -2634,10 +2640,10 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,

	if (!format->yuv) {
		ch->colorspace = V4L2_COLORSPACE_SRGB;
		ch->pitch = ch->xres * format->bpp / 8;
		ch->pitch = ch->xres_virtual * format->bpp / 8;
	} else {
		ch->colorspace = V4L2_COLORSPACE_REC709;
		ch->pitch = ch->xres;
		ch->pitch = ch->xres_virtual;
	}

	ch->display.width = cfg->panel_cfg.width;
@@ -2723,7 +2729,6 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
		}
		init_waitqueue_head(&ch->frame_end_wait);
		init_completion(&ch->vsync_completion);
		ch->pan_offset = 0;

		/* probe the backlight is there is one defined */
		if (ch->cfg->bl_info.max_brightness)
+3 −2
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ struct sh_mobile_lcdc_entity {
/*
 * struct sh_mobile_lcdc_chan - LCDC display channel
 *
 * @pan_y_offset: Panning linear offset in bytes (luma component)
 * @base_addr_y: Frame buffer viewport base address (luma component)
 * @base_addr_c: Frame buffer viewport base address (chroma component)
 * @pitch: Frame buffer line pitch
@@ -59,7 +60,7 @@ struct sh_mobile_lcdc_chan {
	unsigned long *reg_offs;
	unsigned long ldmt1r_value;
	unsigned long enabled; /* ME and SE in LDCNT2R */
	void *meram;
	void *cache;

	struct mutex open_lock;		/* protects the use counter */
	int use_count;
@@ -68,7 +69,7 @@ struct sh_mobile_lcdc_chan {
	unsigned long fb_size;

	dma_addr_t dma_handle;
	unsigned long pan_offset;
	unsigned long pan_y_offset;

	unsigned long frame_end;
	wait_queue_head_t frame_end_wait;
+132 −103
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@

#include <linux/device.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -194,11 +195,26 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
}

/* -----------------------------------------------------------------------------
 * Allocation
 * MERAM allocation and free
 */

static unsigned long meram_alloc(struct sh_mobile_meram_priv *priv, size_t size)
{
	return gen_pool_alloc(priv->pool, size);
}

static void meram_free(struct sh_mobile_meram_priv *priv, unsigned long mem,
		       size_t size)
{
	gen_pool_free(priv->pool, mem, size);
}

/* -----------------------------------------------------------------------------
 * LCDC cache planes allocation, init, cleanup and free
 */

/* Allocate ICBs and MERAM for a plane. */
static int __meram_alloc(struct sh_mobile_meram_priv *priv,
static int meram_plane_alloc(struct sh_mobile_meram_priv *priv,
			     struct sh_mobile_meram_fb_plane *plane,
			     size_t size)
{
@@ -215,7 +231,7 @@ static int __meram_alloc(struct sh_mobile_meram_priv *priv,
		return -ENOMEM;
	plane->marker = &priv->icbs[idx];

	mem = gen_pool_alloc(priv->pool, size * 1024);
	mem = meram_alloc(priv, size * 1024);
	if (mem == 0)
		return -ENOMEM;

@@ -229,10 +245,10 @@ static int __meram_alloc(struct sh_mobile_meram_priv *priv,
}

/* Free ICBs and MERAM for a plane. */
static void __meram_free(struct sh_mobile_meram_priv *priv,
static void meram_plane_free(struct sh_mobile_meram_priv *priv,
			     struct sh_mobile_meram_fb_plane *plane)
{
	gen_pool_free(priv->pool, priv->meram + plane->marker->offset,
	meram_free(priv, priv->meram + plane->marker->offset,
		   plane->marker->size * 1024);

	__clear_bit(plane->marker->index, &priv->used_icb);
@@ -248,62 +264,6 @@ static int is_nvcolor(int cspace)
	return 0;
}

/* Allocate memory for the ICBs and mark them as used. */
static struct sh_mobile_meram_fb_cache *
meram_alloc(struct sh_mobile_meram_priv *priv,
	    const struct sh_mobile_meram_cfg *cfg,
	    int pixelformat)
{
	struct sh_mobile_meram_fb_cache *cache;
	unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
	int ret;

	if (cfg->icb[0].meram_size == 0)
		return ERR_PTR(-EINVAL);

	if (nplanes == 2 && cfg->icb[1].meram_size == 0)
		return ERR_PTR(-EINVAL);

	cache = kzalloc(sizeof(*cache), GFP_KERNEL);
	if (cache == NULL)
		return ERR_PTR(-ENOMEM);

	cache->nplanes = nplanes;

	ret = __meram_alloc(priv, &cache->planes[0], cfg->icb[0].meram_size);
	if (ret < 0)
		goto error;

	cache->planes[0].marker->current_reg = 1;
	cache->planes[0].marker->pixelformat = pixelformat;

	if (cache->nplanes == 1)
		return cache;

	ret = __meram_alloc(priv, &cache->planes[1], cfg->icb[1].meram_size);
	if (ret < 0) {
		__meram_free(priv, &cache->planes[0]);
		goto error;
	}

	return cache;

error:
	kfree(cache);
	return ERR_PTR(-ENOMEM);
}

/* Unmark the specified ICB as used. */
static void meram_free(struct sh_mobile_meram_priv *priv,
		       struct sh_mobile_meram_fb_cache *cache)
{
	__meram_free(priv, &cache->planes[0]);
	if (cache->nplanes == 2)
		__meram_free(priv, &cache->planes[1]);

	kfree(cache);
}

/* Set the next address to fetch. */
static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
				struct sh_mobile_meram_fb_cache *cache,
@@ -355,7 +315,7 @@ meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
	(((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))

/* Initialize MERAM. */
static int meram_init(struct sh_mobile_meram_priv *priv,
static int meram_plane_init(struct sh_mobile_meram_priv *priv,
			    struct sh_mobile_meram_fb_plane *plane,
			    unsigned int xres, unsigned int yres,
			    unsigned int *out_pitch)
@@ -427,7 +387,7 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
	return 0;
}

static void meram_deinit(struct sh_mobile_meram_priv *priv,
static void meram_plane_cleanup(struct sh_mobile_meram_priv *priv,
				struct sh_mobile_meram_fb_plane *plane)
{
	/* disable ICB */
@@ -441,20 +401,82 @@ static void meram_deinit(struct sh_mobile_meram_priv *priv,
}

/* -----------------------------------------------------------------------------
 * Registration/unregistration
 * MERAM operations
 */

static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
unsigned long sh_mobile_meram_alloc(struct sh_mobile_meram_info *pdata,
				    size_t size)
{
	struct sh_mobile_meram_priv *priv = pdata->priv;

	return meram_alloc(priv, size);
}
EXPORT_SYMBOL_GPL(sh_mobile_meram_alloc);

void sh_mobile_meram_free(struct sh_mobile_meram_info *pdata, unsigned long mem,
			  size_t size)
{
	struct sh_mobile_meram_priv *priv = pdata->priv;

	meram_free(priv, mem, size);
}
EXPORT_SYMBOL_GPL(sh_mobile_meram_free);

/* Allocate memory for the ICBs and mark them as used. */
static struct sh_mobile_meram_fb_cache *
meram_cache_alloc(struct sh_mobile_meram_priv *priv,
		  const struct sh_mobile_meram_cfg *cfg,
		  int pixelformat)
{
	unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
	struct sh_mobile_meram_fb_cache *cache;
	int ret;

	cache = kzalloc(sizeof(*cache), GFP_KERNEL);
	if (cache == NULL)
		return ERR_PTR(-ENOMEM);

	cache->nplanes = nplanes;

	ret = meram_plane_alloc(priv, &cache->planes[0],
				cfg->icb[0].meram_size);
	if (ret < 0)
		goto error;

	cache->planes[0].marker->current_reg = 1;
	cache->planes[0].marker->pixelformat = pixelformat;

	if (cache->nplanes == 1)
		return cache;

	ret = meram_plane_alloc(priv, &cache->planes[1],
				cfg->icb[1].meram_size);
	if (ret < 0) {
		meram_plane_free(priv, &cache->planes[0]);
		goto error;
	}

	return cache;

error:
	kfree(cache);
	return ERR_PTR(-ENOMEM);
}

void *sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *pdata,
				  const struct sh_mobile_meram_cfg *cfg,
				  unsigned int xres, unsigned int yres,
				      unsigned int pixelformat,
				      unsigned int *pitch)
				  unsigned int pixelformat, unsigned int *pitch)
{
	struct sh_mobile_meram_fb_cache *cache;
	struct sh_mobile_meram_priv *priv = pdata->priv;
	struct platform_device *pdev = pdata->pdev;
	unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
	unsigned int out_pitch;

	if (priv == NULL)
		return ERR_PTR(-ENODEV);

	if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
	    pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
	    pixelformat != SH_MOBILE_MERAM_PF_RGB)
@@ -469,10 +491,16 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
		return ERR_PTR(-EINVAL);
	}

	if (cfg->icb[0].meram_size == 0)
		return ERR_PTR(-EINVAL);

	if (nplanes == 2 && cfg->icb[1].meram_size == 0)
		return ERR_PTR(-EINVAL);

	mutex_lock(&priv->lock);

	/* We now register the ICBs and allocate the MERAM regions. */
	cache = meram_alloc(priv, cfg, pixelformat);
	cache = meram_cache_alloc(priv, cfg, pixelformat);
	if (IS_ERR(cache)) {
		dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
			PTR_ERR(cache));
@@ -480,42 +508,50 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
	}

	/* initialize MERAM */
	meram_init(priv, &cache->planes[0], xres, yres, &out_pitch);
	meram_plane_init(priv, &cache->planes[0], xres, yres, &out_pitch);
	*pitch = out_pitch;
	if (pixelformat == SH_MOBILE_MERAM_PF_NV)
		meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2,
			&out_pitch);
		meram_plane_init(priv, &cache->planes[1],
				 xres, (yres + 1) / 2, &out_pitch);
	else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
		meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2,
			&out_pitch);
		meram_plane_init(priv, &cache->planes[1],
				 2 * xres, (yres + 1) / 2, &out_pitch);

err:
	mutex_unlock(&priv->lock);
	return cache;
}
EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_alloc);

static void
sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
void
sh_mobile_meram_cache_free(struct sh_mobile_meram_info *pdata, void *data)
{
	struct sh_mobile_meram_fb_cache *cache = data;
	struct sh_mobile_meram_priv *priv = pdata->priv;

	mutex_lock(&priv->lock);

	/* deinit & free */
	meram_deinit(priv, &cache->planes[0]);
	if (cache->nplanes == 2)
		meram_deinit(priv, &cache->planes[1]);
	/* Cleanup and free. */
	meram_plane_cleanup(priv, &cache->planes[0]);
	meram_plane_free(priv, &cache->planes[0]);

	meram_free(priv, cache);
	if (cache->nplanes == 2) {
		meram_plane_cleanup(priv, &cache->planes[1]);
		meram_plane_free(priv, &cache->planes[1]);
	}

	kfree(cache);

	mutex_unlock(&priv->lock);
}
EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_free);

static void
sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
		       unsigned long base_addr_y, unsigned long base_addr_c,
		       unsigned long *icb_addr_y, unsigned long *icb_addr_c)
void
sh_mobile_meram_cache_update(struct sh_mobile_meram_info *pdata, void *data,
			     unsigned long base_addr_y,
			     unsigned long base_addr_c,
			     unsigned long *icb_addr_y,
			     unsigned long *icb_addr_c)
{
	struct sh_mobile_meram_fb_cache *cache = data;
	struct sh_mobile_meram_priv *priv = pdata->priv;
@@ -527,13 +563,7 @@ sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,

	mutex_unlock(&priv->lock);
}

static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
	.module			= THIS_MODULE,
	.meram_register		= sh_mobile_meram_register,
	.meram_unregister	= sh_mobile_meram_unregister,
	.meram_update		= sh_mobile_meram_update,
};
EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_update);

/* -----------------------------------------------------------------------------
 * Power management
@@ -624,7 +654,6 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
	for (i = 0; i < MERAM_ICB_NUM; ++i)
		priv->icbs[i].index = i;

	pdata->ops = &sh_mobile_meram_ops;
	pdata->priv = priv;
	pdata->pdev = pdev;

+51 −20
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@ enum {


struct sh_mobile_meram_priv;
struct sh_mobile_meram_ops;

/*
 * struct sh_mobile_meram_info - MERAM platform data
@@ -24,7 +23,6 @@ struct sh_mobile_meram_ops;
struct sh_mobile_meram_info {
	int				addr_mode;
	u32				reserved_icbs;
	struct sh_mobile_meram_ops	*ops;
	struct sh_mobile_meram_priv	*priv;
	struct platform_device		*pdev;
};
@@ -38,26 +36,59 @@ struct sh_mobile_meram_cfg {
	struct sh_mobile_meram_icb_cfg icb[2];
};

struct module;
struct sh_mobile_meram_ops {
	struct module	*module;
	/* register usage of meram */
	void *(*meram_register)(struct sh_mobile_meram_info *meram_dev,
#if defined(CONFIG_FB_SH_MOBILE_MERAM) || \
    defined(CONFIG_FB_SH_MOBILE_MERAM_MODULE)
unsigned long sh_mobile_meram_alloc(struct sh_mobile_meram_info *meram_dev,
				    size_t size);
void sh_mobile_meram_free(struct sh_mobile_meram_info *meram_dev,
			  unsigned long mem, size_t size);
void *sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *dev,
				  const struct sh_mobile_meram_cfg *cfg,
				  unsigned int xres, unsigned int yres,
				  unsigned int pixelformat,
				  unsigned int *pitch);
void sh_mobile_meram_cache_free(struct sh_mobile_meram_info *dev, void *data);
void sh_mobile_meram_cache_update(struct sh_mobile_meram_info *dev, void *data,
				  unsigned long base_addr_y,
				  unsigned long base_addr_c,
				  unsigned long *icb_addr_y,
				  unsigned long *icb_addr_c);
#else
static inline unsigned long
sh_mobile_meram_alloc(struct sh_mobile_meram_info *meram_dev, size_t size)
{
	return 0;
}

	/* unregister usage of meram */
	void (*meram_unregister)(struct sh_mobile_meram_info *meram_dev,
				 void *data);
static inline void
sh_mobile_meram_free(struct sh_mobile_meram_info *meram_dev,
		     unsigned long mem, size_t size)
{
}

	/* update meram settings */
	void (*meram_update)(struct sh_mobile_meram_info *meram_dev, void *data,
static inline void *
sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *dev,
			    const struct sh_mobile_meram_cfg *cfg,
			    unsigned int xres, unsigned int yres,
			    unsigned int pixelformat,
			    unsigned int *pitch)
{
	return ERR_PTR(-ENODEV);
}

static inline void
sh_mobile_meram_cache_free(struct sh_mobile_meram_info *dev, void *data)
{
}

static inline void
sh_mobile_meram_cache_update(struct sh_mobile_meram_info *dev, void *data,
			     unsigned long base_addr_y,
			     unsigned long base_addr_c,
			     unsigned long *icb_addr_y,
			     unsigned long *icb_addr_c);
};
			     unsigned long *icb_addr_c)
{
}
#endif

#endif /* __VIDEO_SH_MOBILE_MERAM_H__  */