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