aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/sh_mobile_meram.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/sh_mobile_meram.c')
-rw-r--r--drivers/video/sh_mobile_meram.c690
1 files changed, 366 insertions, 324 deletions
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index f45d83ecfd21..82ba830bf95d 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -9,16 +9,22 @@
9 * for more details. 9 * for more details.
10 */ 10 */
11 11
12#include <linux/device.h>
13#include <linux/err.h>
14#include <linux/genalloc.h>
15#include <linux/io.h>
12#include <linux/kernel.h> 16#include <linux/kernel.h>
13#include <linux/module.h> 17#include <linux/module.h>
14#include <linux/device.h> 18#include <linux/platform_device.h>
15#include <linux/pm_runtime.h> 19#include <linux/pm_runtime.h>
16#include <linux/io.h>
17#include <linux/slab.h> 20#include <linux/slab.h>
18#include <linux/platform_device.h> 21
19#include <video/sh_mobile_meram.h> 22#include <video/sh_mobile_meram.h>
20 23
21/* meram registers */ 24/* -----------------------------------------------------------------------------
25 * MERAM registers
26 */
27
22#define MEVCR1 0x4 28#define MEVCR1 0x4
23#define MEVCR1_RST (1 << 31) 29#define MEVCR1_RST (1 << 31)
24#define MEVCR1_WD (1 << 30) 30#define MEVCR1_WD (1 << 30)
@@ -81,16 +87,14 @@
81 ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \ 87 ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
82 ((xszm1) << MExxBSIZE_XSZM1_SHIFT)) 88 ((xszm1) << MExxBSIZE_XSZM1_SHIFT))
83 89
84#define SH_MOBILE_MERAM_ICB_NUM 32 90static const unsigned long common_regs[] = {
85
86static unsigned long common_regs[] = {
87 MEVCR1, 91 MEVCR1,
88 MEQSEL1, 92 MEQSEL1,
89 MEQSEL2, 93 MEQSEL2,
90}; 94};
91#define CMN_REGS_SIZE ARRAY_SIZE(common_regs) 95#define MERAM_REGS_SIZE ARRAY_SIZE(common_regs)
92 96
93static unsigned long icb_regs[] = { 97static const unsigned long icb_regs[] = {
94 MExxCTL, 98 MExxCTL,
95 MExxBSIZE, 99 MExxBSIZE,
96 MExxMNCF, 100 MExxMNCF,
@@ -100,216 +104,269 @@ static unsigned long icb_regs[] = {
100}; 104};
101#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs) 105#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
102 106
107/*
108 * sh_mobile_meram_icb - MERAM ICB information
109 * @regs: Registers cache
110 * @index: ICB index
111 * @offset: MERAM block offset
112 * @size: MERAM block size in KiB
113 * @cache_unit: Bytes to cache per ICB
114 * @pixelformat: Video pixel format of the data stored in the ICB
115 * @current_reg: Which of Start Address Register A (0) or B (1) is in use
116 */
117struct sh_mobile_meram_icb {
118 unsigned long regs[ICB_REGS_SIZE];
119 unsigned int index;
120 unsigned long offset;
121 unsigned int size;
122
123 unsigned int cache_unit;
124 unsigned int pixelformat;
125 unsigned int current_reg;
126};
127
128#define MERAM_ICB_NUM 32
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
140/*
141 * sh_mobile_meram_priv - MERAM device
142 * @base: Registers base address
143 * @meram: MERAM physical address
144 * @regs: Registers cache
145 * @lock: Protects used_icb and icbs
146 * @used_icb: Bitmask of used ICBs
147 * @icbs: ICBs
148 * @pool: Allocation pool to manage the MERAM
149 */
103struct sh_mobile_meram_priv { 150struct sh_mobile_meram_priv {
104 void __iomem *base; 151 void __iomem *base;
105 struct mutex lock; 152 unsigned long meram;
106 unsigned long used_icb; 153 unsigned long regs[MERAM_REGS_SIZE];
107 int used_meram_cache_regions; 154
108 unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM]; 155 struct mutex lock;
109 unsigned long cmn_saved_regs[CMN_REGS_SIZE]; 156 unsigned long used_icb;
110 unsigned long icb_saved_regs[ICB_REGS_SIZE * SH_MOBILE_MERAM_ICB_NUM]; 157 struct sh_mobile_meram_icb icbs[MERAM_ICB_NUM];
158
159 struct gen_pool *pool;
111}; 160};
112 161
113/* settings */ 162/* settings */
114#define MERAM_SEC_LINE 15 163#define MERAM_GRANULARITY 1024
115#define MERAM_LINE_WIDTH 2048 164#define MERAM_SEC_LINE 15
165#define MERAM_LINE_WIDTH 2048
116 166
117/* 167/* -----------------------------------------------------------------------------
118 * MERAM/ICB access functions 168 * Registers access
119 */ 169 */
120 170
121#define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20) 171#define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20)
122 172
123static inline void meram_write_icb(void __iomem *base, int idx, int off, 173static inline void meram_write_icb(void __iomem *base, unsigned int idx,
124 unsigned long val) 174 unsigned int off, unsigned long val)
125{ 175{
126 iowrite32(val, MERAM_ICB_OFFSET(base, idx, off)); 176 iowrite32(val, MERAM_ICB_OFFSET(base, idx, off));
127} 177}
128 178
129static inline unsigned long meram_read_icb(void __iomem *base, int idx, int off) 179static inline unsigned long meram_read_icb(void __iomem *base, unsigned int idx,
180 unsigned int off)
130{ 181{
131 return ioread32(MERAM_ICB_OFFSET(base, idx, off)); 182 return ioread32(MERAM_ICB_OFFSET(base, idx, off));
132} 183}
133 184
134static inline void meram_write_reg(void __iomem *base, int off, 185static inline void meram_write_reg(void __iomem *base, unsigned int off,
135 unsigned long val) 186 unsigned long val)
136{ 187{
137 iowrite32(val, base + off); 188 iowrite32(val, base + off);
138} 189}
139 190
140static inline unsigned long meram_read_reg(void __iomem *base, int off) 191static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
141{ 192{
142 return ioread32(base + off); 193 return ioread32(base + off);
143} 194}
144 195
145/* 196/* -----------------------------------------------------------------------------
146 * register ICB 197 * Allocation
147 */
148
149#define MERAM_CACHE_START(p) ((p) >> 16)
150#define MERAM_CACHE_END(p) ((p) & 0xffff)
151#define MERAM_CACHE_SET(o, s) ((((o) & 0xffff) << 16) | \
152 (((o) + (s) - 1) & 0xffff))
153
154/*
155 * check if there's no overlaps in MERAM allocation.
156 */ 198 */
157 199
158static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv, 200/* Allocate ICBs and MERAM for a plane. */
159 struct sh_mobile_meram_icb *new) 201static int __meram_alloc(struct sh_mobile_meram_priv *priv,
202 struct sh_mobile_meram_fb_plane *plane,
203 size_t size)
160{ 204{
161 int i; 205 unsigned long mem;
162 int used_start, used_end, meram_start, meram_end; 206 unsigned long idx;
163 207
164 /* valid ICB? */ 208 idx = find_first_zero_bit(&priv->used_icb, 28);
165 if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f) 209 if (idx == 28)
166 return 1; 210 return -ENOMEM;
211 plane->cache = &priv->icbs[idx];
167 212
168 if (test_bit(new->marker_icb, &priv->used_icb) || 213 idx = find_next_zero_bit(&priv->used_icb, 32, 28);
169 test_bit(new->cache_icb, &priv->used_icb)) 214 if (idx == 32)
170 return 1; 215 return -ENOMEM;
216 plane->marker = &priv->icbs[idx];
171 217
172 for (i = 0; i < priv->used_meram_cache_regions; i++) { 218 mem = gen_pool_alloc(priv->pool, size * 1024);
173 used_start = MERAM_CACHE_START(priv->used_meram_cache[i]); 219 if (mem == 0)
174 used_end = MERAM_CACHE_END(priv->used_meram_cache[i]); 220 return -ENOMEM;
175 meram_start = new->meram_offset;
176 meram_end = new->meram_offset + new->meram_size;
177 221
178 if ((meram_start >= used_start && meram_start < used_end) || 222 __set_bit(plane->marker->index, &priv->used_icb);
179 (meram_end > used_start && meram_end < used_end)) 223 __set_bit(plane->cache->index, &priv->used_icb);
180 return 1; 224
181 } 225 plane->marker->offset = mem - priv->meram;
226 plane->marker->size = size;
182 227
183 return 0; 228 return 0;
184} 229}
185 230
186/* 231/* Free ICBs and MERAM for a plane. */
187 * mark the specified ICB as used 232static void __meram_free(struct sh_mobile_meram_priv *priv,
188 */ 233 struct sh_mobile_meram_fb_plane *plane)
234{
235 gen_pool_free(priv->pool, priv->meram + plane->marker->offset,
236 plane->marker->size * 1024);
189 237
190static inline void meram_mark(struct sh_mobile_meram_priv *priv, 238 __clear_bit(plane->marker->index, &priv->used_icb);
191 struct sh_mobile_meram_icb *new) 239 __clear_bit(plane->cache->index, &priv->used_icb);
240}
241
242/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
243static int is_nvcolor(int cspace)
192{ 244{
193 int n; 245 if (cspace == SH_MOBILE_MERAM_PF_NV ||
246 cspace == SH_MOBILE_MERAM_PF_NV24)
247 return 1;
248 return 0;
249}
194 250
195 if (new->marker_icb < 0 || new->cache_icb < 0) 251/* Allocate memory for the ICBs and mark them as used. */
196 return; 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;
197 260
198 __set_bit(new->marker_icb, &priv->used_icb); 261 if (cfg->icb[0].meram_size == 0)
199 __set_bit(new->cache_icb, &priv->used_icb); 262 return ERR_PTR(-EINVAL);
200 263
201 n = priv->used_meram_cache_regions; 264 if (nplanes == 2 && cfg->icb[1].meram_size == 0)
265 return ERR_PTR(-EINVAL);
202 266
203 priv->used_meram_cache[n] = MERAM_CACHE_SET(new->meram_offset, 267 cache = kzalloc(sizeof(*cache), GFP_KERNEL);
204 new->meram_size); 268 if (cache == NULL)
269 return ERR_PTR(-ENOMEM);
205 270
206 priv->used_meram_cache_regions++; 271 cache->nplanes = nplanes;
207}
208 272
209/* 273 ret = __meram_alloc(priv, &cache->planes[0], cfg->icb[0].meram_size);
210 * unmark the specified ICB as used 274 if (ret < 0)
211 */ 275 goto error;
212 276
213static inline void meram_unmark(struct sh_mobile_meram_priv *priv, 277 cache->planes[0].marker->current_reg = 1;
214 struct sh_mobile_meram_icb *icb) 278 cache->planes[0].marker->pixelformat = pixelformat;
215{ 279
216 int i; 280 if (cache->nplanes == 1)
217 unsigned long pattern; 281 return cache;
218 282
219 if (icb->marker_icb < 0 || icb->cache_icb < 0) 283 ret = __meram_alloc(priv, &cache->planes[1], cfg->icb[1].meram_size);
220 return; 284 if (ret < 0) {
221 285 __meram_free(priv, &cache->planes[0]);
222 __clear_bit(icb->marker_icb, &priv->used_icb); 286 goto error;
223 __clear_bit(icb->cache_icb, &priv->used_icb);
224
225 pattern = MERAM_CACHE_SET(icb->meram_offset, icb->meram_size);
226 for (i = 0; i < priv->used_meram_cache_regions; i++) {
227 if (priv->used_meram_cache[i] == pattern) {
228 while (i < priv->used_meram_cache_regions - 1) {
229 priv->used_meram_cache[i] =
230 priv->used_meram_cache[i + 1] ;
231 i++;
232 }
233 priv->used_meram_cache[i] = 0;
234 priv->used_meram_cache_regions--;
235 break;
236 }
237 } 287 }
288
289 return cache;
290
291error:
292 kfree(cache);
293 return ERR_PTR(-ENOMEM);
238} 294}
239 295
240/* 296/* Unmark the specified ICB as used. */
241 * is this a YCbCr(NV12, NV16 or NV24) colorspace 297static void meram_free(struct sh_mobile_meram_priv *priv,
242 */ 298 struct sh_mobile_meram_fb_cache *cache)
243static inline int is_nvcolor(int cspace)
244{ 299{
245 if (cspace == SH_MOBILE_MERAM_PF_NV || 300 __meram_free(priv, &cache->planes[0]);
246 cspace == SH_MOBILE_MERAM_PF_NV24) 301 if (cache->nplanes == 2)
247 return 1; 302 __meram_free(priv, &cache->planes[1]);
248 return 0; 303
304 kfree(cache);
249} 305}
250 306
251/* 307/* Set the next address to fetch. */
252 * set the next address to fetch 308static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
253 */ 309 struct sh_mobile_meram_fb_cache *cache,
254static inline void meram_set_next_addr(struct sh_mobile_meram_priv *priv, 310 unsigned long base_addr_y,
255 struct sh_mobile_meram_cfg *cfg, 311 unsigned long base_addr_c)
256 unsigned long base_addr_y,
257 unsigned long base_addr_c)
258{ 312{
313 struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
259 unsigned long target; 314 unsigned long target;
260 315
261 target = (cfg->current_reg) ? MExxSARA : MExxSARB; 316 icb->current_reg ^= 1;
262 cfg->current_reg ^= 1; 317 target = icb->current_reg ? MExxSARB : MExxSARA;
263 318
264 /* set the next address to fetch */ 319 /* set the next address to fetch */
265 meram_write_icb(priv->base, cfg->icb[0].cache_icb, target, 320 meram_write_icb(priv->base, cache->planes[0].cache->index, target,
266 base_addr_y); 321 base_addr_y);
267 meram_write_icb(priv->base, cfg->icb[0].marker_icb, target, 322 meram_write_icb(priv->base, cache->planes[0].marker->index, target,
268 base_addr_y + cfg->icb[0].cache_unit); 323 base_addr_y + cache->planes[0].marker->cache_unit);
269 324
270 if (is_nvcolor(cfg->pixelformat)) { 325 if (cache->nplanes == 2) {
271 meram_write_icb(priv->base, cfg->icb[1].cache_icb, target, 326 meram_write_icb(priv->base, cache->planes[1].cache->index,
272 base_addr_c); 327 target, base_addr_c);
273 meram_write_icb(priv->base, cfg->icb[1].marker_icb, target, 328 meram_write_icb(priv->base, cache->planes[1].marker->index,
274 base_addr_c + cfg->icb[1].cache_unit); 329 target, base_addr_c +
330 cache->planes[1].marker->cache_unit);
275 } 331 }
276} 332}
277 333
278/* 334/* Get the next ICB address. */
279 * get the next ICB address 335static void
280 */ 336meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
281static inline void meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata, 337 struct sh_mobile_meram_fb_cache *cache,
282 struct sh_mobile_meram_cfg *cfg, 338 unsigned long *icb_addr_y, unsigned long *icb_addr_c)
283 unsigned long *icb_addr_y,
284 unsigned long *icb_addr_c)
285{ 339{
340 struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
286 unsigned long icb_offset; 341 unsigned long icb_offset;
287 342
288 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0) 343 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0)
289 icb_offset = 0x80000000 | (cfg->current_reg << 29); 344 icb_offset = 0x80000000 | (icb->current_reg << 29);
290 else 345 else
291 icb_offset = 0xc0000000 | (cfg->current_reg << 23); 346 icb_offset = 0xc0000000 | (icb->current_reg << 23);
292 347
293 *icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24); 348 *icb_addr_y = icb_offset | (cache->planes[0].marker->index << 24);
294 if (is_nvcolor(cfg->pixelformat)) 349 if (cache->nplanes == 2)
295 *icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24); 350 *icb_addr_c = icb_offset
351 | (cache->planes[1].marker->index << 24);
296} 352}
297 353
298#define MERAM_CALC_BYTECOUNT(x, y) \ 354#define MERAM_CALC_BYTECOUNT(x, y) \
299 (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1)) 355 (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
300 356
301/* 357/* Initialize MERAM. */
302 * initialize MERAM
303 */
304
305static int meram_init(struct sh_mobile_meram_priv *priv, 358static int meram_init(struct sh_mobile_meram_priv *priv,
306 struct sh_mobile_meram_icb *icb, 359 struct sh_mobile_meram_fb_plane *plane,
307 int xres, int yres, int *out_pitch) 360 unsigned int xres, unsigned int yres,
361 unsigned int *out_pitch)
308{ 362{
363 struct sh_mobile_meram_icb *marker = plane->marker;
309 unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres); 364 unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
310 unsigned long bnm; 365 unsigned long bnm;
311 int lcdc_pitch, xpitch, line_cnt; 366 unsigned int lcdc_pitch;
312 int save_lines; 367 unsigned int xpitch;
368 unsigned int line_cnt;
369 unsigned int save_lines;
313 370
314 /* adjust pitch to 1024, 2048, 4096 or 8192 */ 371 /* adjust pitch to 1024, 2048, 4096 or 8192 */
315 lcdc_pitch = (xres - 1) | 1023; 372 lcdc_pitch = (xres - 1) | 1023;
@@ -322,13 +379,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
322 lcdc_pitch = xpitch = MERAM_LINE_WIDTH; 379 lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
323 line_cnt = total_byte_count >> 11; 380 line_cnt = total_byte_count >> 11;
324 *out_pitch = xres; 381 *out_pitch = xres;
325 save_lines = (icb->meram_size / 16 / MERAM_SEC_LINE); 382 save_lines = plane->marker->size / 16 / MERAM_SEC_LINE;
326 save_lines *= MERAM_SEC_LINE; 383 save_lines *= MERAM_SEC_LINE;
327 } else { 384 } else {
328 xpitch = xres; 385 xpitch = xres;
329 line_cnt = yres; 386 line_cnt = yres;
330 *out_pitch = lcdc_pitch; 387 *out_pitch = lcdc_pitch;
331 save_lines = icb->meram_size / (lcdc_pitch >> 10) / 2; 388 save_lines = plane->marker->size / (lcdc_pitch >> 10) / 2;
332 save_lines &= 0xff; 389 save_lines &= 0xff;
333 } 390 }
334 bnm = (save_lines - 1) << 16; 391 bnm = (save_lines - 1) << 16;
@@ -336,19 +393,20 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
336 /* 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 */
337 394
338 /* set up ICB */ 395 /* set up ICB */
339 meram_write_icb(priv->base, icb->cache_icb, MExxBSIZE, 396 meram_write_icb(priv->base, plane->cache->index, MExxBSIZE,
340 MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1)); 397 MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
341 meram_write_icb(priv->base, icb->marker_icb, MExxBSIZE, 398 meram_write_icb(priv->base, plane->marker->index, MExxBSIZE,
342 MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1)); 399 MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
343 400
344 meram_write_icb(priv->base, icb->cache_icb, MExxMNCF, bnm); 401 meram_write_icb(priv->base, plane->cache->index, MExxMNCF, bnm);
345 meram_write_icb(priv->base, icb->marker_icb, MExxMNCF, bnm); 402 meram_write_icb(priv->base, plane->marker->index, MExxMNCF, bnm);
346 403
347 meram_write_icb(priv->base, icb->cache_icb, MExxSBSIZE, xpitch); 404 meram_write_icb(priv->base, plane->cache->index, MExxSBSIZE, xpitch);
348 meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch); 405 meram_write_icb(priv->base, plane->marker->index, MExxSBSIZE, xpitch);
349 406
350 /* save a cache unit size */ 407 /* save a cache unit size */
351 icb->cache_unit = xres * save_lines; 408 plane->cache->cache_unit = xres * save_lines;
409 plane->marker->cache_unit = xres * save_lines;
352 410
353 /* 411 /*
354 * Set MERAM for framebuffer 412 * Set MERAM for framebuffer
@@ -356,13 +414,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
356 * we also chain the cache_icb and the marker_icb. 414 * we also chain the cache_icb and the marker_icb.
357 * we also split the allocated MERAM buffer between two ICBs. 415 * we also split the allocated MERAM buffer between two ICBs.
358 */ 416 */
359 meram_write_icb(priv->base, icb->cache_icb, MExxCTL, 417 meram_write_icb(priv->base, plane->cache->index, MExxCTL,
360 MERAM_MExxCTL_VAL(icb->marker_icb, icb->meram_offset) | 418 MERAM_MExxCTL_VAL(plane->marker->index, marker->offset)
361 MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | 419 | MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
362 MExxCTL_MD_FB); 420 MExxCTL_MD_FB);
363 meram_write_icb(priv->base, icb->marker_icb, MExxCTL, 421 meram_write_icb(priv->base, plane->marker->index, MExxCTL,
364 MERAM_MExxCTL_VAL(icb->cache_icb, icb->meram_offset + 422 MERAM_MExxCTL_VAL(plane->cache->index, marker->offset +
365 icb->meram_size / 2) | 423 plane->marker->size / 2) |
366 MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | 424 MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
367 MExxCTL_MD_FB); 425 MExxCTL_MD_FB);
368 426
@@ -370,239 +428,175 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
370} 428}
371 429
372static void meram_deinit(struct sh_mobile_meram_priv *priv, 430static void meram_deinit(struct sh_mobile_meram_priv *priv,
373 struct sh_mobile_meram_icb *icb) 431 struct sh_mobile_meram_fb_plane *plane)
374{ 432{
375 /* disable ICB */ 433 /* disable ICB */
376 meram_write_icb(priv->base, icb->cache_icb, MExxCTL, 434 meram_write_icb(priv->base, plane->cache->index, MExxCTL,
377 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); 435 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
378 meram_write_icb(priv->base, icb->marker_icb, MExxCTL, 436 meram_write_icb(priv->base, plane->marker->index, MExxCTL,
379 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); 437 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
380 icb->cache_unit = 0; 438
439 plane->cache->cache_unit = 0;
440 plane->marker->cache_unit = 0;
381} 441}
382 442
383/* 443/* -----------------------------------------------------------------------------
384 * register the ICB 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 struct sh_mobile_meram_cfg *cfg, 448 const struct sh_mobile_meram_cfg *cfg,
389 int xres, int yres, int pixelformat, 449 unsigned int xres, unsigned int yres,
390 unsigned long base_addr_y, 450 unsigned int pixelformat,
391 unsigned long base_addr_c, 451 unsigned int *pitch)
392 unsigned long *icb_addr_y,
393 unsigned long *icb_addr_c,
394 int *pitch)
395{ 452{
396 struct platform_device *pdev; 453 struct sh_mobile_meram_fb_cache *cache;
397 struct sh_mobile_meram_priv *priv; 454 struct sh_mobile_meram_priv *priv = pdata->priv;
398 int n, out_pitch; 455 struct platform_device *pdev = pdata->pdev;
399 int error = 0; 456 unsigned int out_pitch;
400
401 if (!pdata || !pdata->priv || !pdata->pdev || !cfg)
402 return -EINVAL;
403 457
404 if (pixelformat != SH_MOBILE_MERAM_PF_NV && 458 if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
405 pixelformat != SH_MOBILE_MERAM_PF_NV24 && 459 pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
406 pixelformat != SH_MOBILE_MERAM_PF_RGB) 460 pixelformat != SH_MOBILE_MERAM_PF_RGB)
407 return -EINVAL; 461 return ERR_PTR(-EINVAL);
408
409 priv = pdata->priv;
410 pdev = pdata->pdev;
411 462
412 dev_dbg(&pdev->dev, "registering %dx%d (%s) (y=%08lx, c=%08lx)", 463 dev_dbg(&pdev->dev, "registering %dx%d (%s)", xres, yres,
413 xres, yres, (!pixelformat) ? "yuv" : "rgb", 464 !pixelformat ? "yuv" : "rgb");
414 base_addr_y, base_addr_c);
415 465
416 /* we can't handle wider than 8192px */ 466 /* we can't handle wider than 8192px */
417 if (xres > 8192) { 467 if (xres > 8192) {
418 dev_err(&pdev->dev, "width exceeding the limit (> 8192)."); 468 dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
419 return -EINVAL; 469 return ERR_PTR(-EINVAL);
420 }
421
422 /* do we have at least one ICB config? */
423 if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) {
424 dev_err(&pdev->dev, "at least one ICB is required.");
425 return -EINVAL;
426 } 470 }
427 471
428 mutex_lock(&priv->lock); 472 mutex_lock(&priv->lock);
429 473
430 if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) { 474 /* We now register the ICBs and allocate the MERAM regions. */
431 dev_err(&pdev->dev, "no more ICB available."); 475 cache = meram_alloc(priv, cfg, pixelformat);
432 error = -EINVAL; 476 if (IS_ERR(cache)) {
433 goto err; 477 dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
434 } 478 PTR_ERR(cache));
435
436 /* make sure that there's no overlaps */
437 if (meram_check_overlap(priv, &cfg->icb[0])) {
438 dev_err(&pdev->dev, "conflicting config detected.");
439 error = -EINVAL;
440 goto err; 479 goto err;
441 } 480 }
442 n = 1;
443
444 /* do the same if we have the second ICB set */
445 if (cfg->icb[1].marker_icb >= 0 && cfg->icb[1].cache_icb >= 0) {
446 if (meram_check_overlap(priv, &cfg->icb[1])) {
447 dev_err(&pdev->dev, "conflicting config detected.");
448 error = -EINVAL;
449 goto err;
450 }
451 n = 2;
452 }
453
454 if (is_nvcolor(pixelformat) && n != 2) {
455 dev_err(&pdev->dev, "requires two ICB sets for planar Y/C.");
456 error = -EINVAL;
457 goto err;
458 }
459
460 /* we now register the ICB */
461 cfg->pixelformat = pixelformat;
462 meram_mark(priv, &cfg->icb[0]);
463 if (is_nvcolor(pixelformat))
464 meram_mark(priv, &cfg->icb[1]);
465 481
466 /* initialize MERAM */ 482 /* initialize MERAM */
467 meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch); 483 meram_init(priv, &cache->planes[0], xres, yres, &out_pitch);
468 *pitch = out_pitch; 484 *pitch = out_pitch;
469 if (pixelformat == SH_MOBILE_MERAM_PF_NV) 485 if (pixelformat == SH_MOBILE_MERAM_PF_NV)
470 meram_init(priv, &cfg->icb[1], xres, (yres + 1) / 2, 486 meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2,
471 &out_pitch); 487 &out_pitch);
472 else if (pixelformat == SH_MOBILE_MERAM_PF_NV24) 488 else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
473 meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2, 489 meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2,
474 &out_pitch); 490 &out_pitch);
475 491
476 cfg->current_reg = 1;
477 meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
478 meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
479
480 dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx",
481 *icb_addr_y, *icb_addr_c);
482
483err: 492err:
484 mutex_unlock(&priv->lock); 493 mutex_unlock(&priv->lock);
485 return error; 494 return cache;
486} 495}
487 496
488static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, 497static void
489 struct sh_mobile_meram_cfg *cfg) 498sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
490{ 499{
491 struct sh_mobile_meram_priv *priv; 500 struct sh_mobile_meram_fb_cache *cache = data;
492 501 struct sh_mobile_meram_priv *priv = pdata->priv;
493 if (!pdata || !pdata->priv || !cfg)
494 return -EINVAL;
495
496 priv = pdata->priv;
497 502
498 mutex_lock(&priv->lock); 503 mutex_lock(&priv->lock);
499 504
500 /* deinit & unmark */ 505 /* deinit & free */
501 if (is_nvcolor(cfg->pixelformat)) { 506 meram_deinit(priv, &cache->planes[0]);
502 meram_deinit(priv, &cfg->icb[1]); 507 if (cache->nplanes == 2)
503 meram_unmark(priv, &cfg->icb[1]); 508 meram_deinit(priv, &cache->planes[1]);
504 }
505 meram_deinit(priv, &cfg->icb[0]);
506 meram_unmark(priv, &cfg->icb[0]);
507 509
508 mutex_unlock(&priv->lock); 510 meram_free(priv, cache);
509 511
510 return 0; 512 mutex_unlock(&priv->lock);
511} 513}
512 514
513static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, 515static void
514 struct sh_mobile_meram_cfg *cfg, 516sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
515 unsigned long base_addr_y, 517 unsigned long base_addr_y, unsigned long base_addr_c,
516 unsigned long base_addr_c, 518 unsigned long *icb_addr_y, unsigned long *icb_addr_c)
517 unsigned long *icb_addr_y,
518 unsigned long *icb_addr_c)
519{ 519{
520 struct sh_mobile_meram_priv *priv; 520 struct sh_mobile_meram_fb_cache *cache = data;
521 521 struct sh_mobile_meram_priv *priv = pdata->priv;
522 if (!pdata || !pdata->priv || !cfg)
523 return -EINVAL;
524
525 priv = pdata->priv;
526 522
527 mutex_lock(&priv->lock); 523 mutex_lock(&priv->lock);
528 524
529 meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c); 525 meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
530 meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c); 526 meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
531 527
532 mutex_unlock(&priv->lock); 528 mutex_unlock(&priv->lock);
533
534 return 0;
535} 529}
536 530
537static int sh_mobile_meram_runtime_suspend(struct device *dev) 531static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
532 .module = THIS_MODULE,
533 .meram_register = sh_mobile_meram_register,
534 .meram_unregister = sh_mobile_meram_unregister,
535 .meram_update = sh_mobile_meram_update,
536};
537
538/* -----------------------------------------------------------------------------
539 * Power management
540 */
541
542static int sh_mobile_meram_suspend(struct device *dev)
538{ 543{
539 struct platform_device *pdev = to_platform_device(dev); 544 struct platform_device *pdev = to_platform_device(dev);
540 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); 545 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
541 int k, j; 546 unsigned int i, j;
542 547
543 for (k = 0; k < CMN_REGS_SIZE; k++) 548 for (i = 0; i < MERAM_REGS_SIZE; i++)
544 priv->cmn_saved_regs[k] = meram_read_reg(priv->base, 549 priv->regs[i] = meram_read_reg(priv->base, common_regs[i]);
545 common_regs[k]);
546 550
547 for (j = 0; j < 32; j++) { 551 for (i = 0; i < 32; i++) {
548 if (!test_bit(j, &priv->used_icb)) 552 if (!test_bit(i, &priv->used_icb))
549 continue; 553 continue;
550 for (k = 0; k < ICB_REGS_SIZE; k++) { 554 for (j = 0; j < ICB_REGS_SIZE; j++) {
551 priv->icb_saved_regs[j * ICB_REGS_SIZE + k] = 555 priv->icbs[i].regs[j] =
552 meram_read_icb(priv->base, j, icb_regs[k]); 556 meram_read_icb(priv->base, i, icb_regs[j]);
553 /* Reset ICB on resume */ 557 /* Reset ICB on resume */
554 if (icb_regs[k] == MExxCTL) 558 if (icb_regs[j] == MExxCTL)
555 priv->icb_saved_regs[j * ICB_REGS_SIZE + k] |= 559 priv->icbs[i].regs[j] |=
556 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF; 560 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
557 } 561 }
558 } 562 }
559 return 0; 563 return 0;
560} 564}
561 565
562static int sh_mobile_meram_runtime_resume(struct device *dev) 566static int sh_mobile_meram_resume(struct device *dev)
563{ 567{
564 struct platform_device *pdev = to_platform_device(dev); 568 struct platform_device *pdev = to_platform_device(dev);
565 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); 569 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
566 int k, j; 570 unsigned int i, j;
567 571
568 for (j = 0; j < 32; j++) { 572 for (i = 0; i < 32; i++) {
569 if (!test_bit(j, &priv->used_icb)) 573 if (!test_bit(i, &priv->used_icb))
570 continue; 574 continue;
571 for (k = 0; k < ICB_REGS_SIZE; k++) { 575 for (j = 0; j < ICB_REGS_SIZE; j++)
572 meram_write_icb(priv->base, j, icb_regs[k], 576 meram_write_icb(priv->base, i, icb_regs[j],
573 priv->icb_saved_regs[j * ICB_REGS_SIZE + k]); 577 priv->icbs[i].regs[j]);
574 }
575 } 578 }
576 579
577 for (k = 0; k < CMN_REGS_SIZE; k++) 580 for (i = 0; i < MERAM_REGS_SIZE; i++)
578 meram_write_reg(priv->base, common_regs[k], 581 meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
579 priv->cmn_saved_regs[k]);
580 return 0; 582 return 0;
581} 583}
582 584
583static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = { 585static UNIVERSAL_DEV_PM_OPS(sh_mobile_meram_dev_pm_ops,
584 .runtime_suspend = sh_mobile_meram_runtime_suspend, 586 sh_mobile_meram_suspend,
585 .runtime_resume = sh_mobile_meram_runtime_resume, 587 sh_mobile_meram_resume, NULL);
586};
587
588static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
589 .module = THIS_MODULE,
590 .meram_register = sh_mobile_meram_register,
591 .meram_unregister = sh_mobile_meram_unregister,
592 .meram_update = sh_mobile_meram_update,
593};
594 588
595/* 589/* -----------------------------------------------------------------------------
596 * initialize MERAM 590 * Probe/remove and driver init/exit
597 */ 591 */
598 592
599static int sh_mobile_meram_remove(struct platform_device *pdev);
600
601static int __devinit sh_mobile_meram_probe(struct platform_device *pdev) 593static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
602{ 594{
603 struct sh_mobile_meram_priv *priv; 595 struct sh_mobile_meram_priv *priv;
604 struct sh_mobile_meram_info *pdata = pdev->dev.platform_data; 596 struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
605 struct resource *res; 597 struct resource *regs;
598 struct resource *meram;
599 unsigned int i;
606 int error; 600 int error;
607 601
608 if (!pdata) { 602 if (!pdata) {
@@ -610,8 +604,9 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
610 return -EINVAL; 604 return -EINVAL;
611 } 605 }
612 606
613 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 607 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
614 if (!res) { 608 meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
609 if (regs == NULL || meram == NULL) {
615 dev_err(&pdev->dev, "cannot get platform resources\n"); 610 dev_err(&pdev->dev, "cannot get platform resources\n");
616 return -ENOENT; 611 return -ENOENT;
617 } 612 }
@@ -622,32 +617,74 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
622 return -ENOMEM; 617 return -ENOMEM;
623 } 618 }
624 619
625 platform_set_drvdata(pdev, priv); 620 /* Initialize private data. */
626
627 /* initialize private data */
628 mutex_init(&priv->lock); 621 mutex_init(&priv->lock);
629 priv->base = ioremap_nocache(res->start, resource_size(res)); 622 priv->used_icb = pdata->reserved_icbs;
623
624 for (i = 0; i < MERAM_ICB_NUM; ++i)
625 priv->icbs[i].index = i;
626
627 pdata->ops = &sh_mobile_meram_ops;
628 pdata->priv = priv;
629 pdata->pdev = pdev;
630
631 /* Request memory regions and remap the registers. */
632 if (!request_mem_region(regs->start, resource_size(regs), pdev->name)) {
633 dev_err(&pdev->dev, "MERAM registers region already claimed\n");
634 error = -EBUSY;
635 goto err_req_regs;
636 }
637
638 if (!request_mem_region(meram->start, resource_size(meram),
639 pdev->name)) {
640 dev_err(&pdev->dev, "MERAM memory region already claimed\n");
641 error = -EBUSY;
642 goto err_req_meram;
643 }
644
645 priv->base = ioremap_nocache(regs->start, resource_size(regs));
630 if (!priv->base) { 646 if (!priv->base) {
631 dev_err(&pdev->dev, "ioremap failed\n"); 647 dev_err(&pdev->dev, "ioremap failed\n");
632 error = -EFAULT; 648 error = -EFAULT;
633 goto err; 649 goto err_ioremap;
634 } 650 }
635 pdata->ops = &sh_mobile_meram_ops; 651
636 pdata->priv = priv; 652 priv->meram = meram->start;
637 pdata->pdev = pdev; 653
654 /* Create and initialize the MERAM memory pool. */
655 priv->pool = gen_pool_create(ilog2(MERAM_GRANULARITY), -1);
656 if (priv->pool == NULL) {
657 error = -ENOMEM;
658 goto err_genpool;
659 }
660
661 error = gen_pool_add(priv->pool, meram->start, resource_size(meram),
662 -1);
663 if (error < 0)
664 goto err_genpool;
638 665
639 /* initialize ICB addressing mode */ 666 /* initialize ICB addressing mode */
640 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1) 667 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1)
641 meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1); 668 meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
642 669
670 platform_set_drvdata(pdev, priv);
643 pm_runtime_enable(&pdev->dev); 671 pm_runtime_enable(&pdev->dev);
644 672
645 dev_info(&pdev->dev, "sh_mobile_meram initialized."); 673 dev_info(&pdev->dev, "sh_mobile_meram initialized.");
646 674
647 return 0; 675 return 0;
648 676
649err: 677err_genpool:
650 sh_mobile_meram_remove(pdev); 678 if (priv->pool)
679 gen_pool_destroy(priv->pool);
680 iounmap(priv->base);
681err_ioremap:
682 release_mem_region(meram->start, resource_size(meram));
683err_req_meram:
684 release_mem_region(regs->start, resource_size(regs));
685err_req_regs:
686 mutex_destroy(&priv->lock);
687 kfree(priv);
651 688
652 return error; 689 return error;
653} 690}
@@ -656,11 +693,16 @@ err:
656static int sh_mobile_meram_remove(struct platform_device *pdev) 693static int sh_mobile_meram_remove(struct platform_device *pdev)
657{ 694{
658 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); 695 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
696 struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
697 struct resource *meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
659 698
660 pm_runtime_disable(&pdev->dev); 699 pm_runtime_disable(&pdev->dev);
661 700
662 if (priv->base) 701 gen_pool_destroy(priv->pool);
663 iounmap(priv->base); 702
703 iounmap(priv->base);
704 release_mem_region(meram->start, resource_size(meram));
705 release_mem_region(regs->start, resource_size(regs));
664 706
665 mutex_destroy(&priv->lock); 707 mutex_destroy(&priv->lock);
666 708