1/* 2 * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com 3 * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 */ 19 20#include <linux/kernel.h> 21 22#include "solo6010.h" 23 24// #define SOLO_TEST_P2M 25 26int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr, 27 void *sys_addr, u32 ext_addr, u32 size) 28{ 29 dma_addr_t dma_addr; 30 int ret; 31 32 WARN_ON(!size); 33 WARN_ON(id >= SOLO_NR_P2M); 34 if (!size || id >= SOLO_NR_P2M) 35 return -EINVAL; 36 37 dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, 38 wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); 39 40 ret = solo_p2m_dma_t(solo_dev, id, wr, dma_addr, ext_addr, size); 41 42 pci_unmap_single(solo_dev->pdev, dma_addr, size, 43 wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); 44 45 return ret; 46} 47 48int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr, 49 dma_addr_t dma_addr, u32 ext_addr, u32 size) 50{ 51 struct solo_p2m_dev *p2m_dev; 52 unsigned int timeout = 0; 53 54 WARN_ON(!size); 55 WARN_ON(id >= SOLO_NR_P2M); 56 if (!size || id >= SOLO_NR_P2M) 57 return -EINVAL; 58 59 p2m_dev = &solo_dev->p2m_dev[id]; 60 61 down(&p2m_dev->sem); 62 63start_dma: 64 INIT_COMPLETION(p2m_dev->completion); 65 p2m_dev->error = 0; 66 solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(id), dma_addr); 67 solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(id), ext_addr); 68 solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(id), 69 SOLO_P2M_COPY_SIZE(size >> 2)); 70 solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 71 SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) | 72 (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON); 73 74 timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ); 75 76 solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); 77 78 if (p2m_dev->error) 79 goto start_dma; 80 81 up(&p2m_dev->sem); 82 83 return (timeout == 0) ? -EAGAIN : 0; 84} 85 86#ifdef SOLO_TEST_P2M 87 88#define P2M_TEST_CHAR 0xbe 89 90static unsigned long long p2m_test(struct solo6010_dev *solo_dev, u8 id, 91 u32 base, int size) 92{ 93 u8 *wr_buf; 94 u8 *rd_buf; 95 int i; 96 unsigned long long err_cnt = 0; 97 98 wr_buf = kmalloc(size, GFP_KERNEL); 99 if (!wr_buf) { 100 printk(SOLO6010_NAME ": Failed to malloc for p2m_test\n"); 101 return size; 102 } 103 104 rd_buf = kmalloc(size, GFP_KERNEL); 105 if (!rd_buf) { 106 printk(SOLO6010_NAME ": Failed to malloc for p2m_test\n"); 107 kfree(wr_buf); 108 return size; 109 } 110 111 memset(wr_buf, P2M_TEST_CHAR, size); 112 memset(rd_buf, P2M_TEST_CHAR + 1, size); 113 114 solo_p2m_dma(solo_dev, id, 1, wr_buf, base, size); 115 solo_p2m_dma(solo_dev, id, 0, rd_buf, base, size); 116 117 for (i = 0; i < size; i++) 118 if (wr_buf[i] != rd_buf[i]) 119 err_cnt++; 120 121 kfree(wr_buf); 122 kfree(rd_buf); 123 124 return err_cnt; 125} 126 127#define TEST_CHUNK_SIZE (8 * 1024) 128 129static void run_p2m_test(struct solo6010_dev *solo_dev) 130{ 131 unsigned long long errs = 0; 132 u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev); 133 int i, d; 134 135 printk(KERN_WARNING "%s: Testing %u bytes of external ram\n", 136 SOLO6010_NAME, size); 137 138 for (i = 0; i < size; i += TEST_CHUNK_SIZE) 139 for (d = 0; d < 4; d++) 140 errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE); 141 142 printk(KERN_WARNING "%s: Found %llu errors during p2m test\n", 143 SOLO6010_NAME, errs); 144 145 return; 146} 147#else 148#define run_p2m_test(__solo) do{}while(0) 149#endif 150 151void solo_p2m_isr(struct solo6010_dev *solo_dev, int id) 152{ 153 solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_P2M(id)); 154 complete(&solo_dev->p2m_dev[id].completion); 155} 156 157void solo_p2m_error_isr(struct solo6010_dev *solo_dev, u32 status) 158{ 159 struct solo_p2m_dev *p2m_dev; 160 int i; 161 162 if (!(status & SOLO_PCI_ERR_P2M)) 163 return; 164 165 for (i = 0; i < SOLO_NR_P2M; i++) { 166 p2m_dev = &solo_dev->p2m_dev[i]; 167 p2m_dev->error = 1; 168 solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); 169 complete(&p2m_dev->completion); 170 } 171} 172 173void solo_p2m_exit(struct solo6010_dev *solo_dev) 174{ 175 int i; 176 177 for (i = 0; i < SOLO_NR_P2M; i++) 178 solo6010_irq_off(solo_dev, SOLO_IRQ_P2M(i)); 179} 180 181int solo_p2m_init(struct solo6010_dev *solo_dev) 182{ 183 struct solo_p2m_dev *p2m_dev; 184 int i; 185 186 for (i = 0; i < SOLO_NR_P2M; i++) { 187 p2m_dev = &solo_dev->p2m_dev[i]; 188 189 init_MUTEX(&p2m_dev->sem); 190 init_completion(&p2m_dev->completion); 191 192 solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(i), 193 __pa(p2m_dev->desc)); 194 195 solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); 196 solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), 197 SOLO_P2M_CSC_16BIT_565 | 198 SOLO_P2M_DMA_INTERVAL(0) | 199 SOLO_P2M_PCI_MASTER_MODE); 200 solo6010_irq_on(solo_dev, SOLO_IRQ_P2M(i)); 201 } 202 203 run_p2m_test(solo_dev); 204 205 return 0; 206} 207