Loading drivers/video/sh_mobile_lcdcfb.c +107 −102 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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: Loading Loading @@ -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) { Loading @@ -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; } } Loading Loading @@ -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; } } Loading Loading @@ -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, Loading @@ -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; } Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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); Loading Loading @@ -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) Loading Loading @@ -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 Loading Loading @@ -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) { Loading Loading @@ -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 Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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) Loading drivers/video/sh_mobile_lcdcfb.h +3 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; Loading @@ -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; Loading drivers/video/sh_mobile_meram.c +132 −103 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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) { Loading @@ -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; Loading @@ -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); Loading @@ -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, Loading Loading @@ -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) Loading Loading @@ -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 */ Loading @@ -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) Loading @@ -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)); Loading @@ -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; Loading @@ -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 Loading Loading @@ -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; Loading include/video/sh_mobile_meram.h +51 −20 Original line number Diff line number Diff line Loading @@ -15,7 +15,6 @@ enum { struct sh_mobile_meram_priv; struct sh_mobile_meram_ops; /* * struct sh_mobile_meram_info - MERAM platform data Loading @@ -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; }; Loading @@ -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__ */ Loading
drivers/video/sh_mobile_lcdcfb.c +107 −102 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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: Loading Loading @@ -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) { Loading @@ -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; } } Loading Loading @@ -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; } } Loading Loading @@ -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, Loading @@ -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; } Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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); Loading Loading @@ -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) Loading Loading @@ -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 Loading Loading @@ -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) { Loading Loading @@ -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 Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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) Loading
drivers/video/sh_mobile_lcdcfb.h +3 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; Loading @@ -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; Loading
drivers/video/sh_mobile_meram.c +132 −103 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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) { Loading @@ -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; Loading @@ -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); Loading @@ -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, Loading Loading @@ -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) Loading Loading @@ -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 */ Loading @@ -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) Loading @@ -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)); Loading @@ -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; Loading @@ -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 Loading Loading @@ -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; Loading
include/video/sh_mobile_meram.h +51 −20 Original line number Diff line number Diff line Loading @@ -15,7 +15,6 @@ enum { struct sh_mobile_meram_priv; struct sh_mobile_meram_ops; /* * struct sh_mobile_meram_info - MERAM platform data Loading @@ -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; }; Loading @@ -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__ */