drm/radeon/kms: enable use of unmappable VRAM V2
[pandora-kernel.git] / drivers / staging / crystalhd / crystalhd_misc.c
1 /***************************************************************************
2  *         Copyright (c) 2005-2009, Broadcom Corporation.
3  *
4  *  Name: crystalhd_misc . c
5  *
6  *  Description:
7  *              BCM70012 Linux driver misc routines.
8  *
9  *  HISTORY:
10  *
11  **********************************************************************
12  * This file is part of the crystalhd device driver.
13  *
14  * This driver is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation, version 2 of the License.
17  *
18  * This driver is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this driver.  If not, see <http://www.gnu.org/licenses/>.
25  **********************************************************************/
26
27 #include "crystalhd_misc.h"
28 #include "crystalhd_lnx.h"
29
30 uint32_t g_linklog_level;
31
32 static inline uint32_t crystalhd_dram_rd(struct crystalhd_adp *adp, uint32_t mem_off)
33 {
34         crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19));
35         return bc_dec_reg_rd(adp, (0x00380000 | (mem_off & 0x0007FFFF)));
36 }
37
38 static inline void crystalhd_dram_wr(struct crystalhd_adp *adp, uint32_t mem_off, uint32_t val)
39 {
40         crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19));
41         bc_dec_reg_wr(adp, (0x00380000 | (mem_off & 0x0007FFFF)), val);
42 }
43
44 static inline BC_STATUS bc_chk_dram_range(struct crystalhd_adp *adp, uint32_t start_off, uint32_t cnt)
45 {
46         return BC_STS_SUCCESS;
47 }
48
49 static crystalhd_dio_req *crystalhd_alloc_dio(struct crystalhd_adp *adp)
50 {
51         unsigned long flags = 0;
52         crystalhd_dio_req *temp = NULL;
53
54         if (!adp) {
55                 BCMLOG_ERR("Invalid Arg!!\n");
56                 return temp;
57         }
58
59         spin_lock_irqsave(&adp->lock, flags);
60         temp = adp->ua_map_free_head;
61         if (temp)
62                 adp->ua_map_free_head = adp->ua_map_free_head->next;
63         spin_unlock_irqrestore(&adp->lock, flags);
64
65         return temp;
66 }
67
68 static void crystalhd_free_dio(struct crystalhd_adp *adp, crystalhd_dio_req *dio)
69 {
70         unsigned long flags = 0;
71
72         if (!adp || !dio)
73                 return;
74         spin_lock_irqsave(&adp->lock, flags);
75         dio->sig = crystalhd_dio_inv;
76         dio->page_cnt = 0;
77         dio->fb_size = 0;
78         memset(&dio->uinfo, 0, sizeof(dio->uinfo));
79         dio->next = adp->ua_map_free_head;
80         adp->ua_map_free_head = dio;
81         spin_unlock_irqrestore(&adp->lock, flags);
82 }
83
84 static crystalhd_elem_t *crystalhd_alloc_elem(struct crystalhd_adp *adp)
85 {
86         unsigned long flags = 0;
87         crystalhd_elem_t *temp = NULL;
88
89         if (!adp)
90                 return temp;
91         spin_lock_irqsave(&adp->lock, flags);
92         temp = adp->elem_pool_head;
93         if (temp) {
94                 adp->elem_pool_head = adp->elem_pool_head->flink;
95                 memset(temp, 0, sizeof(*temp));
96         }
97         spin_unlock_irqrestore(&adp->lock, flags);
98
99         return temp;
100 }
101 static void crystalhd_free_elem(struct crystalhd_adp *adp, crystalhd_elem_t *elem)
102 {
103         unsigned long flags = 0;
104
105         if (!adp || !elem)
106                 return;
107         spin_lock_irqsave(&adp->lock, flags);
108         elem->flink = adp->elem_pool_head;
109         adp->elem_pool_head = elem;
110         spin_unlock_irqrestore(&adp->lock, flags);
111 }
112
113 static inline void crystalhd_set_sg(struct scatterlist *sg, struct page *page,
114                                   unsigned int len, unsigned int offset)
115 {
116         sg_set_page(sg, page, len, offset);
117 #ifdef CONFIG_X86_64
118         sg->dma_length = len;
119 #endif
120 }
121
122 static inline void crystalhd_init_sg(struct scatterlist *sg, unsigned int entries)
123 {
124         /* http://lkml.org/lkml/2007/11/27/68 */
125         sg_init_table(sg, entries);
126 }
127
128 /*========================== Extern ========================================*/
129 /**
130  * bc_dec_reg_rd - Read 7412's device register.
131  * @adp: Adapter instance
132  * @reg_off: Register offset.
133  *
134  * Return:
135  *      32bit value read
136  *
137  * 7412's device register read routine. This interface use
138  * 7412's device access range mapped from BAR-2 (4M) of PCIe
139  * configuration space.
140  */
141 uint32_t bc_dec_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
142 {
143         if (!adp || (reg_off > adp->pci_mem_len)) {
144                 BCMLOG_ERR("dec_rd_reg_off outof range: 0x%08x\n", reg_off);
145                 return 0;
146         }
147
148         return readl(adp->addr + reg_off);
149 }
150
151 /**
152  * bc_dec_reg_wr - Write 7412's device register
153  * @adp: Adapter instance
154  * @reg_off: Register offset.
155  * @val: Dword value to be written.
156  *
157  * Return:
158  *      none.
159  *
160  * 7412's device register write routine. This interface use
161  * 7412's device access range mapped from BAR-2 (4M) of PCIe
162  * configuration space.
163  */
164 void bc_dec_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val)
165 {
166         if (!adp || (reg_off > adp->pci_mem_len)) {
167                 BCMLOG_ERR("dec_wr_reg_off outof range: 0x%08x\n", reg_off);
168                 return;
169         }
170         writel(val, adp->addr + reg_off);
171         udelay(8);
172 }
173
174 /**
175  * crystalhd_reg_rd - Read Link's device register.
176  * @adp: Adapter instance
177  * @reg_off: Register offset.
178  *
179  * Return:
180  *      32bit value read
181  *
182  * Link device register  read routine. This interface use
183  * Link's device access range mapped from BAR-1 (64K) of PCIe
184  * configuration space.
185  *
186  */
187 uint32_t crystalhd_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
188 {
189         if (!adp || (reg_off > adp->pci_i2o_len)) {
190                 BCMLOG_ERR("link_rd_reg_off outof range: 0x%08x\n", reg_off);
191                 return 0;
192         }
193         return readl(adp->i2o_addr + reg_off);
194 }
195
196 /**
197  * crystalhd_reg_wr - Write Link's device register
198  * @adp: Adapter instance
199  * @reg_off: Register offset.
200  * @val: Dword value to be written.
201  *
202  * Return:
203  *      none.
204  *
205  * Link device register  write routine. This interface use
206  * Link's device access range mapped from BAR-1 (64K) of PCIe
207  * configuration space.
208  *
209  */
210 void crystalhd_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val)
211 {
212         if (!adp || (reg_off > adp->pci_i2o_len)) {
213                 BCMLOG_ERR("link_wr_reg_off outof range: 0x%08x\n", reg_off);
214                 return;
215         }
216         writel(val, adp->i2o_addr + reg_off);
217 }
218
219 /**
220  * crystalhd_mem_rd - Read data from 7412's DRAM area.
221  * @adp: Adapter instance
222  * @start_off: Start offset.
223  * @dw_cnt: Count in dwords.
224  * @rd_buff: Buffer to copy the data from dram.
225  *
226  * Return:
227  *      Status.
228  *
229  * 7412's Dram read routine.
230  */
231 BC_STATUS crystalhd_mem_rd(struct crystalhd_adp *adp, uint32_t start_off,
232                          uint32_t dw_cnt, uint32_t *rd_buff)
233 {
234         uint32_t ix = 0;
235
236         if (!adp || !rd_buff ||
237             (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
238                 BCMLOG_ERR("Invalid arg \n");
239                 return BC_STS_INV_ARG;
240         }
241         for (ix = 0; ix < dw_cnt; ix++)
242                 rd_buff[ix] = crystalhd_dram_rd(adp, (start_off + (ix * 4)));
243
244         return BC_STS_SUCCESS;
245 }
246
247 /**
248  * crystalhd_mem_wr - Write data to 7412's DRAM area.
249  * @adp: Adapter instance
250  * @start_off: Start offset.
251  * @dw_cnt: Count in dwords.
252  * @wr_buff: Data Buffer to be written.
253  *
254  * Return:
255  *      Status.
256  *
257  * 7412's Dram write routine.
258  */
259 BC_STATUS crystalhd_mem_wr(struct crystalhd_adp *adp, uint32_t start_off,
260                          uint32_t dw_cnt, uint32_t *wr_buff)
261 {
262         uint32_t ix = 0;
263
264         if (!adp || !wr_buff ||
265             (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
266                 BCMLOG_ERR("Invalid arg \n");
267                 return BC_STS_INV_ARG;
268         }
269
270         for (ix = 0; ix < dw_cnt; ix++)
271                 crystalhd_dram_wr(adp, (start_off + (ix * 4)), wr_buff[ix]);
272
273         return BC_STS_SUCCESS;
274 }
275 /**
276  * crystalhd_pci_cfg_rd - PCIe config read
277  * @adp: Adapter instance
278  * @off: PCI config space offset.
279  * @len: Size -- Byte, Word & dword.
280  * @val: Value read
281  *
282  * Return:
283  *      Status.
284  *
285  * Get value from Link's PCIe config space.
286  */
287 BC_STATUS crystalhd_pci_cfg_rd(struct crystalhd_adp *adp, uint32_t off,
288                              uint32_t len, uint32_t *val)
289 {
290         BC_STATUS sts = BC_STS_SUCCESS;
291         int rc = 0;
292
293         if (!adp || !val) {
294                 BCMLOG_ERR("Invalid arg \n");
295                 return BC_STS_INV_ARG;
296         }
297
298         switch (len) {
299         case 1:
300                 rc = pci_read_config_byte(adp->pdev, off, (u8 *)val);
301                 break;
302         case 2:
303                 rc = pci_read_config_word(adp->pdev, off, (u16 *)val);
304                 break;
305         case 4:
306                 rc = pci_read_config_dword(adp->pdev, off, (u32 *)val);
307                 break;
308         default:
309                 rc = -EINVAL;
310                 sts = BC_STS_INV_ARG;
311                 BCMLOG_ERR("Invalid len:%d\n", len);
312         };
313
314         if (rc && (sts == BC_STS_SUCCESS))
315                 sts = BC_STS_ERROR;
316
317         return sts;
318 }
319
320 /**
321  * crystalhd_pci_cfg_wr - PCIe config write
322  * @adp: Adapter instance
323  * @off: PCI config space offset.
324  * @len: Size -- Byte, Word & dword.
325  * @val: Value to be written
326  *
327  * Return:
328  *      Status.
329  *
330  * Set value to Link's PCIe config space.
331  */
332 BC_STATUS crystalhd_pci_cfg_wr(struct crystalhd_adp *adp, uint32_t off,
333                              uint32_t len, uint32_t val)
334 {
335         BC_STATUS sts = BC_STS_SUCCESS;
336         int rc = 0;
337
338         if (!adp || !val) {
339                 BCMLOG_ERR("Invalid arg \n");
340                 return BC_STS_INV_ARG;
341         }
342
343         switch (len) {
344         case 1:
345                 rc = pci_write_config_byte(adp->pdev, off, (u8)val);
346                 break;
347         case 2:
348                 rc = pci_write_config_word(adp->pdev, off, (u16)val);
349                 break;
350         case 4:
351                 rc = pci_write_config_dword(adp->pdev, off, val);
352                 break;
353         default:
354                 rc = -EINVAL;
355                 sts = BC_STS_INV_ARG;
356                 BCMLOG_ERR("Invalid len:%d\n", len);
357         };
358
359         if (rc && (sts == BC_STS_SUCCESS))
360                 sts = BC_STS_ERROR;
361
362         return sts;
363 }
364
365 /**
366  * bc_kern_dma_alloc - Allocate memory for Dma rings
367  * @adp: Adapter instance
368  * @sz: Size of the memory to allocate.
369  * @phy_addr: Physical address of the memory allocated.
370  *         Typedef to system's dma_addr_t (u64)
371  *
372  * Return:
373  *  Pointer to allocated memory..
374  *
375  * Wrapper to Linux kernel interface.
376  *
377  */
378 void *bc_kern_dma_alloc(struct crystalhd_adp *adp, uint32_t sz,
379                         dma_addr_t *phy_addr)
380 {
381         void *temp = NULL;
382
383         if (!adp || !sz || !phy_addr) {
384                 BCMLOG_ERR("Invalide Arg..\n");
385                 return temp;
386         }
387
388         temp = pci_alloc_consistent(adp->pdev, sz, phy_addr);
389         if (temp)
390                 memset(temp, 0, sz);
391
392         return temp;
393 }
394
395 /**
396  * bc_kern_dma_free - Release Dma ring memory.
397  * @adp: Adapter instance
398  * @sz: Size of the memory to allocate.
399  * @ka: Kernel virtual address returned during _dio_alloc()
400  * @phy_addr: Physical address of the memory allocated.
401  *         Typedef to system's dma_addr_t (u64)
402  *
403  * Return:
404  *     none.
405  */
406 void bc_kern_dma_free(struct crystalhd_adp *adp, uint32_t sz, void *ka,
407                       dma_addr_t phy_addr)
408 {
409         if (!adp || !ka || !sz || !phy_addr) {
410                 BCMLOG_ERR("Invalide Arg..\n");
411                 return;
412         }
413
414         pci_free_consistent(adp->pdev, sz, ka, phy_addr);
415 }
416
417 /**
418  * crystalhd_create_dioq - Create Generic DIO queue
419  * @adp: Adapter instance
420  * @dioq_hnd: Handle to the dio queue created
421  * @cb  : Optional - Call back To free the element.
422  * @cbctx: Context to pass to callback.
423  *
424  * Return:
425  *  status
426  *
427  * Initialize Generic DIO queue to hold any data. Callback
428  * will be used to free elements while deleting the queue.
429  */
430 BC_STATUS crystalhd_create_dioq(struct crystalhd_adp *adp,
431                               crystalhd_dioq_t **dioq_hnd,
432                               crystalhd_data_free_cb cb, void *cbctx)
433 {
434         crystalhd_dioq_t *dioq = NULL;
435
436         if (!adp || !dioq_hnd) {
437                 BCMLOG_ERR("Invalid arg!!\n");
438                 return BC_STS_INV_ARG;
439         }
440
441         dioq = kzalloc(sizeof(*dioq), GFP_KERNEL);
442         if (!dioq)
443                 return BC_STS_INSUFF_RES;
444
445         spin_lock_init(&dioq->lock);
446         dioq->sig = BC_LINK_DIOQ_SIG;
447         dioq->head = (crystalhd_elem_t *)&dioq->head;
448         dioq->tail = (crystalhd_elem_t *)&dioq->head;
449         crystalhd_create_event(&dioq->event);
450         dioq->adp = adp;
451         dioq->data_rel_cb = cb;
452         dioq->cb_context = cbctx;
453         *dioq_hnd = dioq;
454
455         return BC_STS_SUCCESS;
456 }
457
458 /**
459  * crystalhd_delete_dioq - Delete Generic DIO queue
460  * @adp: Adapter instance
461  * @dioq: DIOQ instance..
462  *
463  * Return:
464  *  None.
465  *
466  * Release Generic DIO queue. This function will remove
467  * all the entries from the Queue and will release data
468  * by calling the call back provided during creation.
469  *
470  */
471 void crystalhd_delete_dioq(struct crystalhd_adp *adp, crystalhd_dioq_t *dioq)
472 {
473         void *temp;
474
475         if (!dioq || (dioq->sig != BC_LINK_DIOQ_SIG))
476                 return;
477
478         do {
479                 temp = crystalhd_dioq_fetch(dioq);
480                 if (temp && dioq->data_rel_cb)
481                         dioq->data_rel_cb(dioq->cb_context, temp);
482         } while (temp);
483         dioq->sig = 0;
484         kfree(dioq);
485 }
486
487 /**
488  * crystalhd_dioq_add - Add new DIO request element.
489  * @ioq: DIO queue instance
490  * @t: DIO request to be added.
491  * @wake: True - Wake up suspended process.
492  * @tag: Special tag to assign - For search and get.
493  *
494  * Return:
495  *  Status.
496  *
497  * Insert new element to Q tail.
498  */
499 BC_STATUS crystalhd_dioq_add(crystalhd_dioq_t *ioq, void *data,
500                            bool wake, uint32_t tag)
501 {
502         unsigned long flags = 0;
503         crystalhd_elem_t *tmp;
504
505         if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !data) {
506                 BCMLOG_ERR("Invalid arg!!\n");
507                 return BC_STS_INV_ARG;
508         }
509
510         tmp = crystalhd_alloc_elem(ioq->adp);
511         if (!tmp) {
512                 BCMLOG_ERR("No free elements.\n");
513                 return BC_STS_INSUFF_RES;
514         }
515
516         tmp->data = data;
517         tmp->tag = tag;
518         spin_lock_irqsave(&ioq->lock, flags);
519         tmp->flink = (crystalhd_elem_t *)&ioq->head;
520         tmp->blink = ioq->tail;
521         tmp->flink->blink = tmp;
522         tmp->blink->flink = tmp;
523         ioq->count++;
524         spin_unlock_irqrestore(&ioq->lock, flags);
525
526         if (wake)
527                 crystalhd_set_event(&ioq->event);
528
529         return BC_STS_SUCCESS;
530 }
531
532 /**
533  * crystalhd_dioq_fetch - Fetch element from head.
534  * @ioq: DIO queue instance
535  *
536  * Return:
537  *      data element from the head..
538  *
539  * Remove an element from Queue.
540  */
541 void *crystalhd_dioq_fetch(crystalhd_dioq_t *ioq)
542 {
543         unsigned long flags = 0;
544         crystalhd_elem_t *tmp;
545         crystalhd_elem_t *ret = NULL;
546         void *data = NULL;
547
548         if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
549                 BCMLOG_ERR("Invalid arg!!\n");
550                 return data;
551         }
552
553         spin_lock_irqsave(&ioq->lock, flags);
554         tmp = ioq->head;
555         if (tmp != (crystalhd_elem_t *)&ioq->head) {
556                 ret = tmp;
557                 tmp->flink->blink = tmp->blink;
558                 tmp->blink->flink = tmp->flink;
559                 ioq->count--;
560         }
561         spin_unlock_irqrestore(&ioq->lock, flags);
562         if (ret) {
563                 data = ret->data;
564                 crystalhd_free_elem(ioq->adp, ret);
565         }
566
567         return data;
568 }
569 /**
570  * crystalhd_dioq_find_and_fetch - Search the tag and Fetch element
571  * @ioq: DIO queue instance
572  * @tag: Tag to search for.
573  *
574  * Return:
575  *      element from the head..
576  *
577  * Search TAG and remove the element.
578  */
579 void *crystalhd_dioq_find_and_fetch(crystalhd_dioq_t *ioq, uint32_t tag)
580 {
581         unsigned long flags = 0;
582         crystalhd_elem_t *tmp;
583         crystalhd_elem_t *ret = NULL;
584         void *data = NULL;
585
586         if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
587                 BCMLOG_ERR("Invalid arg!!\n");
588                 return data;
589         }
590
591         spin_lock_irqsave(&ioq->lock, flags);
592         tmp = ioq->head;
593         while (tmp != (crystalhd_elem_t *)&ioq->head) {
594                 if (tmp->tag == tag) {
595                         ret = tmp;
596                         tmp->flink->blink = tmp->blink;
597                         tmp->blink->flink = tmp->flink;
598                         ioq->count--;
599                         break;
600                 }
601                 tmp = tmp->flink;
602         }
603         spin_unlock_irqrestore(&ioq->lock, flags);
604
605         if (ret) {
606                 data = ret->data;
607                 crystalhd_free_elem(ioq->adp, ret);
608         }
609
610         return data;
611 }
612
613 /**
614  * crystalhd_dioq_fetch_wait - Fetch element from Head.
615  * @ioq: DIO queue instance
616  * @to_secs: Wait timeout in seconds..
617  *
618  * Return:
619  *      element from the head..
620  *
621  * Return element from head if Q is not empty. Wait for new element
622  * if Q is empty for Timeout seconds.
623  */
624 void *crystalhd_dioq_fetch_wait(crystalhd_dioq_t *ioq, uint32_t to_secs,
625                               uint32_t *sig_pend)
626 {
627         unsigned long flags = 0;
628         int rc = 0, count;
629         void *tmp = NULL;
630
631         if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !to_secs || !sig_pend) {
632                 BCMLOG_ERR("Invalid arg!!\n");
633                 return tmp;
634         }
635
636         count = to_secs;
637         spin_lock_irqsave(&ioq->lock, flags);
638         while ((ioq->count == 0) && count) {
639                 spin_unlock_irqrestore(&ioq->lock, flags);
640
641                 crystalhd_wait_on_event(&ioq->event, (ioq->count > 0), 1000, rc, 0);
642                 if (rc == 0) {
643                         goto out;
644                 } else if (rc == -EINTR) {
645                         BCMLOG(BCMLOG_INFO, "Cancelling fetch wait\n");
646                         *sig_pend = 1;
647                         return tmp;
648                 }
649                 spin_lock_irqsave(&ioq->lock, flags);
650                 count--;
651         }
652         spin_unlock_irqrestore(&ioq->lock, flags);
653
654 out:
655         return crystalhd_dioq_fetch(ioq);
656 }
657
658 /**
659  * crystalhd_map_dio - Map user address for DMA
660  * @adp:        Adapter instance
661  * @ubuff:      User buffer to map.
662  * @ubuff_sz:   User buffer size.
663  * @uv_offset:  UV buffer offset.
664  * @en_422mode: TRUE:422 FALSE:420 Capture mode.
665  * @dir_tx:     TRUE for Tx (To device from host)
666  * @dio_hnd:    Handle to mapped DIO request.
667  *
668  * Return:
669  *      Status.
670  *
671  * This routine maps user address and lock pages for DMA.
672  *
673  */
674 BC_STATUS crystalhd_map_dio(struct crystalhd_adp *adp, void *ubuff,
675                           uint32_t ubuff_sz, uint32_t uv_offset,
676                           bool en_422mode, bool dir_tx,
677                           crystalhd_dio_req **dio_hnd)
678 {
679         crystalhd_dio_req       *dio;
680         /* FIXME: jarod: should some of these unsigned longs be uint32_t or uintptr_t? */
681         unsigned long start = 0, end = 0, uaddr = 0, count = 0;
682         unsigned long spsz = 0, uv_start = 0;
683         int i = 0, rw = 0, res = 0, nr_pages = 0, skip_fb_sg = 0;
684
685         if (!adp || !ubuff || !ubuff_sz || !dio_hnd) {
686                 BCMLOG_ERR("Invalid arg \n");
687                 return BC_STS_INV_ARG;
688         }
689         /* Compute pages */
690         uaddr = (unsigned long)ubuff;
691         count = (unsigned long)ubuff_sz;
692         end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
693         start = uaddr >> PAGE_SHIFT;
694         nr_pages = end - start;
695
696         if (!count || ((uaddr + count) < uaddr)) {
697                 BCMLOG_ERR("User addr overflow!!\n");
698                 return BC_STS_INV_ARG;
699         }
700
701         dio = crystalhd_alloc_dio(adp);
702         if (!dio) {
703                 BCMLOG_ERR("dio pool empty..\n");
704                 return BC_STS_INSUFF_RES;
705         }
706
707         if (dir_tx) {
708                 rw = WRITE;
709                 dio->direction = DMA_TO_DEVICE;
710         } else {
711                 rw = READ;
712                 dio->direction = DMA_FROM_DEVICE;
713         }
714
715         if (nr_pages > dio->max_pages) {
716                 BCMLOG_ERR("max_pages(%d) exceeded(%d)!!\n",
717                            dio->max_pages, nr_pages);
718                 crystalhd_unmap_dio(adp, dio);
719                 return BC_STS_INSUFF_RES;
720         }
721
722         if (uv_offset) {
723                 uv_start = (uaddr + (unsigned long)uv_offset)  >> PAGE_SHIFT;
724                 dio->uinfo.uv_sg_ix = uv_start - start;
725                 dio->uinfo.uv_sg_off = ((uaddr + (unsigned long)uv_offset) & ~PAGE_MASK);
726         }
727
728         dio->fb_size = ubuff_sz & 0x03;
729         if (dio->fb_size) {
730                 res = copy_from_user(dio->fb_va,
731                                      (void *)(uaddr + count - dio->fb_size),
732                                      dio->fb_size);
733                 if (res) {
734                         BCMLOG_ERR("failed %d to copy %u fill bytes from %p\n",
735                                    res, dio->fb_size,
736                                    (void *)(uaddr + count-dio->fb_size));
737                         crystalhd_unmap_dio(adp, dio);
738                         return BC_STS_INSUFF_RES;
739                 }
740         }
741
742         down_read(&current->mm->mmap_sem);
743         res = get_user_pages(current, current->mm, uaddr, nr_pages, rw == READ,
744                              0, dio->pages, NULL);
745         up_read(&current->mm->mmap_sem);
746
747         /* Save for release..*/
748         dio->sig = crystalhd_dio_locked;
749         if (res < nr_pages) {
750                 BCMLOG_ERR("get pages failed: %d-%d\n", nr_pages, res);
751                 dio->page_cnt = res;
752                 crystalhd_unmap_dio(adp, dio);
753                 return BC_STS_ERROR;
754         }
755
756         dio->page_cnt = nr_pages;
757         /* Get scatter/gather */
758         crystalhd_init_sg(dio->sg, dio->page_cnt);
759         crystalhd_set_sg(&dio->sg[0], dio->pages[0], 0, uaddr & ~PAGE_MASK);
760         if (nr_pages > 1) {
761                 dio->sg[0].length = PAGE_SIZE - dio->sg[0].offset;
762
763 #ifdef CONFIG_X86_64
764                 dio->sg[0].dma_length = dio->sg[0].length;
765 #endif
766                 count -= dio->sg[0].length;
767                 for (i = 1; i < nr_pages; i++) {
768                         if (count < 4) {
769                                 spsz = count;
770                                 skip_fb_sg = 1;
771                         } else {
772                                 spsz = (count < PAGE_SIZE) ?
773                                         (count & ~0x03) : PAGE_SIZE;
774                         }
775                         crystalhd_set_sg(&dio->sg[i], dio->pages[i], spsz, 0);
776                         count -= spsz;
777                 }
778         } else {
779                 if (count < 4) {
780                         dio->sg[0].length = count;
781                         skip_fb_sg = 1;
782                 } else {
783                         dio->sg[0].length = count - dio->fb_size;
784                 }
785 #ifdef CONFIG_X86_64
786                 dio->sg[0].dma_length = dio->sg[0].length;
787 #endif
788         }
789         dio->sg_cnt = pci_map_sg(adp->pdev, dio->sg,
790                                  dio->page_cnt, dio->direction);
791         if (dio->sg_cnt <= 0) {
792                 BCMLOG_ERR("sg map %d-%d \n", dio->sg_cnt, dio->page_cnt);
793                 crystalhd_unmap_dio(adp, dio);
794                 return BC_STS_ERROR;
795         }
796         if (dio->sg_cnt && skip_fb_sg)
797                 dio->sg_cnt -= 1;
798         dio->sig = crystalhd_dio_sg_mapped;
799         /* Fill in User info.. */
800         dio->uinfo.xfr_len   = ubuff_sz;
801         dio->uinfo.xfr_buff  = ubuff;
802         dio->uinfo.uv_offset = uv_offset;
803         dio->uinfo.b422mode  = en_422mode;
804         dio->uinfo.dir_tx    = dir_tx;
805
806         *dio_hnd = dio;
807
808         return BC_STS_SUCCESS;
809 }
810
811 /**
812  * crystalhd_unmap_sgl - Release mapped resources
813  * @adp: Adapter instance
814  * @dio: DIO request instance
815  *
816  * Return:
817  *      Status.
818  *
819  * This routine is to unmap the user buffer pages.
820  */
821 BC_STATUS crystalhd_unmap_dio(struct crystalhd_adp *adp, crystalhd_dio_req *dio)
822 {
823         struct page *page = NULL;
824         int j = 0;
825
826         if (!adp || !dio) {
827                 BCMLOG_ERR("Invalid arg \n");
828                 return BC_STS_INV_ARG;
829         }
830
831         if ((dio->page_cnt > 0) && (dio->sig != crystalhd_dio_inv)) {
832                 for (j = 0; j < dio->page_cnt; j++) {
833                         page = dio->pages[j];
834                         if (page) {
835                                 if (!PageReserved(page) &&
836                                     (dio->direction == DMA_FROM_DEVICE))
837                                         SetPageDirty(page);
838                                 page_cache_release(page);
839                         }
840                 }
841         }
842         if (dio->sig == crystalhd_dio_sg_mapped)
843                 pci_unmap_sg(adp->pdev, dio->sg, dio->page_cnt, dio->direction);
844
845         crystalhd_free_dio(adp, dio);
846
847         return BC_STS_SUCCESS;
848 }
849
850 /**
851  * crystalhd_create_dio_pool - Allocate mem pool for DIO management.
852  * @adp: Adapter instance
853  * @max_pages: Max pages for size calculation.
854  *
855  * Return:
856  *      system error.
857  *
858  * This routine creates a memory pool to hold dio context for
859  * for HW Direct IO operation.
860  */
861 int crystalhd_create_dio_pool(struct crystalhd_adp *adp, uint32_t max_pages)
862 {
863         uint32_t asz = 0, i = 0;
864         uint8_t *temp;
865         crystalhd_dio_req *dio;
866
867         if (!adp || !max_pages) {
868                 BCMLOG_ERR("Invalid Arg!!\n");
869                 return -EINVAL;
870         }
871
872         /* Get dma memory for fill byte handling..*/
873         adp->fill_byte_pool = pci_pool_create("crystalhd_fbyte",
874                                               adp->pdev, 8, 8, 0);
875         if (!adp->fill_byte_pool) {
876                 BCMLOG_ERR("failed to create fill byte pool\n");
877                 return -ENOMEM;
878         }
879
880         /* Get the max size from user based on 420/422 modes */
881         asz =  (sizeof(*dio->pages) * max_pages) +
882                (sizeof(*dio->sg) * max_pages) + sizeof(*dio);
883
884         BCMLOG(BCMLOG_DBG, "Initializing Dio pool %d %d %x %p\n",
885                BC_LINK_SG_POOL_SZ, max_pages, asz, adp->fill_byte_pool);
886
887         for (i = 0; i < BC_LINK_SG_POOL_SZ; i++) {
888                 temp = (uint8_t *)kzalloc(asz, GFP_KERNEL);
889                 if ((temp) == NULL) {
890                         BCMLOG_ERR("Failed to alloc %d mem\n", asz);
891                         return -ENOMEM;
892                 }
893
894                 dio = (crystalhd_dio_req *)temp;
895                 temp += sizeof(*dio);
896                 dio->pages = (struct page **)temp;
897                 temp += (sizeof(*dio->pages) * max_pages);
898                 dio->sg = (struct scatterlist *)temp;
899                 dio->max_pages = max_pages;
900                 dio->fb_va = pci_pool_alloc(adp->fill_byte_pool, GFP_KERNEL,
901                                             &dio->fb_pa);
902                 if (!dio->fb_va) {
903                         BCMLOG_ERR("fill byte alloc failed.\n");
904                         return -ENOMEM;
905                 }
906
907                 crystalhd_free_dio(adp, dio);
908         }
909
910         return 0;
911 }
912
913 /**
914  * crystalhd_destroy_dio_pool - Release DIO mem pool.
915  * @adp: Adapter instance
916  *
917  * Return:
918  *      none.
919  *
920  * This routine releases dio memory pool during close.
921  */
922 void crystalhd_destroy_dio_pool(struct crystalhd_adp *adp)
923 {
924         crystalhd_dio_req *dio;
925         int count = 0;
926
927         if (!adp) {
928                 BCMLOG_ERR("Invalid Arg!!\n");
929                 return;
930         }
931
932         do {
933                 dio = crystalhd_alloc_dio(adp);
934                 if (dio) {
935                         if (dio->fb_va)
936                                 pci_pool_free(adp->fill_byte_pool,
937                                               dio->fb_va, dio->fb_pa);
938                         count++;
939                         kfree(dio);
940                 }
941         } while (dio);
942
943         if (adp->fill_byte_pool) {
944                 pci_pool_destroy(adp->fill_byte_pool);
945                 adp->fill_byte_pool = NULL;
946         }
947
948         BCMLOG(BCMLOG_DBG, "Released dio pool %d \n", count);
949 }
950
951 /**
952  * crystalhd_create_elem_pool - List element pool creation.
953  * @adp: Adapter instance
954  * @pool_size: Number of elements in the pool.
955  *
956  * Return:
957  *      0 - success, <0 error
958  *
959  * Create general purpose list element pool to hold pending,
960  * and active requests.
961  */
962 int __devinit crystalhd_create_elem_pool(struct crystalhd_adp *adp,
963                 uint32_t pool_size)
964 {
965         uint32_t i;
966         crystalhd_elem_t *temp;
967
968         if (!adp || !pool_size)
969                 return -EINVAL;
970
971         for (i = 0; i < pool_size; i++) {
972                 temp = kzalloc(sizeof(*temp), GFP_KERNEL);
973                 if (!temp) {
974                         BCMLOG_ERR("kalloc failed \n");
975                         return -ENOMEM;
976                 }
977                 crystalhd_free_elem(adp, temp);
978         }
979         BCMLOG(BCMLOG_DBG, "allocated %d elem\n", pool_size);
980         return 0;
981 }
982
983 /**
984  * crystalhd_delete_elem_pool - List element pool deletion.
985  * @adp: Adapter instance
986  *
987  * Return:
988  *      none
989  *
990  * Delete general purpose list element pool.
991  */
992 void crystalhd_delete_elem_pool(struct crystalhd_adp *adp)
993 {
994         crystalhd_elem_t *temp;
995         int dbg_cnt = 0;
996
997         if (!adp)
998                 return;
999
1000         do {
1001                 temp = crystalhd_alloc_elem(adp);
1002                 if (temp) {
1003                         kfree(temp);
1004                         dbg_cnt++;
1005                 }
1006         } while (temp);
1007
1008         BCMLOG(BCMLOG_DBG, "released %d elem\n", dbg_cnt);
1009 }
1010
1011 /*================ Debug support routines.. ================================*/
1012 void crystalhd_show_buffer(uint32_t off, uint8_t *buff, uint32_t dwcount)
1013 {
1014         uint32_t i, k = 1;
1015
1016         for (i = 0; i < dwcount; i++) {
1017                 if (k == 1)
1018                         BCMLOG(BCMLOG_DATA, "0x%08X : ", off);
1019
1020                 BCMLOG(BCMLOG_DATA, " 0x%08X ", *((uint32_t *)buff));
1021
1022                 buff += sizeof(uint32_t);
1023                 off  += sizeof(uint32_t);
1024                 k++;
1025                 if ((i == dwcount - 1) || (k > 4)) {
1026                         BCMLOG(BCMLOG_DATA, "\n");
1027                         k = 1;
1028                 }
1029         }
1030 }