diff options
Diffstat (limited to 'drivers/video/sh_mobile_meram.c')
-rw-r--r-- | drivers/video/sh_mobile_meram.c | 690 |
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 | 90 | static const unsigned long common_regs[] = { |
85 | |||
86 | static 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 | ||
93 | static unsigned long icb_regs[] = { | 97 | static 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 | */ | ||
117 | struct 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 | |||
130 | struct sh_mobile_meram_fb_plane { | ||
131 | struct sh_mobile_meram_icb *marker; | ||
132 | struct sh_mobile_meram_icb *cache; | ||
133 | }; | ||
134 | |||
135 | struct 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 | */ | ||
103 | struct sh_mobile_meram_priv { | 150 | struct 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 | ||
123 | static inline void meram_write_icb(void __iomem *base, int idx, int off, | 173 | static 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 | ||
129 | static inline unsigned long meram_read_icb(void __iomem *base, int idx, int off) | 179 | static 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 | ||
134 | static inline void meram_write_reg(void __iomem *base, int off, | 185 | static 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 | ||
140 | static inline unsigned long meram_read_reg(void __iomem *base, int off) | 191 | static 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 | ||
158 | static 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) | 201 | static 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 | 232 | static 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 | ||
190 | static 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? */ | ||
243 | static 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; | 252 | static struct sh_mobile_meram_fb_cache * |
253 | meram_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 | ||
213 | static 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 | |||
291 | error: | ||
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 | 297 | static void meram_free(struct sh_mobile_meram_priv *priv, |
242 | */ | 298 | struct sh_mobile_meram_fb_cache *cache) |
243 | static 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 | 308 | static void meram_set_next_addr(struct sh_mobile_meram_priv *priv, |
253 | */ | 309 | struct sh_mobile_meram_fb_cache *cache, |
254 | static 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 | 335 | static void |
280 | */ | 336 | meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata, |
281 | static 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 | |||
305 | static int meram_init(struct sh_mobile_meram_priv *priv, | 358 | static 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 | ||
372 | static void meram_deinit(struct sh_mobile_meram_priv *priv, | 430 | static 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 | ||
387 | static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata, | 447 | static 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 | |||
483 | err: | 492 | err: |
484 | mutex_unlock(&priv->lock); | 493 | mutex_unlock(&priv->lock); |
485 | return error; | 494 | return cache; |
486 | } | 495 | } |
487 | 496 | ||
488 | static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, | 497 | static void |
489 | struct sh_mobile_meram_cfg *cfg) | 498 | sh_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 | ||
513 | static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, | 515 | static void |
514 | struct sh_mobile_meram_cfg *cfg, | 516 | sh_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 | ||
537 | static int sh_mobile_meram_runtime_suspend(struct device *dev) | 531 | static 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 | |||
542 | static 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 | ||
562 | static int sh_mobile_meram_runtime_resume(struct device *dev) | 566 | static 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 | ||
583 | static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = { | 585 | static 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 | |||
588 | static 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 | ||
599 | static int sh_mobile_meram_remove(struct platform_device *pdev); | ||
600 | |||
601 | static int __devinit sh_mobile_meram_probe(struct platform_device *pdev) | 593 | static 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 | ||
649 | err: | 677 | err_genpool: |
650 | sh_mobile_meram_remove(pdev); | 678 | if (priv->pool) |
679 | gen_pool_destroy(priv->pool); | ||
680 | iounmap(priv->base); | ||
681 | err_ioremap: | ||
682 | release_mem_region(meram->start, resource_size(meram)); | ||
683 | err_req_meram: | ||
684 | release_mem_region(regs->start, resource_size(regs)); | ||
685 | err_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: | |||
656 | static int sh_mobile_meram_remove(struct platform_device *pdev) | 693 | static 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 | ||