arm: mach-shmobile: Constify sh_mobile_meram_cfg structures
[GitHub/LineageOS/android_kernel_samsung_universal7580.git] / drivers / video / sh_mobile_meram.c
CommitLineData
7caa4342
DHG
1/*
2 * SuperH Mobile MERAM Driver for SuperH Mobile LCDC Driver
3 *
4 * Copyright (c) 2011 Damian Hobson-Garcia <dhobsong@igel.co.jp>
5 * Takanari Hayama <taki@igel.co.jp>
6 *
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License. See the file "COPYING" in the main directory of this archive
9 * for more details.
10 */
11
7554340c 12#include <linux/device.h>
48110050 13#include <linux/err.h>
974d250b 14#include <linux/genalloc.h>
7554340c 15#include <linux/io.h>
7caa4342
DHG
16#include <linux/kernel.h>
17#include <linux/module.h>
7554340c 18#include <linux/platform_device.h>
17673778 19#include <linux/pm_runtime.h>
7caa4342 20#include <linux/slab.h>
7554340c 21
8a20974f 22#include <video/sh_mobile_meram.h>
7caa4342 23
7554340c
LP
24/* -----------------------------------------------------------------------------
25 * MERAM registers
26 */
27
f0a260fe
LP
28#define MEVCR1 0x4
29#define MEVCR1_RST (1 << 31)
30#define MEVCR1_WD (1 << 30)
31#define MEVCR1_AMD1 (1 << 29)
32#define MEVCR1_AMD0 (1 << 28)
33#define MEQSEL1 0x40
34#define MEQSEL2 0x44
35
36#define MExxCTL 0x400
37#define MExxCTL_BV (1 << 31)
38#define MExxCTL_BSZ_SHIFT 28
39#define MExxCTL_MSAR_MASK (0x7ff << MExxCTL_MSAR_SHIFT)
40#define MExxCTL_MSAR_SHIFT 16
41#define MExxCTL_NXT_MASK (0x1f << MExxCTL_NXT_SHIFT)
42#define MExxCTL_NXT_SHIFT 11
43#define MExxCTL_WD1 (1 << 10)
44#define MExxCTL_WD0 (1 << 9)
45#define MExxCTL_WS (1 << 8)
46#define MExxCTL_CB (1 << 7)
47#define MExxCTL_WBF (1 << 6)
48#define MExxCTL_WF (1 << 5)
49#define MExxCTL_RF (1 << 4)
50#define MExxCTL_CM (1 << 3)
51#define MExxCTL_MD_READ (1 << 0)
52#define MExxCTL_MD_WRITE (2 << 0)
53#define MExxCTL_MD_ICB_WB (3 << 0)
54#define MExxCTL_MD_ICB (4 << 0)
55#define MExxCTL_MD_FB (7 << 0)
56#define MExxCTL_MD_MASK (7 << 0)
57#define MExxBSIZE 0x404
58#define MExxBSIZE_RCNT_SHIFT 28
59#define MExxBSIZE_YSZM1_SHIFT 16
60#define MExxBSIZE_XSZM1_SHIFT 0
61#define MExxMNCF 0x408
62#define MExxMNCF_KWBNM_SHIFT 28
63#define MExxMNCF_KRBNM_SHIFT 24
64#define MExxMNCF_BNM_SHIFT 16
65#define MExxMNCF_XBV (1 << 15)
66#define MExxMNCF_CPL_YCBCR444 (1 << 12)
67#define MExxMNCF_CPL_YCBCR420 (2 << 12)
68#define MExxMNCF_CPL_YCBCR422 (3 << 12)
69#define MExxMNCF_CPL_MSK (3 << 12)
70#define MExxMNCF_BL (1 << 2)
71#define MExxMNCF_LNM_SHIFT 0
72#define MExxSARA 0x410
73#define MExxSARB 0x414
74#define MExxSBSIZE 0x418
75#define MExxSBSIZE_HDV (1 << 31)
76#define MExxSBSIZE_HSZ16 (0 << 28)
77#define MExxSBSIZE_HSZ32 (1 << 28)
78#define MExxSBSIZE_HSZ64 (2 << 28)
79#define MExxSBSIZE_HSZ128 (3 << 28)
80#define MExxSBSIZE_SBSIZZ_SHIFT 0
81
82#define MERAM_MExxCTL_VAL(next, addr) \
83 ((((next) << MExxCTL_NXT_SHIFT) & MExxCTL_NXT_MASK) | \
84 (((addr) << MExxCTL_MSAR_SHIFT) & MExxCTL_MSAR_MASK))
85#define MERAM_MExxBSIZE_VAL(rcnt, yszm1, xszm1) \
86 (((rcnt) << MExxBSIZE_RCNT_SHIFT) | \
87 ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
88 ((xszm1) << MExxBSIZE_XSZM1_SHIFT))
7caa4342 89
762f7cc9 90static const unsigned long common_regs[] = {
0b3bb77c
DHG
91 MEVCR1,
92 MEQSEL1,
93 MEQSEL2,
94};
7554340c 95#define MERAM_REGS_SIZE ARRAY_SIZE(common_regs)
0b3bb77c 96
762f7cc9 97static const unsigned long icb_regs[] = {
0b3bb77c
DHG
98 MExxCTL,
99 MExxBSIZE,
100 MExxMNCF,
101 MExxSARA,
102 MExxSARB,
103 MExxSBSIZE,
104};
105#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
106
2a618e03
LP
107/*
108 * sh_mobile_meram_icb - MERAM ICB information
109 * @regs: Registers cache
48110050 110 * @index: ICB index
974d250b 111 * @offset: MERAM block offset
48110050 112 * @size: MERAM block size in KiB
2a618e03
LP
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];
48110050 119 unsigned int index;
974d250b
LP
120 unsigned long offset;
121 unsigned int size;
2a618e03 122
2a618e03
LP
123 unsigned int cache_unit;
124 unsigned int pixelformat;
125 unsigned int current_reg;
126};
127
7554340c
LP
128#define MERAM_ICB_NUM 32
129
48110050
LP
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
2a618e03
LP
140/*
141 * sh_mobile_meram_priv - MERAM device
142 * @base: Registers base address
974d250b 143 * @meram: MERAM physical address
2a618e03
LP
144 * @regs: Registers cache
145 * @lock: Protects used_icb and icbs
146 * @used_icb: Bitmask of used ICBs
147 * @icbs: ICBs
974d250b 148 * @pool: Allocation pool to manage the MERAM
2a618e03 149 */
0aa492be 150struct sh_mobile_meram_priv {
2a618e03 151 void __iomem *base;
974d250b 152 unsigned long meram;
7554340c 153 unsigned long regs[MERAM_REGS_SIZE];
2a618e03
LP
154
155 struct mutex lock;
156 unsigned long used_icb;
7554340c 157 struct sh_mobile_meram_icb icbs[MERAM_ICB_NUM];
974d250b
LP
158
159 struct gen_pool *pool;
0aa492be
DHG
160};
161
7caa4342 162/* settings */
974d250b 163#define MERAM_GRANULARITY 1024
7554340c
LP
164#define MERAM_SEC_LINE 15
165#define MERAM_LINE_WIDTH 2048
7caa4342 166
7554340c
LP
167/* -----------------------------------------------------------------------------
168 * Registers access
7caa4342
DHG
169 */
170
f0a260fe 171#define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20)
7caa4342 172
05432837
LP
173static inline void meram_write_icb(void __iomem *base, unsigned int idx,
174 unsigned int off, unsigned long val)
7caa4342
DHG
175{
176 iowrite32(val, MERAM_ICB_OFFSET(base, idx, off));
177}
178
05432837
LP
179static inline unsigned long meram_read_icb(void __iomem *base, unsigned int idx,
180 unsigned int off)
7caa4342
DHG
181{
182 return ioread32(MERAM_ICB_OFFSET(base, idx, off));
183}
184
05432837
LP
185static inline void meram_write_reg(void __iomem *base, unsigned int off,
186 unsigned long val)
7caa4342
DHG
187{
188 iowrite32(val, base + off);
189}
190
05432837 191static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
7caa4342
DHG
192{
193 return ioread32(base + off);
194}
195
7554340c
LP
196/* -----------------------------------------------------------------------------
197 * Allocation
7caa4342
DHG
198 */
199
48110050
LP
200/* Allocate ICBs and MERAM for a plane. */
201static int __meram_alloc(struct sh_mobile_meram_priv *priv,
202 struct sh_mobile_meram_fb_plane *plane,
203 size_t size)
7caa4342 204{
48110050
LP
205 unsigned long mem;
206 unsigned long idx;
7caa4342 207
48110050
LP
208 idx = find_first_zero_bit(&priv->used_icb, 28);
209 if (idx == 28)
210 return -ENOMEM;
211 plane->cache = &priv->icbs[idx];
7caa4342 212
48110050
LP
213 idx = find_next_zero_bit(&priv->used_icb, 32, 28);
214 if (idx == 32)
215 return -ENOMEM;
216 plane->marker = &priv->icbs[idx];
974d250b 217
48110050 218 mem = gen_pool_alloc(priv->pool, size * 1024);
974d250b
LP
219 if (mem == 0)
220 return -ENOMEM;
221
48110050
LP
222 __set_bit(plane->marker->index, &priv->used_icb);
223 __set_bit(plane->cache->index, &priv->used_icb);
7caa4342 224
48110050
LP
225 plane->marker->offset = mem - priv->meram;
226 plane->marker->size = size;
974d250b
LP
227
228 return 0;
7caa4342
DHG
229}
230
48110050
LP
231/* Free ICBs and MERAM for a plane. */
232static void __meram_free(struct sh_mobile_meram_priv *priv,
233 struct sh_mobile_meram_fb_plane *plane)
7caa4342 234{
48110050
LP
235 gen_pool_free(priv->pool, priv->meram + plane->marker->offset,
236 plane->marker->size * 1024);
974d250b 237
48110050
LP
238 __clear_bit(plane->marker->index, &priv->used_icb);
239 __clear_bit(plane->cache->index, &priv->used_icb);
7caa4342
DHG
240}
241
7554340c 242/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
762f7cc9 243static int is_nvcolor(int cspace)
3fedd2ac
DHG
244{
245 if (cspace == SH_MOBILE_MERAM_PF_NV ||
2a618e03 246 cspace == SH_MOBILE_MERAM_PF_NV24)
3fedd2ac
DHG
247 return 1;
248 return 0;
249}
7caa4342 250
48110050
LP
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
7554340c 307/* Set the next address to fetch. */
762f7cc9 308static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
48110050 309 struct sh_mobile_meram_fb_cache *cache,
762f7cc9
LP
310 unsigned long base_addr_y,
311 unsigned long base_addr_c)
7caa4342 312{
48110050 313 struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
7caa4342
DHG
314 unsigned long target;
315
2a618e03
LP
316 icb->current_reg ^= 1;
317 target = icb->current_reg ? MExxSARB : MExxSARA;
7caa4342
DHG
318
319 /* set the next address to fetch */
48110050 320 meram_write_icb(priv->base, cache->planes[0].cache->index, target,
7caa4342 321 base_addr_y);
48110050
LP
322 meram_write_icb(priv->base, cache->planes[0].marker->index, target,
323 base_addr_y + cache->planes[0].marker->cache_unit);
324
325 if (cache->nplanes == 2) {
326 meram_write_icb(priv->base, cache->planes[1].cache->index,
327 target, base_addr_c);
328 meram_write_icb(priv->base, cache->planes[1].marker->index,
329 target, base_addr_c +
330 cache->planes[1].marker->cache_unit);
7caa4342
DHG
331 }
332}
333
7554340c 334/* Get the next ICB address. */
762f7cc9 335static void
2a618e03 336meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
48110050 337 struct sh_mobile_meram_fb_cache *cache,
2a618e03 338 unsigned long *icb_addr_y, unsigned long *icb_addr_c)
7caa4342 339{
48110050 340 struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
7caa4342
DHG
341 unsigned long icb_offset;
342
343 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0)
2a618e03 344 icb_offset = 0x80000000 | (icb->current_reg << 29);
7caa4342 345 else
2a618e03 346 icb_offset = 0xc0000000 | (icb->current_reg << 23);
7caa4342 347
48110050
LP
348 *icb_addr_y = icb_offset | (cache->planes[0].marker->index << 24);
349 if (cache->nplanes == 2)
350 *icb_addr_c = icb_offset
351 | (cache->planes[1].marker->index << 24);
7caa4342
DHG
352}
353
354#define MERAM_CALC_BYTECOUNT(x, y) \
355 (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
356
7554340c 357/* Initialize MERAM. */
7caa4342 358static int meram_init(struct sh_mobile_meram_priv *priv,
48110050 359 struct sh_mobile_meram_fb_plane *plane,
05432837
LP
360 unsigned int xres, unsigned int yres,
361 unsigned int *out_pitch)
7caa4342 362{
48110050 363 struct sh_mobile_meram_icb *marker = plane->marker;
7caa4342
DHG
364 unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
365 unsigned long bnm;
05432837
LP
366 unsigned int lcdc_pitch;
367 unsigned int xpitch;
368 unsigned int line_cnt;
369 unsigned int save_lines;
7caa4342
DHG
370
371 /* adjust pitch to 1024, 2048, 4096 or 8192 */
372 lcdc_pitch = (xres - 1) | 1023;
373 lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 1);
374 lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 2);
375 lcdc_pitch += 1;
376
377 /* derive settings */
378 if (lcdc_pitch == 8192 && yres >= 1024) {
379 lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
380 line_cnt = total_byte_count >> 11;
381 *out_pitch = xres;
48110050 382 save_lines = plane->marker->size / 16 / MERAM_SEC_LINE;
7caa4342
DHG
383 save_lines *= MERAM_SEC_LINE;
384 } else {
385 xpitch = xres;
386 line_cnt = yres;
387 *out_pitch = lcdc_pitch;
48110050 388 save_lines = plane->marker->size / (lcdc_pitch >> 10) / 2;
7caa4342
DHG
389 save_lines &= 0xff;
390 }
391 bnm = (save_lines - 1) << 16;
392
393 /* TODO: we better to check if we have enough MERAM buffer size */
394
395 /* set up ICB */
48110050 396 meram_write_icb(priv->base, plane->cache->index, MExxBSIZE,
7caa4342 397 MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
48110050 398 meram_write_icb(priv->base, plane->marker->index, MExxBSIZE,
7caa4342
DHG
399 MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
400
48110050
LP
401 meram_write_icb(priv->base, plane->cache->index, MExxMNCF, bnm);
402 meram_write_icb(priv->base, plane->marker->index, MExxMNCF, bnm);
7caa4342 403
48110050
LP
404 meram_write_icb(priv->base, plane->cache->index, MExxSBSIZE, xpitch);
405 meram_write_icb(priv->base, plane->marker->index, MExxSBSIZE, xpitch);
7caa4342
DHG
406
407 /* save a cache unit size */
48110050
LP
408 plane->cache->cache_unit = xres * save_lines;
409 plane->marker->cache_unit = xres * save_lines;
7caa4342
DHG
410
411 /*
412 * Set MERAM for framebuffer
413 *
7caa4342
DHG
414 * we also chain the cache_icb and the marker_icb.
415 * we also split the allocated MERAM buffer between two ICBs.
416 */
48110050
LP
417 meram_write_icb(priv->base, plane->cache->index, MExxCTL,
418 MERAM_MExxCTL_VAL(plane->marker->index, marker->offset)
419 | MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
f0a260fe 420 MExxCTL_MD_FB);
48110050
LP
421 meram_write_icb(priv->base, plane->marker->index, MExxCTL,
422 MERAM_MExxCTL_VAL(plane->cache->index, marker->offset +
423 plane->marker->size / 2) |
f0a260fe
LP
424 MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
425 MExxCTL_MD_FB);
7caa4342
DHG
426
427 return 0;
428}
429
430static void meram_deinit(struct sh_mobile_meram_priv *priv,
48110050 431 struct sh_mobile_meram_fb_plane *plane)
7caa4342
DHG
432{
433 /* disable ICB */
48110050 434 meram_write_icb(priv->base, plane->cache->index, MExxCTL,
da6cf512 435 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
48110050 436 meram_write_icb(priv->base, plane->marker->index, MExxCTL,
da6cf512 437 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
2a618e03 438
48110050
LP
439 plane->cache->cache_unit = 0;
440 plane->marker->cache_unit = 0;
7caa4342
DHG
441}
442
7554340c
LP
443/* -----------------------------------------------------------------------------
444 * Registration/unregistration
7caa4342
DHG
445 */
446
48110050
LP
447static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
448 const struct sh_mobile_meram_cfg *cfg,
449 unsigned int xres, unsigned int yres,
450 unsigned int pixelformat,
451 unsigned long base_addr_y,
452 unsigned long base_addr_c,
453 unsigned long *icb_addr_y,
454 unsigned long *icb_addr_c,
455 unsigned int *pitch)
7caa4342 456{
48110050 457 struct sh_mobile_meram_fb_cache *cache;
7caa4342 458 struct sh_mobile_meram_priv *priv;
48110050 459 struct platform_device *pdev;
05432837 460 unsigned int out_pitch;
7caa4342
DHG
461
462 if (!pdata || !pdata->priv || !pdata->pdev || !cfg)
48110050 463 return ERR_PTR(-EINVAL);
7caa4342
DHG
464
465 if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
3fedd2ac 466 pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
7caa4342 467 pixelformat != SH_MOBILE_MERAM_PF_RGB)
48110050 468 return ERR_PTR(-EINVAL);
7caa4342
DHG
469
470 priv = pdata->priv;
471 pdev = pdata->pdev;
472
473 dev_dbg(&pdev->dev, "registering %dx%d (%s) (y=%08lx, c=%08lx)",
474 xres, yres, (!pixelformat) ? "yuv" : "rgb",
475 base_addr_y, base_addr_c);
476
7caa4342
DHG
477 /* we can't handle wider than 8192px */
478 if (xres > 8192) {
479 dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
48110050 480 return ERR_PTR(-EINVAL);
7963e21e
LP
481 }
482
483 mutex_lock(&priv->lock);
484
974d250b 485 /* We now register the ICBs and allocate the MERAM regions. */
48110050
LP
486 cache = meram_alloc(priv, cfg, pixelformat);
487 if (IS_ERR(cache)) {
488 dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
489 PTR_ERR(cache));
974d250b 490 goto err;
974d250b 491 }
7caa4342
DHG
492
493 /* initialize MERAM */
48110050 494 meram_init(priv, &cache->planes[0], xres, yres, &out_pitch);
7caa4342
DHG
495 *pitch = out_pitch;
496 if (pixelformat == SH_MOBILE_MERAM_PF_NV)
48110050 497 meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2,
7caa4342 498 &out_pitch);
3fedd2ac 499 else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
48110050 500 meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2,
3fedd2ac 501 &out_pitch);
7caa4342 502
48110050
LP
503 meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
504 meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
7caa4342
DHG
505
506 dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx",
507 *icb_addr_y, *icb_addr_c);
508
509err:
510 mutex_unlock(&priv->lock);
48110050 511 return cache;
7caa4342
DHG
512}
513
48110050
LP
514static int
515sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
7caa4342 516{
48110050 517 struct sh_mobile_meram_fb_cache *cache = data;
7caa4342
DHG
518 struct sh_mobile_meram_priv *priv;
519
48110050 520 if (!pdata || !pdata->priv || !data)
7caa4342
DHG
521 return -EINVAL;
522
523 priv = pdata->priv;
524
525 mutex_lock(&priv->lock);
526
48110050
LP
527 /* deinit & free */
528 meram_deinit(priv, &cache->planes[0]);
529 if (cache->nplanes == 2)
530 meram_deinit(priv, &cache->planes[1]);
531
532 meram_free(priv, cache);
7caa4342
DHG
533
534 mutex_unlock(&priv->lock);
535
536 return 0;
537}
538
48110050
LP
539static int
540sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
541 unsigned long base_addr_y, unsigned long base_addr_c,
542 unsigned long *icb_addr_y, unsigned long *icb_addr_c)
7caa4342 543{
48110050 544 struct sh_mobile_meram_fb_cache *cache = data;
7caa4342
DHG
545 struct sh_mobile_meram_priv *priv;
546
48110050 547 if (!pdata || !pdata->priv || !data)
7caa4342
DHG
548 return -EINVAL;
549
550 priv = pdata->priv;
551
552 mutex_lock(&priv->lock);
553
48110050
LP
554 meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
555 meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
7caa4342
DHG
556
557 mutex_unlock(&priv->lock);
558
559 return 0;
560}
561
7554340c
LP
562static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
563 .module = THIS_MODULE,
564 .meram_register = sh_mobile_meram_register,
565 .meram_unregister = sh_mobile_meram_unregister,
566 .meram_update = sh_mobile_meram_update,
567};
568
569/* -----------------------------------------------------------------------------
570 * Power management
571 */
572
0b3bb77c
DHG
573static int sh_mobile_meram_runtime_suspend(struct device *dev)
574{
575 struct platform_device *pdev = to_platform_device(dev);
576 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
05432837 577 unsigned int i, j;
0b3bb77c 578
7554340c 579 for (i = 0; i < MERAM_REGS_SIZE; i++)
2a618e03 580 priv->regs[i] = meram_read_reg(priv->base, common_regs[i]);
0b3bb77c 581
05432837
LP
582 for (i = 0; i < 32; i++) {
583 if (!test_bit(i, &priv->used_icb))
0b3bb77c 584 continue;
05432837 585 for (j = 0; j < ICB_REGS_SIZE; j++) {
2a618e03 586 priv->icbs[i].regs[j] =
05432837 587 meram_read_icb(priv->base, i, icb_regs[j]);
0b3bb77c 588 /* Reset ICB on resume */
05432837 589 if (icb_regs[j] == MExxCTL)
2a618e03 590 priv->icbs[i].regs[j] |=
f0a260fe 591 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
0b3bb77c
DHG
592 }
593 }
594 return 0;
595}
596
597static int sh_mobile_meram_runtime_resume(struct device *dev)
598{
599 struct platform_device *pdev = to_platform_device(dev);
600 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
05432837 601 unsigned int i, j;
0b3bb77c 602
05432837
LP
603 for (i = 0; i < 32; i++) {
604 if (!test_bit(i, &priv->used_icb))
0b3bb77c 605 continue;
2a618e03 606 for (j = 0; j < ICB_REGS_SIZE; j++)
05432837 607 meram_write_icb(priv->base, i, icb_regs[j],
2a618e03 608 priv->icbs[i].regs[j]);
0b3bb77c
DHG
609 }
610
7554340c 611 for (i = 0; i < MERAM_REGS_SIZE; i++)
2a618e03 612 meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
0b3bb77c
DHG
613 return 0;
614}
615
616static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = {
617 .runtime_suspend = sh_mobile_meram_runtime_suspend,
618 .runtime_resume = sh_mobile_meram_runtime_resume,
619};
620
7554340c
LP
621/* -----------------------------------------------------------------------------
622 * Probe/remove and driver init/exit
7caa4342
DHG
623 */
624
7caa4342
DHG
625static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
626{
627 struct sh_mobile_meram_priv *priv;
628 struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
e1d1144e
LP
629 struct resource *regs;
630 struct resource *meram;
48110050 631 unsigned int i;
7caa4342
DHG
632 int error;
633
634 if (!pdata) {
635 dev_err(&pdev->dev, "no platform data defined\n");
636 return -EINVAL;
637 }
638
e1d1144e
LP
639 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
640 meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
641 if (regs == NULL || meram == NULL) {
7caa4342
DHG
642 dev_err(&pdev->dev, "cannot get platform resources\n");
643 return -ENOENT;
644 }
645
646 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
647 if (!priv) {
648 dev_err(&pdev->dev, "cannot allocate device data\n");
649 return -ENOMEM;
650 }
651
48110050 652 /* Initialize private data. */
7caa4342 653 mutex_init(&priv->lock);
48110050
LP
654 priv->used_icb = pdata->reserved_icbs;
655
656 for (i = 0; i < MERAM_ICB_NUM; ++i)
657 priv->icbs[i].index = i;
658
e1d1144e
LP
659 pdata->ops = &sh_mobile_meram_ops;
660 pdata->priv = priv;
661 pdata->pdev = pdev;
662
974d250b 663 /* Request memory regions and remap the registers. */
e1d1144e
LP
664 if (!request_mem_region(regs->start, resource_size(regs), pdev->name)) {
665 dev_err(&pdev->dev, "MERAM registers region already claimed\n");
666 error = -EBUSY;
667 goto err_req_regs;
668 }
669
670 if (!request_mem_region(meram->start, resource_size(meram),
671 pdev->name)) {
672 dev_err(&pdev->dev, "MERAM memory region already claimed\n");
673 error = -EBUSY;
674 goto err_req_meram;
675 }
676
677 priv->base = ioremap_nocache(regs->start, resource_size(regs));
7caa4342
DHG
678 if (!priv->base) {
679 dev_err(&pdev->dev, "ioremap failed\n");
680 error = -EFAULT;
e1d1144e 681 goto err_ioremap;
7caa4342 682 }
7caa4342 683
974d250b
LP
684 priv->meram = meram->start;
685
686 /* Create and initialize the MERAM memory pool. */
687 priv->pool = gen_pool_create(ilog2(MERAM_GRANULARITY), -1);
688 if (priv->pool == NULL) {
689 error = -ENOMEM;
690 goto err_genpool;
691 }
692
693 error = gen_pool_add(priv->pool, meram->start, resource_size(meram),
694 -1);
695 if (error < 0)
696 goto err_genpool;
697
7caa4342
DHG
698 /* initialize ICB addressing mode */
699 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1)
f0a260fe 700 meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
7caa4342 701
e1d1144e 702 platform_set_drvdata(pdev, priv);
17673778
DHG
703 pm_runtime_enable(&pdev->dev);
704
7caa4342
DHG
705 dev_info(&pdev->dev, "sh_mobile_meram initialized.");
706
707 return 0;
708
974d250b
LP
709err_genpool:
710 if (priv->pool)
711 gen_pool_destroy(priv->pool);
712 iounmap(priv->base);
e1d1144e
LP
713err_ioremap:
714 release_mem_region(meram->start, resource_size(meram));
715err_req_meram:
716 release_mem_region(regs->start, resource_size(regs));
717err_req_regs:
718 mutex_destroy(&priv->lock);
719 kfree(priv);
7caa4342
DHG
720
721 return error;
722}
723
724
725static int sh_mobile_meram_remove(struct platform_device *pdev)
726{
727 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
e1d1144e
LP
728 struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
729 struct resource *meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
7caa4342 730
17673778
DHG
731 pm_runtime_disable(&pdev->dev);
732
974d250b
LP
733 gen_pool_destroy(priv->pool);
734
e1d1144e
LP
735 iounmap(priv->base);
736 release_mem_region(meram->start, resource_size(meram));
737 release_mem_region(regs->start, resource_size(regs));
7caa4342
DHG
738
739 mutex_destroy(&priv->lock);
740
741 kfree(priv);
742
743 return 0;
744}
745
746static struct platform_driver sh_mobile_meram_driver = {
747 .driver = {
748 .name = "sh_mobile_meram",
749 .owner = THIS_MODULE,
0b3bb77c 750 .pm = &sh_mobile_meram_dev_pm_ops,
7caa4342
DHG
751 },
752 .probe = sh_mobile_meram_probe,
753 .remove = sh_mobile_meram_remove,
754};
755
4277f2c4 756module_platform_driver(sh_mobile_meram_driver);
7caa4342
DHG
757
758MODULE_DESCRIPTION("SuperH Mobile MERAM driver");
759MODULE_AUTHOR("Damian Hobson-Garcia / Takanari Hayama");
760MODULE_LICENSE("GPL v2");