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