aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2011-12-12 10:36:13 -0500
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2012-03-12 17:41:11 -0400
commit481100506b34d666243832c3f2aee905c03cb8e7 (patch)
treecee1b897a4c87c5c211d75d70ecb73095001576b /drivers
parent974d250be2c70c7bf899275b23b241685d4ed7f8 (diff)
fbdev: sh_mobile_meram: Allocate ICBs automatically
Instead of manually specifying the ICBs to use in platform data, allocate them automatically at runtime. The range of reserved ICBs (for instance to be used through UIO), if any, is passed in the platform data reserved_icbs field as a bitmask. The MERAM registration function now returns a pointer to an opaque MERAM object, which is passed to the update and unregistration functions. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c27
-rw-r--r--drivers/video/sh_mobile_lcdcfb.h2
-rw-r--r--drivers/video/sh_mobile_meram.c345
3 files changed, 199 insertions, 175 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index adc911f20033..d0c902699222 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -840,6 +840,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
840 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 840 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
841 struct sh_mobile_meram_cfg *cfg; 841 struct sh_mobile_meram_cfg *cfg;
842 int pixelformat; 842 int pixelformat;
843 void *meram;
843 844
844 ch = &priv->ch[k]; 845 ch = &priv->ch[k];
845 if (!ch->enabled) 846 if (!ch->enabled)
@@ -856,9 +857,9 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
856 /* we need to de-init configured ICBs before we can 857 /* we need to de-init configured ICBs before we can
857 * re-initialize them. 858 * re-initialize them.
858 */ 859 */
859 if (ch->meram_enabled) { 860 if (ch->meram) {
860 mdev->ops->meram_unregister(mdev, cfg); 861 mdev->ops->meram_unregister(mdev, ch->meram);
861 ch->meram_enabled = 0; 862 ch->meram = NULL;
862 } 863 }
863 864
864 switch (ch->format->fourcc) { 865 switch (ch->format->fourcc) {
@@ -880,13 +881,13 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
880 break; 881 break;
881 } 882 }
882 883
883 ret = mdev->ops->meram_register(mdev, cfg, ch->pitch, 884 meram = mdev->ops->meram_register(mdev, cfg, ch->pitch,
884 ch->yres, pixelformat, 885 ch->yres, pixelformat,
885 ch->base_addr_y, ch->base_addr_c, 886 ch->base_addr_y, ch->base_addr_c,
886 &ch->base_addr_y, &ch->base_addr_c, 887 &ch->base_addr_y, &ch->base_addr_c,
887 &ch->pitch); 888 &ch->pitch);
888 if (!ret) 889 if (!IS_ERR(meram))
889 ch->meram_enabled = 1; 890 ch->meram = meram;
890 } 891 }
891 892
892 /* Start the LCDC. */ 893 /* Start the LCDC. */
@@ -951,13 +952,11 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
951 sh_mobile_lcdc_display_off(ch); 952 sh_mobile_lcdc_display_off(ch);
952 953
953 /* disable the meram */ 954 /* disable the meram */
954 if (ch->meram_enabled) { 955 if (ch->meram) {
955 struct sh_mobile_meram_cfg *cfg;
956 struct sh_mobile_meram_info *mdev; 956 struct sh_mobile_meram_info *mdev;
957 cfg = ch->cfg.meram_cfg;
958 mdev = priv->meram_dev; 957 mdev = priv->meram_dev;
959 mdev->ops->meram_unregister(mdev, cfg); 958 mdev->ops->meram_unregister(mdev, ch->meram);
960 ch->meram_enabled = 0; 959 ch->meram = 0;
961 } 960 }
962 961
963 } 962 }
@@ -1070,14 +1069,12 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
1070 base_addr_c += var->xoffset; 1069 base_addr_c += var->xoffset;
1071 } 1070 }
1072 1071
1073 if (ch->meram_enabled) { 1072 if (ch->meram) {
1074 struct sh_mobile_meram_cfg *cfg;
1075 struct sh_mobile_meram_info *mdev; 1073 struct sh_mobile_meram_info *mdev;
1076 int ret; 1074 int ret;
1077 1075
1078 cfg = ch->cfg.meram_cfg;
1079 mdev = priv->meram_dev; 1076 mdev = priv->meram_dev;
1080 ret = mdev->ops->meram_update(mdev, cfg, 1077 ret = mdev->ops->meram_update(mdev, ch->meram,
1081 base_addr_y, base_addr_c, 1078 base_addr_y, base_addr_c,
1082 &base_addr_y, &base_addr_c); 1079 &base_addr_y, &base_addr_c);
1083 if (ret) 1080 if (ret)
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h
index 19a4cd74c89a..bf1707cd3657 100644
--- a/drivers/video/sh_mobile_lcdcfb.h
+++ b/drivers/video/sh_mobile_lcdcfb.h
@@ -59,7 +59,7 @@ struct sh_mobile_lcdc_chan {
59 unsigned long *reg_offs; 59 unsigned long *reg_offs;
60 unsigned long ldmt1r_value; 60 unsigned long ldmt1r_value;
61 unsigned long enabled; /* ME and SE in LDCNT2R */ 61 unsigned long enabled; /* ME and SE in LDCNT2R */
62 int meram_enabled; 62 void *meram;
63 63
64 struct mutex open_lock; /* protects the use counter */ 64 struct mutex open_lock; /* protects the use counter */
65 int use_count; 65 int use_count;
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index 92dc9bd5b667..085c49ac99dd 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -10,6 +10,7 @@
10 */ 10 */
11 11
12#include <linux/device.h> 12#include <linux/device.h>
13#include <linux/err.h>
13#include <linux/genalloc.h> 14#include <linux/genalloc.h>
14#include <linux/io.h> 15#include <linux/io.h>
15#include <linux/kernel.h> 16#include <linux/kernel.h>
@@ -106,14 +107,16 @@ static const unsigned long icb_regs[] = {
106/* 107/*
107 * sh_mobile_meram_icb - MERAM ICB information 108 * sh_mobile_meram_icb - MERAM ICB information
108 * @regs: Registers cache 109 * @regs: Registers cache
110 * @index: ICB index
109 * @offset: MERAM block offset 111 * @offset: MERAM block offset
110 * @size: MERAM block size in bytes 112 * @size: MERAM block size in KiB
111 * @cache_unit: Bytes to cache per ICB 113 * @cache_unit: Bytes to cache per ICB
112 * @pixelformat: Video pixel format of the data stored in the ICB 114 * @pixelformat: Video pixel format of the data stored in the ICB
113 * @current_reg: Which of Start Address Register A (0) or B (1) is in use 115 * @current_reg: Which of Start Address Register A (0) or B (1) is in use
114 */ 116 */
115struct sh_mobile_meram_icb { 117struct sh_mobile_meram_icb {
116 unsigned long regs[ICB_REGS_SIZE]; 118 unsigned long regs[ICB_REGS_SIZE];
119 unsigned int index;
117 unsigned long offset; 120 unsigned long offset;
118 unsigned int size; 121 unsigned int size;
119 122
@@ -124,6 +127,16 @@ struct sh_mobile_meram_icb {
124 127
125#define MERAM_ICB_NUM 32 128#define MERAM_ICB_NUM 32
126 129
130struct sh_mobile_meram_fb_plane {
131 struct sh_mobile_meram_icb *marker;
132 struct sh_mobile_meram_icb *cache;
133};
134
135struct sh_mobile_meram_fb_cache {
136 unsigned int nplanes;
137 struct sh_mobile_meram_fb_plane planes[2];
138};
139
127/* 140/*
128 * sh_mobile_meram_priv - MERAM device 141 * sh_mobile_meram_priv - MERAM device
129 * @base: Registers base address 142 * @base: Registers base address
@@ -184,54 +197,46 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
184 * Allocation 197 * Allocation
185 */ 198 */
186 199
187/* Check if there's no overlaps in MERAM allocation. */ 200/* Allocate ICBs and MERAM for a plane. */
188static int meram_check_overlap(struct sh_mobile_meram_priv *priv, 201static int __meram_alloc(struct sh_mobile_meram_priv *priv,
189 const struct sh_mobile_meram_icb_cfg *new) 202 struct sh_mobile_meram_fb_plane *plane,
203 size_t size)
190{ 204{
191 /* valid ICB? */ 205 unsigned long mem;
192 if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f) 206 unsigned long idx;
193 return 1;
194
195 if (test_bit(new->marker_icb, &priv->used_icb) ||
196 test_bit(new->cache_icb, &priv->used_icb))
197 return 1;
198 207
199 return 0; 208 idx = find_first_zero_bit(&priv->used_icb, 28);
200} 209 if (idx == 28)
210 return -ENOMEM;
211 plane->cache = &priv->icbs[idx];
201 212
202/* Allocate memory for the ICBs and mark them as used. */ 213 idx = find_next_zero_bit(&priv->used_icb, 32, 28);
203static int meram_alloc(struct sh_mobile_meram_priv *priv, 214 if (idx == 32)
204 const struct sh_mobile_meram_icb_cfg *new, 215 return -ENOMEM;
205 int pixelformat) 216 plane->marker = &priv->icbs[idx];
206{
207 struct sh_mobile_meram_icb *marker = &priv->icbs[new->marker_icb];
208 unsigned long mem;
209 217
210 mem = gen_pool_alloc(priv->pool, new->meram_size * 1024); 218 mem = gen_pool_alloc(priv->pool, size * 1024);
211 if (mem == 0) 219 if (mem == 0)
212 return -ENOMEM; 220 return -ENOMEM;
213 221
214 __set_bit(new->marker_icb, &priv->used_icb); 222 __set_bit(plane->marker->index, &priv->used_icb);
215 __set_bit(new->cache_icb, &priv->used_icb); 223 __set_bit(plane->cache->index, &priv->used_icb);
216 224
217 marker->offset = mem - priv->meram; 225 plane->marker->offset = mem - priv->meram;
218 marker->size = new->meram_size * 1024; 226 plane->marker->size = size;
219 marker->current_reg = 1;
220 marker->pixelformat = pixelformat;
221 227
222 return 0; 228 return 0;
223} 229}
224 230
225/* Unmark the specified ICB as used. */ 231/* Free ICBs and MERAM for a plane. */
226static void meram_free(struct sh_mobile_meram_priv *priv, 232static void __meram_free(struct sh_mobile_meram_priv *priv,
227 const struct sh_mobile_meram_icb_cfg *icb) 233 struct sh_mobile_meram_fb_plane *plane)
228{ 234{
229 struct sh_mobile_meram_icb *marker = &priv->icbs[icb->marker_icb]; 235 gen_pool_free(priv->pool, priv->meram + plane->marker->offset,
236 plane->marker->size * 1024);
230 237
231 gen_pool_free(priv->pool, priv->meram + marker->offset, marker->size); 238 __clear_bit(plane->marker->index, &priv->used_icb);
232 239 __clear_bit(plane->cache->index, &priv->used_icb);
233 __clear_bit(icb->marker_icb, &priv->used_icb);
234 __clear_bit(icb->cache_icb, &priv->used_icb);
235} 240}
236 241
237/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */ 242/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
@@ -243,42 +248,96 @@ static int is_nvcolor(int cspace)
243 return 0; 248 return 0;
244} 249}
245 250
251/* Allocate memory for the ICBs and mark them as used. */
252static struct sh_mobile_meram_fb_cache *
253meram_alloc(struct sh_mobile_meram_priv *priv,
254 const struct sh_mobile_meram_cfg *cfg,
255 int pixelformat)
256{
257 struct sh_mobile_meram_fb_cache *cache;
258 unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
259 int ret;
260
261 if (cfg->icb[0].meram_size == 0)
262 return ERR_PTR(-EINVAL);
263
264 if (nplanes == 2 && cfg->icb[1].meram_size == 0)
265 return ERR_PTR(-EINVAL);
266
267 cache = kzalloc(sizeof(*cache), GFP_KERNEL);
268 if (cache == NULL)
269 return ERR_PTR(-ENOMEM);
270
271 cache->nplanes = nplanes;
272
273 ret = __meram_alloc(priv, &cache->planes[0], cfg->icb[0].meram_size);
274 if (ret < 0)
275 goto error;
276
277 cache->planes[0].marker->current_reg = 1;
278 cache->planes[0].marker->pixelformat = pixelformat;
279
280 if (cache->nplanes == 1)
281 return cache;
282
283 ret = __meram_alloc(priv, &cache->planes[1], cfg->icb[1].meram_size);
284 if (ret < 0) {
285 __meram_free(priv, &cache->planes[0]);
286 goto error;
287 }
288
289 return cache;
290
291error:
292 kfree(cache);
293 return ERR_PTR(-ENOMEM);
294}
295
296/* Unmark the specified ICB as used. */
297static void meram_free(struct sh_mobile_meram_priv *priv,
298 struct sh_mobile_meram_fb_cache *cache)
299{
300 __meram_free(priv, &cache->planes[0]);
301 if (cache->nplanes == 2)
302 __meram_free(priv, &cache->planes[1]);
303
304 kfree(cache);
305}
306
246/* Set the next address to fetch. */ 307/* Set the next address to fetch. */
247static void meram_set_next_addr(struct sh_mobile_meram_priv *priv, 308static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
248 const struct sh_mobile_meram_cfg *cfg, 309 struct sh_mobile_meram_fb_cache *cache,
249 unsigned long base_addr_y, 310 unsigned long base_addr_y,
250 unsigned long base_addr_c) 311 unsigned long base_addr_c)
251{ 312{
252 struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb]; 313 struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
253 unsigned long target; 314 unsigned long target;
254 315
255 icb->current_reg ^= 1; 316 icb->current_reg ^= 1;
256 target = icb->current_reg ? MExxSARB : MExxSARA; 317 target = icb->current_reg ? MExxSARB : MExxSARA;
257 318
258 /* set the next address to fetch */ 319 /* set the next address to fetch */
259 meram_write_icb(priv->base, cfg->icb[0].cache_icb, target, 320 meram_write_icb(priv->base, cache->planes[0].cache->index, target,
260 base_addr_y); 321 base_addr_y);
261 meram_write_icb(priv->base, cfg->icb[0].marker_icb, target, 322 meram_write_icb(priv->base, cache->planes[0].marker->index, target,
262 base_addr_y + 323 base_addr_y + cache->planes[0].marker->cache_unit);
263 priv->icbs[cfg->icb[0].marker_icb].cache_unit); 324
264 325 if (cache->nplanes == 2) {
265 if (is_nvcolor(icb->pixelformat)) { 326 meram_write_icb(priv->base, cache->planes[1].cache->index,
266 meram_write_icb(priv->base, cfg->icb[1].cache_icb, target, 327 target, base_addr_c);
267 base_addr_c); 328 meram_write_icb(priv->base, cache->planes[1].marker->index,
268 meram_write_icb(priv->base, cfg->icb[1].marker_icb, target, 329 target, base_addr_c +
269 base_addr_c + 330 cache->planes[1].marker->cache_unit);
270 priv->icbs[cfg->icb[1].marker_icb].cache_unit);
271 } 331 }
272} 332}
273 333
274/* Get the next ICB address. */ 334/* Get the next ICB address. */
275static void 335static void
276meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata, 336meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
277 const struct sh_mobile_meram_cfg *cfg, 337 struct sh_mobile_meram_fb_cache *cache,
278 unsigned long *icb_addr_y, unsigned long *icb_addr_c) 338 unsigned long *icb_addr_y, unsigned long *icb_addr_c)
279{ 339{
280 struct sh_mobile_meram_priv *priv = pdata->priv; 340 struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
281 struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb];
282 unsigned long icb_offset; 341 unsigned long icb_offset;
283 342
284 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0) 343 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0)
@@ -286,9 +345,10 @@ meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
286 else 345 else
287 icb_offset = 0xc0000000 | (icb->current_reg << 23); 346 icb_offset = 0xc0000000 | (icb->current_reg << 23);
288 347
289 *icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24); 348 *icb_addr_y = icb_offset | (cache->planes[0].marker->index << 24);
290 if (is_nvcolor(icb->pixelformat)) 349 if (cache->nplanes == 2)
291 *icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24); 350 *icb_addr_c = icb_offset
351 | (cache->planes[1].marker->index << 24);
292} 352}
293 353
294#define MERAM_CALC_BYTECOUNT(x, y) \ 354#define MERAM_CALC_BYTECOUNT(x, y) \
@@ -296,11 +356,11 @@ meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
296 356
297/* Initialize MERAM. */ 357/* Initialize MERAM. */
298static int meram_init(struct sh_mobile_meram_priv *priv, 358static int meram_init(struct sh_mobile_meram_priv *priv,
299 const struct sh_mobile_meram_icb_cfg *icb, 359 struct sh_mobile_meram_fb_plane *plane,
300 unsigned int xres, unsigned int yres, 360 unsigned int xres, unsigned int yres,
301 unsigned int *out_pitch) 361 unsigned int *out_pitch)
302{ 362{
303 struct sh_mobile_meram_icb *marker = &priv->icbs[icb->marker_icb]; 363 struct sh_mobile_meram_icb *marker = plane->marker;
304 unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres); 364 unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
305 unsigned long bnm; 365 unsigned long bnm;
306 unsigned int lcdc_pitch; 366 unsigned int lcdc_pitch;
@@ -319,13 +379,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
319 lcdc_pitch = xpitch = MERAM_LINE_WIDTH; 379 lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
320 line_cnt = total_byte_count >> 11; 380 line_cnt = total_byte_count >> 11;
321 *out_pitch = xres; 381 *out_pitch = xres;
322 save_lines = (icb->meram_size / 16 / MERAM_SEC_LINE); 382 save_lines = plane->marker->size / 16 / MERAM_SEC_LINE;
323 save_lines *= MERAM_SEC_LINE; 383 save_lines *= MERAM_SEC_LINE;
324 } else { 384 } else {
325 xpitch = xres; 385 xpitch = xres;
326 line_cnt = yres; 386 line_cnt = yres;
327 *out_pitch = lcdc_pitch; 387 *out_pitch = lcdc_pitch;
328 save_lines = icb->meram_size / (lcdc_pitch >> 10) / 2; 388 save_lines = plane->marker->size / (lcdc_pitch >> 10) / 2;
329 save_lines &= 0xff; 389 save_lines &= 0xff;
330 } 390 }
331 bnm = (save_lines - 1) << 16; 391 bnm = (save_lines - 1) << 16;
@@ -333,20 +393,20 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
333 /* TODO: we better to check if we have enough MERAM buffer size */ 393 /* TODO: we better to check if we have enough MERAM buffer size */
334 394
335 /* set up ICB */ 395 /* set up ICB */
336 meram_write_icb(priv->base, icb->cache_icb, MExxBSIZE, 396 meram_write_icb(priv->base, plane->cache->index, MExxBSIZE,
337 MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1)); 397 MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
338 meram_write_icb(priv->base, icb->marker_icb, MExxBSIZE, 398 meram_write_icb(priv->base, plane->marker->index, MExxBSIZE,
339 MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1)); 399 MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
340 400
341 meram_write_icb(priv->base, icb->cache_icb, MExxMNCF, bnm); 401 meram_write_icb(priv->base, plane->cache->index, MExxMNCF, bnm);
342 meram_write_icb(priv->base, icb->marker_icb, MExxMNCF, bnm); 402 meram_write_icb(priv->base, plane->marker->index, MExxMNCF, bnm);
343 403
344 meram_write_icb(priv->base, icb->cache_icb, MExxSBSIZE, xpitch); 404 meram_write_icb(priv->base, plane->cache->index, MExxSBSIZE, xpitch);
345 meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch); 405 meram_write_icb(priv->base, plane->marker->index, MExxSBSIZE, xpitch);
346 406
347 /* save a cache unit size */ 407 /* save a cache unit size */
348 priv->icbs[icb->cache_icb].cache_unit = xres * save_lines; 408 plane->cache->cache_unit = xres * save_lines;
349 priv->icbs[icb->marker_icb].cache_unit = xres * save_lines; 409 plane->marker->cache_unit = xres * save_lines;
350 410
351 /* 411 /*
352 * Set MERAM for framebuffer 412 * Set MERAM for framebuffer
@@ -354,13 +414,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
354 * we also chain the cache_icb and the marker_icb. 414 * we also chain the cache_icb and the marker_icb.
355 * we also split the allocated MERAM buffer between two ICBs. 415 * we also split the allocated MERAM buffer between two ICBs.
356 */ 416 */
357 meram_write_icb(priv->base, icb->cache_icb, MExxCTL, 417 meram_write_icb(priv->base, plane->cache->index, MExxCTL,
358 MERAM_MExxCTL_VAL(icb->marker_icb, marker->offset) | 418 MERAM_MExxCTL_VAL(plane->marker->index, marker->offset)
359 MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | 419 | MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
360 MExxCTL_MD_FB); 420 MExxCTL_MD_FB);
361 meram_write_icb(priv->base, icb->marker_icb, MExxCTL, 421 meram_write_icb(priv->base, plane->marker->index, MExxCTL,
362 MERAM_MExxCTL_VAL(icb->cache_icb, marker->offset + 422 MERAM_MExxCTL_VAL(plane->cache->index, marker->offset +
363 icb->meram_size / 2) | 423 plane->marker->size / 2) |
364 MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | 424 MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
365 MExxCTL_MD_FB); 425 MExxCTL_MD_FB);
366 426
@@ -368,45 +428,44 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
368} 428}
369 429
370static void meram_deinit(struct sh_mobile_meram_priv *priv, 430static void meram_deinit(struct sh_mobile_meram_priv *priv,
371 const struct sh_mobile_meram_icb_cfg *icb) 431 struct sh_mobile_meram_fb_plane *plane)
372{ 432{
373 /* disable ICB */ 433 /* disable ICB */
374 meram_write_icb(priv->base, icb->cache_icb, MExxCTL, 434 meram_write_icb(priv->base, plane->cache->index, MExxCTL,
375 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); 435 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
376 meram_write_icb(priv->base, icb->marker_icb, MExxCTL, 436 meram_write_icb(priv->base, plane->marker->index, MExxCTL,
377 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); 437 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
378 438
379 priv->icbs[icb->cache_icb].cache_unit = 0; 439 plane->cache->cache_unit = 0;
380 priv->icbs[icb->marker_icb].cache_unit = 0; 440 plane->marker->cache_unit = 0;
381} 441}
382 442
383/* ----------------------------------------------------------------------------- 443/* -----------------------------------------------------------------------------
384 * Registration/unregistration 444 * Registration/unregistration
385 */ 445 */
386 446
387static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata, 447static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
388 const struct sh_mobile_meram_cfg *cfg, 448 const struct sh_mobile_meram_cfg *cfg,
389 unsigned int xres, unsigned int yres, 449 unsigned int xres, unsigned int yres,
390 unsigned int pixelformat, 450 unsigned int pixelformat,
391 unsigned long base_addr_y, 451 unsigned long base_addr_y,
392 unsigned long base_addr_c, 452 unsigned long base_addr_c,
393 unsigned long *icb_addr_y, 453 unsigned long *icb_addr_y,
394 unsigned long *icb_addr_c, 454 unsigned long *icb_addr_c,
395 unsigned int *pitch) 455 unsigned int *pitch)
396{ 456{
397 struct platform_device *pdev; 457 struct sh_mobile_meram_fb_cache *cache;
398 struct sh_mobile_meram_priv *priv; 458 struct sh_mobile_meram_priv *priv;
459 struct platform_device *pdev;
399 unsigned int out_pitch; 460 unsigned int out_pitch;
400 unsigned int n;
401 int error = 0;
402 461
403 if (!pdata || !pdata->priv || !pdata->pdev || !cfg) 462 if (!pdata || !pdata->priv || !pdata->pdev || !cfg)
404 return -EINVAL; 463 return ERR_PTR(-EINVAL);
405 464
406 if (pixelformat != SH_MOBILE_MERAM_PF_NV && 465 if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
407 pixelformat != SH_MOBILE_MERAM_PF_NV24 && 466 pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
408 pixelformat != SH_MOBILE_MERAM_PF_RGB) 467 pixelformat != SH_MOBILE_MERAM_PF_RGB)
409 return -EINVAL; 468 return ERR_PTR(-EINVAL);
410 469
411 priv = pdata->priv; 470 priv = pdata->priv;
412 pdev = pdata->pdev; 471 pdev = pdata->pdev;
@@ -418,120 +477,82 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
418 /* we can't handle wider than 8192px */ 477 /* we can't handle wider than 8192px */
419 if (xres > 8192) { 478 if (xres > 8192) {
420 dev_err(&pdev->dev, "width exceeding the limit (> 8192)."); 479 dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
421 return -EINVAL; 480 return ERR_PTR(-EINVAL);
422 }
423
424 /* do we have at least one ICB config? */
425 if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) {
426 dev_err(&pdev->dev, "at least one ICB is required.");
427 return -EINVAL;
428 } 481 }
429 482
430 mutex_lock(&priv->lock); 483 mutex_lock(&priv->lock);
431 484
432 /* make sure that there's no overlaps */
433 if (meram_check_overlap(priv, &cfg->icb[0])) {
434 dev_err(&pdev->dev, "conflicting config detected.");
435 error = -EINVAL;
436 goto err;
437 }
438 n = 1;
439
440 /* do the same if we have the second ICB set */
441 if (cfg->icb[1].marker_icb >= 0 && cfg->icb[1].cache_icb >= 0) {
442 if (meram_check_overlap(priv, &cfg->icb[1])) {
443 dev_err(&pdev->dev, "conflicting config detected.");
444 error = -EINVAL;
445 goto err;
446 }
447 n = 2;
448 }
449
450 if (is_nvcolor(pixelformat) && n != 2) {
451 dev_err(&pdev->dev, "requires two ICB sets for planar Y/C.");
452 error = -EINVAL;
453 goto err;
454 }
455
456 /* We now register the ICBs and allocate the MERAM regions. */ 485 /* We now register the ICBs and allocate the MERAM regions. */
457 error = meram_alloc(priv, &cfg->icb[0], pixelformat); 486 cache = meram_alloc(priv, cfg, pixelformat);
458 if (error < 0) 487 if (IS_ERR(cache)) {
488 dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
489 PTR_ERR(cache));
459 goto err; 490 goto err;
460
461 if (is_nvcolor(pixelformat)) {
462 error = meram_alloc(priv, &cfg->icb[1], pixelformat);
463 if (error < 0) {
464 meram_free(priv, &cfg->icb[0]);
465 goto err;
466 }
467 } 491 }
468 492
469 /* initialize MERAM */ 493 /* initialize MERAM */
470 meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch); 494 meram_init(priv, &cache->planes[0], xres, yres, &out_pitch);
471 *pitch = out_pitch; 495 *pitch = out_pitch;
472 if (pixelformat == SH_MOBILE_MERAM_PF_NV) 496 if (pixelformat == SH_MOBILE_MERAM_PF_NV)
473 meram_init(priv, &cfg->icb[1], xres, (yres + 1) / 2, 497 meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2,
474 &out_pitch); 498 &out_pitch);
475 else if (pixelformat == SH_MOBILE_MERAM_PF_NV24) 499 else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
476 meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2, 500 meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2,
477 &out_pitch); 501 &out_pitch);
478 502
479 meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c); 503 meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
480 meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c); 504 meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
481 505
482 dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx", 506 dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx",
483 *icb_addr_y, *icb_addr_c); 507 *icb_addr_y, *icb_addr_c);
484 508
485err: 509err:
486 mutex_unlock(&priv->lock); 510 mutex_unlock(&priv->lock);
487 return error; 511 return cache;
488} 512}
489 513
490static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, 514static int
491 const struct sh_mobile_meram_cfg *cfg) 515sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
492{ 516{
517 struct sh_mobile_meram_fb_cache *cache = data;
493 struct sh_mobile_meram_priv *priv; 518 struct sh_mobile_meram_priv *priv;
494 struct sh_mobile_meram_icb *icb;
495 519
496 if (!pdata || !pdata->priv || !cfg) 520 if (!pdata || !pdata->priv || !data)
497 return -EINVAL; 521 return -EINVAL;
498 522
499 priv = pdata->priv; 523 priv = pdata->priv;
500 icb = &priv->icbs[cfg->icb[0].marker_icb];
501 524
502 mutex_lock(&priv->lock); 525 mutex_lock(&priv->lock);
503 526
504 /* deinit & unmark */ 527 /* deinit & free */
505 if (is_nvcolor(icb->pixelformat)) { 528 meram_deinit(priv, &cache->planes[0]);
506 meram_deinit(priv, &cfg->icb[1]); 529 if (cache->nplanes == 2)
507 meram_free(priv, &cfg->icb[1]); 530 meram_deinit(priv, &cache->planes[1]);
508 } 531
509 meram_deinit(priv, &cfg->icb[0]); 532 meram_free(priv, cache);
510 meram_free(priv, &cfg->icb[0]);
511 533
512 mutex_unlock(&priv->lock); 534 mutex_unlock(&priv->lock);
513 535
514 return 0; 536 return 0;
515} 537}
516 538
517static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, 539static int
518 const struct sh_mobile_meram_cfg *cfg, 540sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
519 unsigned long base_addr_y, 541 unsigned long base_addr_y, unsigned long base_addr_c,
520 unsigned long base_addr_c, 542 unsigned long *icb_addr_y, unsigned long *icb_addr_c)
521 unsigned long *icb_addr_y,
522 unsigned long *icb_addr_c)
523{ 543{
544 struct sh_mobile_meram_fb_cache *cache = data;
524 struct sh_mobile_meram_priv *priv; 545 struct sh_mobile_meram_priv *priv;
525 546
526 if (!pdata || !pdata->priv || !cfg) 547 if (!pdata || !pdata->priv || !data)
527 return -EINVAL; 548 return -EINVAL;
528 549
529 priv = pdata->priv; 550 priv = pdata->priv;
530 551
531 mutex_lock(&priv->lock); 552 mutex_lock(&priv->lock);
532 553
533 meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c); 554 meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
534 meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c); 555 meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
535 556
536 mutex_unlock(&priv->lock); 557 mutex_unlock(&priv->lock);
537 558
@@ -607,6 +628,7 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
607 struct sh_mobile_meram_info *pdata = pdev->dev.platform_data; 628 struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
608 struct resource *regs; 629 struct resource *regs;
609 struct resource *meram; 630 struct resource *meram;
631 unsigned int i;
610 int error; 632 int error;
611 633
612 if (!pdata) { 634 if (!pdata) {
@@ -627,8 +649,13 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
627 return -ENOMEM; 649 return -ENOMEM;
628 } 650 }
629 651
630 /* initialize private data */ 652 /* Initialize private data. */
631 mutex_init(&priv->lock); 653 mutex_init(&priv->lock);
654 priv->used_icb = pdata->reserved_icbs;
655
656 for (i = 0; i < MERAM_ICB_NUM; ++i)
657 priv->icbs[i].index = i;
658
632 pdata->ops = &sh_mobile_meram_ops; 659 pdata->ops = &sh_mobile_meram_ops;
633 pdata->priv = priv; 660 pdata->priv = priv;
634 pdata->pdev = pdev; 661 pdata->pdev = pdev;