blob: 91cc2afbe14e23bb25948fee7dc093c678a31683 [file] [log] [blame]
Jim Pariscffb4add2009-01-06 11:32:10 +00001/**
2 * ps3vram - Use extra PS3 video ram as MTD block device.
3 *
4 * Copyright (c) 2007-2008 Jim Paris <jim@jtan.com>
5 * Added support RSX DMA Vivien Chappelier <vivien.chappelier@free.fr>
6 */
7
8#include <linux/io.h>
9#include <linux/init.h>
10#include <linux/kernel.h>
11#include <linux/list.h>
12#include <linux/module.h>
13#include <linux/moduleparam.h>
14#include <linux/slab.h>
15#include <linux/version.h>
16#include <linux/gfp.h>
17#include <linux/delay.h>
18#include <linux/mtd/mtd.h>
19
20#include <asm/lv1call.h>
21#include <asm/ps3.h>
22
23#define DEVICE_NAME "ps3vram"
24
25#define XDR_BUF_SIZE (2 * 1024 * 1024) /* XDR buffer (must be 1MiB aligned) */
26#define XDR_IOIF 0x0c000000
27
28#define FIFO_BASE XDR_IOIF
29#define FIFO_SIZE (64 * 1024)
30
31#define DMA_PAGE_SIZE (4 * 1024)
32
33#define CACHE_PAGE_SIZE (256 * 1024)
34#define CACHE_PAGE_COUNT ((XDR_BUF_SIZE - FIFO_SIZE) / CACHE_PAGE_SIZE)
35
36#define CACHE_OFFSET CACHE_PAGE_SIZE
37#define FIFO_OFFSET 0
38
39#define CTRL_PUT 0x10
40#define CTRL_GET 0x11
41#define CTRL_TOP 0x15
42
43#define UPLOAD_SUBCH 1
44#define DOWNLOAD_SUBCH 2
45
46#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c
47#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104
48
49#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601
50
51struct mtd_info ps3vram_mtd;
52
53#define CACHE_PAGE_PRESENT 1
54#define CACHE_PAGE_DIRTY 2
55
Jim Pariscffb4add2009-01-06 11:32:10 +000056struct ps3vram_tag {
57 unsigned int address;
58 unsigned int flags;
59};
60
61struct ps3vram_cache {
62 unsigned int page_count;
63 unsigned int page_size;
64 struct ps3vram_tag *tags;
65};
66
67struct ps3vram_priv {
Geoff Levand993e62e2009-01-06 11:32:28 +000068 u64 memory_handle;
69 u64 context_handle;
70 u32 *ctrl;
71 u32 *reports;
72 u8 *base;
73 u8 *xdr_buf;
Jim Pariscffb4add2009-01-06 11:32:10 +000074
Geoff Levand993e62e2009-01-06 11:32:28 +000075 u32 *fifo_base;
76 u32 *fifo_ptr;
Jim Pariscffb4add2009-01-06 11:32:10 +000077
Geoff Levandf259d74e2009-01-06 11:32:21 +000078 struct device *dev;
Jim Pariscffb4add2009-01-06 11:32:10 +000079 struct ps3vram_cache cache;
80
81 /* Used to serialize cache/DMA operations */
82 struct mutex lock;
83};
84
85#define DMA_NOTIFIER_HANDLE_BASE 0x66604200 /* first DMA notifier handle */
86#define DMA_NOTIFIER_OFFSET_BASE 0x1000 /* first DMA notifier offset */
87#define DMA_NOTIFIER_SIZE 0x40
Jim Pariscffb4add2009-01-06 11:32:10 +000088#define NOTIFIER 7 /* notifier used for completion report */
89
90/* A trailing '-' means to subtract off ps3fb_videomemory.size */
91char *size = "256M-";
92module_param(size, charp, 0);
93MODULE_PARM_DESC(size, "memory size");
94
Geoff Levand993e62e2009-01-06 11:32:28 +000095static u32 *ps3vram_get_notifier(u32 *reports, int notifier)
Jim Pariscffb4add2009-01-06 11:32:10 +000096{
97 return (void *) reports +
98 DMA_NOTIFIER_OFFSET_BASE +
99 DMA_NOTIFIER_SIZE * notifier;
100}
101
102static void ps3vram_notifier_reset(struct mtd_info *mtd)
103{
104 int i;
Geoff Levand993e62e2009-01-06 11:32:28 +0000105
Jim Pariscffb4add2009-01-06 11:32:10 +0000106 struct ps3vram_priv *priv = mtd->priv;
Geoff Levand993e62e2009-01-06 11:32:28 +0000107 u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
Jim Pariscffb4add2009-01-06 11:32:10 +0000108 for (i = 0; i < 4; i++)
109 notify[i] = 0xffffffff;
110}
111
112static int ps3vram_notifier_wait(struct mtd_info *mtd, int timeout_ms)
113{
114 struct ps3vram_priv *priv = mtd->priv;
Geoff Levand993e62e2009-01-06 11:32:28 +0000115 u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
Jim Pariscffb4add2009-01-06 11:32:10 +0000116
117 timeout_ms *= 1000;
118
119 do {
120 if (notify[3] == 0)
121 return 0;
122
123 if (timeout_ms)
124 udelay(1);
125 } while (timeout_ms--);
126
127 return -1;
128}
129
Jim Pariscffb4add2009-01-06 11:32:10 +0000130static void ps3vram_init_ring(struct mtd_info *mtd)
131{
132 struct ps3vram_priv *priv = mtd->priv;
133
134 priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
135 priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET;
136}
137
138static int ps3vram_wait_ring(struct mtd_info *mtd, int timeout)
139{
140 struct ps3vram_priv *priv = mtd->priv;
141
142 /* wait until setup commands are processed */
143 timeout *= 1000;
144 while (--timeout) {
145 if (priv->ctrl[CTRL_PUT] == priv->ctrl[CTRL_GET])
146 break;
147 udelay(1);
148 }
149 if (timeout == 0) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000150 dev_dbg(priv->dev, "%s:%d: FIFO timeout (%08x/%08x/%08x)\n",
151 __func__, __LINE__, priv->ctrl[CTRL_PUT],
152 priv->ctrl[CTRL_GET], priv->ctrl[CTRL_TOP]);
Jim Pariscffb4add2009-01-06 11:32:10 +0000153 return -ETIMEDOUT;
154 }
155
156 return 0;
157}
158
Geoff Levand993e62e2009-01-06 11:32:28 +0000159static void ps3vram_out_ring(struct ps3vram_priv *priv, u32 data)
Jim Pariscffb4add2009-01-06 11:32:10 +0000160{
161 *(priv->fifo_ptr)++ = data;
162}
163
Geoff Levand993e62e2009-01-06 11:32:28 +0000164static void ps3vram_begin_ring(struct ps3vram_priv *priv, u32 chan,
165 u32 tag, u32 size)
Jim Pariscffb4add2009-01-06 11:32:10 +0000166{
167 ps3vram_out_ring(priv, (size << 18) | (chan << 13) | tag);
168}
169
170static void ps3vram_rewind_ring(struct mtd_info *mtd)
171{
172 struct ps3vram_priv *priv = mtd->priv;
173 u64 status;
174
175 ps3vram_out_ring(priv, 0x20000000 | (FIFO_BASE + FIFO_OFFSET));
176
177 priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
178
179 /* asking the HV for a blit will kick the fifo */
180 status = lv1_gpu_context_attribute(priv->context_handle,
181 L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
182 0, 0, 0, 0);
183 if (status)
Geoff Levandf259d74e2009-01-06 11:32:21 +0000184 dev_err(priv->dev, "%s:%d: lv1_gpu_context_attribute failed\n",
185 __func__, __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000186
187 priv->fifo_ptr = priv->fifo_base;
188}
189
190static void ps3vram_fire_ring(struct mtd_info *mtd)
191{
192 struct ps3vram_priv *priv = mtd->priv;
193 u64 status;
194
195 mutex_lock(&ps3_gpu_mutex);
196
197 priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET +
Geoff Levand993e62e2009-01-06 11:32:28 +0000198 (priv->fifo_ptr - priv->fifo_base) * sizeof(u32);
Jim Pariscffb4add2009-01-06 11:32:10 +0000199
200 /* asking the HV for a blit will kick the fifo */
201 status = lv1_gpu_context_attribute(priv->context_handle,
202 L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
203 0, 0, 0, 0);
204 if (status)
Geoff Levandf259d74e2009-01-06 11:32:21 +0000205 dev_err(priv->dev, "%s:%d: lv1_gpu_context_attribute failed\n",
206 __func__, __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000207
Geoff Levand993e62e2009-01-06 11:32:28 +0000208 if ((priv->fifo_ptr - priv->fifo_base) * sizeof(u32) >
209 FIFO_SIZE - 1024) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000210 dev_dbg(priv->dev, "%s:%d: fifo full, rewinding\n", __func__,
211 __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000212 ps3vram_wait_ring(mtd, 200);
213 ps3vram_rewind_ring(mtd);
214 }
215
216 mutex_unlock(&ps3_gpu_mutex);
217}
218
219static void ps3vram_bind(struct mtd_info *mtd)
220{
221 struct ps3vram_priv *priv = mtd->priv;
222
223 ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0, 1);
224 ps3vram_out_ring(priv, 0x31337303);
225 ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x180, 3);
226 ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER);
227 ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */
228 ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */
229
230 ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0, 1);
231 ps3vram_out_ring(priv, 0x3137c0de);
232 ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x180, 3);
233 ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER);
234 ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */
235 ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */
236
237 ps3vram_fire_ring(mtd);
238}
239
240static int ps3vram_upload(struct mtd_info *mtd, unsigned int src_offset,
241 unsigned int dst_offset, int len, int count)
242{
243 struct ps3vram_priv *priv = mtd->priv;
244
245 ps3vram_begin_ring(priv, UPLOAD_SUBCH,
246 NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
247 ps3vram_out_ring(priv, XDR_IOIF + src_offset);
248 ps3vram_out_ring(priv, dst_offset);
249 ps3vram_out_ring(priv, len);
250 ps3vram_out_ring(priv, len);
251 ps3vram_out_ring(priv, len);
252 ps3vram_out_ring(priv, count);
253 ps3vram_out_ring(priv, (1 << 8) | 1);
254 ps3vram_out_ring(priv, 0);
255
256 ps3vram_notifier_reset(mtd);
257 ps3vram_begin_ring(priv, UPLOAD_SUBCH,
258 NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
259 ps3vram_out_ring(priv, 0);
260 ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x100, 1);
261 ps3vram_out_ring(priv, 0);
262 ps3vram_fire_ring(mtd);
263 if (ps3vram_notifier_wait(mtd, 200) < 0) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000264 dev_dbg(priv->dev, "%s:%d: notifier timeout\n", __func__,
265 __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000266 return -1;
267 }
268
269 return 0;
270}
271
272static int ps3vram_download(struct mtd_info *mtd, unsigned int src_offset,
273 unsigned int dst_offset, int len, int count)
274{
275 struct ps3vram_priv *priv = mtd->priv;
276
277 ps3vram_begin_ring(priv, DOWNLOAD_SUBCH,
278 NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
279 ps3vram_out_ring(priv, src_offset);
280 ps3vram_out_ring(priv, XDR_IOIF + dst_offset);
281 ps3vram_out_ring(priv, len);
282 ps3vram_out_ring(priv, len);
283 ps3vram_out_ring(priv, len);
284 ps3vram_out_ring(priv, count);
285 ps3vram_out_ring(priv, (1 << 8) | 1);
286 ps3vram_out_ring(priv, 0);
287
288 ps3vram_notifier_reset(mtd);
289 ps3vram_begin_ring(priv, DOWNLOAD_SUBCH,
290 NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
291 ps3vram_out_ring(priv, 0);
292 ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x100, 1);
293 ps3vram_out_ring(priv, 0);
294 ps3vram_fire_ring(mtd);
295 if (ps3vram_notifier_wait(mtd, 200) < 0) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000296 dev_dbg(priv->dev, "%s:%d: notifier timeout\n", __func__,
297 __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000298 return -1;
299 }
300
301 return 0;
302}
303
304static void ps3vram_cache_evict(struct mtd_info *mtd, int entry)
305{
306 struct ps3vram_priv *priv = mtd->priv;
307 struct ps3vram_cache *cache = &priv->cache;
308
309 if (cache->tags[entry].flags & CACHE_PAGE_DIRTY) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000310 dev_dbg(priv->dev, "%s:%d: flushing %d : 0x%08x\n", __func__,
311 __LINE__, entry, cache->tags[entry].address);
Jim Pariscffb4add2009-01-06 11:32:10 +0000312 if (ps3vram_upload(mtd,
313 CACHE_OFFSET + entry * cache->page_size,
314 cache->tags[entry].address,
315 DMA_PAGE_SIZE,
316 cache->page_size / DMA_PAGE_SIZE) < 0) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000317 dev_dbg(priv->dev, "%s:%d: failed to upload from "
318 "0x%x to 0x%x size 0x%x\n", __func__, __LINE__,
319 entry * cache->page_size,
320 cache->tags[entry].address, cache->page_size);
Jim Pariscffb4add2009-01-06 11:32:10 +0000321 }
322 cache->tags[entry].flags &= ~CACHE_PAGE_DIRTY;
323 }
324}
325
326static void ps3vram_cache_load(struct mtd_info *mtd, int entry,
327 unsigned int address)
328{
329 struct ps3vram_priv *priv = mtd->priv;
330 struct ps3vram_cache *cache = &priv->cache;
331
Geoff Levandf259d74e2009-01-06 11:32:21 +0000332 dev_dbg(priv->dev, "%s:%d: fetching %d : 0x%08x\n", __func__, __LINE__,
333 entry, address);
Jim Pariscffb4add2009-01-06 11:32:10 +0000334 if (ps3vram_download(mtd,
335 address,
336 CACHE_OFFSET + entry * cache->page_size,
337 DMA_PAGE_SIZE,
338 cache->page_size / DMA_PAGE_SIZE) < 0) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000339 dev_err(priv->dev, "%s:%d: failed to download from "
340 "0x%x to 0x%x size 0x%x\n", __func__, __LINE__, address,
341 entry * cache->page_size, cache->page_size);
Jim Pariscffb4add2009-01-06 11:32:10 +0000342 }
343
344 cache->tags[entry].address = address;
345 cache->tags[entry].flags |= CACHE_PAGE_PRESENT;
346}
347
348
349static void ps3vram_cache_flush(struct mtd_info *mtd)
350{
351 struct ps3vram_priv *priv = mtd->priv;
352 struct ps3vram_cache *cache = &priv->cache;
353 int i;
354
Geoff Levandf259d74e2009-01-06 11:32:21 +0000355 dev_dbg(priv->dev, "%s:%d: FLUSH\n", __func__, __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000356 for (i = 0; i < cache->page_count; i++) {
357 ps3vram_cache_evict(mtd, i);
358 cache->tags[i].flags = 0;
359 }
360}
361
362static unsigned int ps3vram_cache_match(struct mtd_info *mtd, loff_t address)
363{
364 struct ps3vram_priv *priv = mtd->priv;
365 struct ps3vram_cache *cache = &priv->cache;
366 unsigned int base;
367 unsigned int offset;
368 int i;
369 static int counter;
370
371 offset = (unsigned int) (address & (cache->page_size - 1));
372 base = (unsigned int) (address - offset);
373
374 /* fully associative check */
375 for (i = 0; i < cache->page_count; i++) {
376 if ((cache->tags[i].flags & CACHE_PAGE_PRESENT) &&
377 cache->tags[i].address == base) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000378 dev_dbg(priv->dev, "%s:%d: found entry %d : 0x%08x\n",
379 __func__, __LINE__, i, cache->tags[i].address);
Jim Pariscffb4add2009-01-06 11:32:10 +0000380 return i;
381 }
382 }
383
384 /* choose a random entry */
385 i = (jiffies + (counter++)) % cache->page_count;
Geoff Levandf259d74e2009-01-06 11:32:21 +0000386 dev_dbg(priv->dev, "%s:%d: using entry %d\n", __func__, __LINE__, i);
Jim Pariscffb4add2009-01-06 11:32:10 +0000387
388 ps3vram_cache_evict(mtd, i);
389 ps3vram_cache_load(mtd, i, base);
390
391 return i;
392}
393
394static int ps3vram_cache_init(struct mtd_info *mtd)
395{
396 struct ps3vram_priv *priv = mtd->priv;
397
Jim Pariscffb4add2009-01-06 11:32:10 +0000398 priv->cache.page_count = CACHE_PAGE_COUNT;
399 priv->cache.page_size = CACHE_PAGE_SIZE;
400 priv->cache.tags = kzalloc(sizeof(struct ps3vram_tag) *
401 CACHE_PAGE_COUNT, GFP_KERNEL);
402 if (priv->cache.tags == NULL) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000403 dev_err(priv->dev, "%s:%d: could not allocate cache tags\n",
404 __func__, __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000405 return -ENOMEM;
406 }
407
Geoff Levandf259d74e2009-01-06 11:32:21 +0000408 dev_info(priv->dev, "created ram cache: %d entries, %d KiB each\n",
409 CACHE_PAGE_COUNT, CACHE_PAGE_SIZE / 1024);
410
Jim Pariscffb4add2009-01-06 11:32:10 +0000411 return 0;
412}
413
414static void ps3vram_cache_cleanup(struct mtd_info *mtd)
415{
416 struct ps3vram_priv *priv = mtd->priv;
417
418 ps3vram_cache_flush(mtd);
419 kfree(priv->cache.tags);
420}
421
422static int ps3vram_erase(struct mtd_info *mtd, struct erase_info *instr)
423{
424 struct ps3vram_priv *priv = mtd->priv;
425
426 if (instr->addr + instr->len > mtd->size)
427 return -EINVAL;
428
429 mutex_lock(&priv->lock);
430
431 ps3vram_cache_flush(mtd);
432
433 /* Set bytes to 0xFF */
434 memset(priv->base + instr->addr, 0xFF, instr->len);
435
436 mutex_unlock(&priv->lock);
437
438 instr->state = MTD_ERASE_DONE;
439 mtd_erase_callback(instr);
440
441 return 0;
442}
443
Jim Pariscffb4add2009-01-06 11:32:10 +0000444static int ps3vram_read(struct mtd_info *mtd, loff_t from, size_t len,
445 size_t *retlen, u_char *buf)
446{
447 struct ps3vram_priv *priv = mtd->priv;
448 unsigned int cached, count;
449
Geoff Levandf259d74e2009-01-06 11:32:21 +0000450 dev_dbg(priv->dev, "%s:%d: from=0x%08x len=0x%zx\n", __func__, __LINE__,
451 (unsigned int)from, len);
Jim Pariscffb4add2009-01-06 11:32:10 +0000452
453 if (from >= mtd->size)
454 return -EINVAL;
455
456 if (len > mtd->size - from)
457 len = mtd->size - from;
458
459 /* Copy from vram to buf */
460 count = len;
461 while (count) {
462 unsigned int offset, avail;
463 unsigned int entry;
464
465 offset = (unsigned int) (from & (priv->cache.page_size - 1));
466 avail = priv->cache.page_size - offset;
467
468 mutex_lock(&priv->lock);
469
470 entry = ps3vram_cache_match(mtd, from);
471 cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
472
Geoff Levandf259d74e2009-01-06 11:32:21 +0000473 dev_dbg(priv->dev, "%s:%d: from=%08x cached=%08x offset=%08x "
474 "avail=%08x count=%08x\n", __func__, __LINE__,
475 (unsigned int)from, cached, offset, avail, count);
Jim Pariscffb4add2009-01-06 11:32:10 +0000476
477 if (avail > count)
478 avail = count;
479 memcpy(buf, priv->xdr_buf + cached, avail);
480
481 mutex_unlock(&priv->lock);
482
483 buf += avail;
484 count -= avail;
485 from += avail;
486 }
487
488 *retlen = len;
489 return 0;
490}
491
492static int ps3vram_write(struct mtd_info *mtd, loff_t to, size_t len,
493 size_t *retlen, const u_char *buf)
494{
495 struct ps3vram_priv *priv = mtd->priv;
496 unsigned int cached, count;
497
498 if (to >= mtd->size)
499 return -EINVAL;
500
501 if (len > mtd->size - to)
502 len = mtd->size - to;
503
504 /* Copy from buf to vram */
505 count = len;
506 while (count) {
507 unsigned int offset, avail;
508 unsigned int entry;
509
510 offset = (unsigned int) (to & (priv->cache.page_size - 1));
511 avail = priv->cache.page_size - offset;
512
513 mutex_lock(&priv->lock);
514
515 entry = ps3vram_cache_match(mtd, to);
516 cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
517
Geoff Levandf259d74e2009-01-06 11:32:21 +0000518 dev_dbg(priv->dev, "%s:%d: to=%08x cached=%08x offset=%08x "
519 "avail=%08x count=%08x\n", __func__, __LINE__,
520 (unsigned int)to, cached, offset, avail, count);
Jim Pariscffb4add2009-01-06 11:32:10 +0000521
522 if (avail > count)
523 avail = count;
524 memcpy(priv->xdr_buf + cached, buf, avail);
525
526 priv->cache.tags[entry].flags |= CACHE_PAGE_DIRTY;
527
528 mutex_unlock(&priv->lock);
529
530 buf += avail;
531 count -= avail;
532 to += avail;
533 }
534
535 *retlen = len;
536 return 0;
537}
538
539static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
540{
541 struct ps3vram_priv *priv;
Geoff Levand993e62e2009-01-06 11:32:28 +0000542 int status;
543 u64 ddr_lpar;
544 u64 ctrl_lpar;
545 u64 info_lpar;
546 u64 reports_lpar;
547 u64 ddr_size;
548 u64 reports_size;
Jim Pariscffb4add2009-01-06 11:32:10 +0000549 int ret = -ENOMEM;
550 char *rest;
551
552 ret = -EIO;
553 ps3vram_mtd.priv = kzalloc(sizeof(struct ps3vram_priv), GFP_KERNEL);
554 if (!ps3vram_mtd.priv)
555 goto out;
556 priv = ps3vram_mtd.priv;
557
558 mutex_init(&priv->lock);
Geoff Levandf259d74e2009-01-06 11:32:21 +0000559 priv->dev = &dev->core;
Jim Pariscffb4add2009-01-06 11:32:10 +0000560
561 /* Allocate XDR buffer (1MiB aligned) */
Geoff Levand993e62e2009-01-06 11:32:28 +0000562 priv->xdr_buf = (void *)__get_free_pages(GFP_KERNEL,
563 get_order(XDR_BUF_SIZE));
Jim Pariscffb4add2009-01-06 11:32:10 +0000564 if (priv->xdr_buf == NULL) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000565 dev_dbg(&dev->core, "%s:%d: could not allocate XDR buffer\n",
566 __func__, __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000567 ret = -ENOMEM;
568 goto out_free_priv;
569 }
570
571 /* Put FIFO at begginning of XDR buffer */
Geoff Levand993e62e2009-01-06 11:32:28 +0000572 priv->fifo_base = (u32 *) (priv->xdr_buf + FIFO_OFFSET);
Jim Pariscffb4add2009-01-06 11:32:10 +0000573 priv->fifo_ptr = priv->fifo_base;
574
575 /* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */
576 if (ps3_open_hv_device(dev)) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000577 dev_err(&dev->core, "%s:%d: ps3_open_hv_device failed\n",
578 __func__, __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000579 ret = -EAGAIN;
580 goto out_close_gpu;
581 }
582
583 /* Request memory */
584 status = -1;
585 ddr_size = memparse(size, &rest);
586 if (*rest == '-')
587 ddr_size -= ps3fb_videomemory.size;
588 ddr_size = ALIGN(ddr_size, 1024*1024);
589 if (ddr_size <= 0) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000590 dev_err(&dev->core, "%s:%d: specified size is too small\n",
591 __func__, __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000592 ret = -EINVAL;
593 goto out_close_gpu;
594 }
595
596 while (ddr_size > 0) {
597 status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0,
598 &priv->memory_handle,
599 &ddr_lpar);
Geoff Levand993e62e2009-01-06 11:32:28 +0000600 if (!status)
Jim Pariscffb4add2009-01-06 11:32:10 +0000601 break;
602 ddr_size -= 1024*1024;
603 }
Geoff Levand993e62e2009-01-06 11:32:28 +0000604 if (status || ddr_size <= 0) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000605 dev_err(&dev->core, "%s:%d: lv1_gpu_memory_allocate failed\n",
606 __func__, __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000607 ret = -ENOMEM;
608 goto out_free_xdr_buf;
609 }
Jim Pariscffb4add2009-01-06 11:32:10 +0000610
611 /* Request context */
612 status = lv1_gpu_context_allocate(priv->memory_handle,
613 0,
614 &priv->context_handle,
615 &ctrl_lpar,
616 &info_lpar,
617 &reports_lpar,
618 &reports_size);
619 if (status) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000620 dev_err(&dev->core, "%s:%d: lv1_gpu_context_allocate failed\n",
621 __func__, __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000622 ret = -ENOMEM;
623 goto out_free_memory;
624 }
625
626 /* Map XDR buffer to RSX */
627 status = lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF,
628 ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)),
629 XDR_BUF_SIZE, 0);
630 if (status) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000631 dev_err(&dev->core, "%s:%d: lv1_gpu_context_iomap failed\n",
632 __func__, __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000633 ret = -ENOMEM;
634 goto out_free_context;
635 }
636
637 priv->base = ioremap(ddr_lpar, ddr_size);
638 if (!priv->base) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000639 dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__,
640 __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000641 ret = -ENOMEM;
642 goto out_free_context;
643 }
644
645 priv->ctrl = ioremap(ctrl_lpar, 64 * 1024);
646 if (!priv->ctrl) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000647 dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__,
648 __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000649 ret = -ENOMEM;
650 goto out_unmap_vram;
651 }
652
653 priv->reports = ioremap(reports_lpar, reports_size);
654 if (!priv->reports) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000655 dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__,
656 __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000657 ret = -ENOMEM;
658 goto out_unmap_ctrl;
659 }
660
661 mutex_lock(&ps3_gpu_mutex);
662 ps3vram_init_ring(&ps3vram_mtd);
663 mutex_unlock(&ps3_gpu_mutex);
664
665 ps3vram_mtd.name = "ps3vram";
666 ps3vram_mtd.size = ddr_size;
667 ps3vram_mtd.flags = MTD_CAP_RAM;
668 ps3vram_mtd.erase = ps3vram_erase;
669 ps3vram_mtd.point = NULL;
670 ps3vram_mtd.unpoint = NULL;
671 ps3vram_mtd.read = ps3vram_read;
672 ps3vram_mtd.write = ps3vram_write;
673 ps3vram_mtd.owner = THIS_MODULE;
674 ps3vram_mtd.type = MTD_RAM;
675 ps3vram_mtd.erasesize = CACHE_PAGE_SIZE;
676 ps3vram_mtd.writesize = 1;
677
678 ps3vram_bind(&ps3vram_mtd);
679
680 mutex_lock(&ps3_gpu_mutex);
681 ret = ps3vram_wait_ring(&ps3vram_mtd, 100);
682 mutex_unlock(&ps3_gpu_mutex);
683 if (ret < 0) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000684 dev_err(&dev->core, "%s:%d: failed to initialize channels\n",
685 __func__, __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000686 ret = -ETIMEDOUT;
687 goto out_unmap_reports;
688 }
689
690 ps3vram_cache_init(&ps3vram_mtd);
691
692 if (add_mtd_device(&ps3vram_mtd)) {
Geoff Levandf259d74e2009-01-06 11:32:21 +0000693 dev_err(&dev->core, "%s:%d: add_mtd_device failed\n",
694 __func__, __LINE__);
Jim Pariscffb4add2009-01-06 11:32:10 +0000695 ret = -EAGAIN;
696 goto out_cache_cleanup;
697 }
698
Geoff Levandf259d74e2009-01-06 11:32:21 +0000699 dev_info(&dev->core, "reserved %u MiB of gpu memory\n",
700 (unsigned int)(ddr_size / 1024 / 1024));
701
Jim Pariscffb4add2009-01-06 11:32:10 +0000702 return 0;
703
704out_cache_cleanup:
705 ps3vram_cache_cleanup(&ps3vram_mtd);
706out_unmap_reports:
707 iounmap(priv->reports);
708out_unmap_ctrl:
709 iounmap(priv->ctrl);
710out_unmap_vram:
711 iounmap(priv->base);
712out_free_context:
713 lv1_gpu_context_free(priv->context_handle);
714out_free_memory:
715 lv1_gpu_memory_free(priv->memory_handle);
716out_close_gpu:
717 ps3_close_hv_device(dev);
718out_free_xdr_buf:
719 free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
720out_free_priv:
721 kfree(ps3vram_mtd.priv);
722 ps3vram_mtd.priv = NULL;
723out:
724 return ret;
725}
726
727static int ps3vram_shutdown(struct ps3_system_bus_device *dev)
728{
729 struct ps3vram_priv *priv;
730
731 priv = ps3vram_mtd.priv;
732
733 del_mtd_device(&ps3vram_mtd);
734 ps3vram_cache_cleanup(&ps3vram_mtd);
735 iounmap(priv->reports);
736 iounmap(priv->ctrl);
737 iounmap(priv->base);
738 lv1_gpu_context_free(priv->context_handle);
739 lv1_gpu_memory_free(priv->memory_handle);
740 ps3_close_hv_device(dev);
741 free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
742 kfree(priv);
743 return 0;
744}
745
746static struct ps3_system_bus_driver ps3vram_driver = {
747 .match_id = PS3_MATCH_ID_GPU,
748 .match_sub_id = PS3_MATCH_SUB_ID_GPU_RAMDISK,
749 .core.name = DEVICE_NAME,
750 .core.owner = THIS_MODULE,
751 .probe = ps3vram_probe,
752 .remove = ps3vram_shutdown,
753 .shutdown = ps3vram_shutdown,
754};
755
756static int __init ps3vram_init(void)
757{
758 return ps3_system_bus_driver_register(&ps3vram_driver);
759}
760
761static void __exit ps3vram_exit(void)
762{
763 ps3_system_bus_driver_unregister(&ps3vram_driver);
764}
765
766module_init(ps3vram_init);
767module_exit(ps3vram_exit);
768
769MODULE_LICENSE("GPL");
770MODULE_AUTHOR("Jim Paris <jim@jtan.com>");
771MODULE_DESCRIPTION("MTD driver for PS3 video RAM");
Geert Uytterhoeven0a2d15b2009-01-06 11:32:03 +0000772MODULE_ALIAS(PS3_MODULE_ALIAS_GPU_RAMDISK);