Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | // SPDX-License-Identifier: GPL-2.0-or-later /* * tw68_risc.c * Part of the device driver for Techwell 68xx based cards * * Much of this code is derived from the cx88 and sa7134 drivers, which * were in turn derived from the bt87x driver. The original work was by * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab, * Hans Verkuil, Andy Walls and many others. Their work is gratefully * acknowledged. Full credit goes to them - any problems within this code * are mine. * * Copyright (C) 2009 William M. Brack * * Refactored and updated to the latest v4l core frameworks: * * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl> */ #include "tw68.h" /** * tw68_risc_field * @rp: pointer to current risc program position * @sglist: pointer to "scatter-gather list" of buffer pointers * @offset: offset to target memory buffer * @sync_line: 0 -> no sync, 1 -> odd sync, 2 -> even sync * @bpl: number of bytes per scan line * @padding: number of bytes of padding to add * @lines: number of lines in field * @jump: insert a jump at the start */ static __le32 *tw68_risc_field(__le32 *rp, struct scatterlist *sglist, unsigned int offset, u32 sync_line, unsigned int bpl, unsigned int padding, unsigned int lines, bool jump) { struct scatterlist *sg; unsigned int line, todo, done; if (jump) { *(rp++) = cpu_to_le32(RISC_JUMP); *(rp++) = 0; } /* sync instruction */ if (sync_line == 1) *(rp++) = cpu_to_le32(RISC_SYNCO); else *(rp++) = cpu_to_le32(RISC_SYNCE); *(rp++) = 0; /* scan lines */ sg = sglist; for (line = 0; line < lines; line++) { /* calculate next starting position */ while (offset && offset >= sg_dma_len(sg)) { offset -= sg_dma_len(sg); sg = sg_next(sg); } if (bpl <= sg_dma_len(sg) - offset) { /* fits into current chunk */ *(rp++) = cpu_to_le32(RISC_LINESTART | /* (offset<<12) |*/ bpl); *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); offset += bpl; } else { /* * scanline needs to be split. Put the start in * whatever memory remains using RISC_LINESTART, * then the remainder into following addresses * given by the scatter-gather list. */ todo = bpl; /* one full line to be done */ /* first fragment */ done = (sg_dma_len(sg) - offset); *(rp++) = cpu_to_le32(RISC_LINESTART | (7 << 24) | done); *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); todo -= done; sg = sg_next(sg); /* succeeding fragments have no offset */ while (todo > sg_dma_len(sg)) { *(rp++) = cpu_to_le32(RISC_INLINE | (done << 12) | sg_dma_len(sg)); *(rp++) = cpu_to_le32(sg_dma_address(sg)); todo -= sg_dma_len(sg); sg = sg_next(sg); done += sg_dma_len(sg); } if (todo) { /* final chunk - offset 0, count 'todo' */ *(rp++) = cpu_to_le32(RISC_INLINE | (done << 12) | todo); *(rp++) = cpu_to_le32(sg_dma_address(sg)); } offset = todo; } offset += padding; } return rp; } /** * tw68_risc_buffer * * This routine is called by tw68-video. It allocates * memory for the dma controller "program" and then fills in that * memory with the appropriate "instructions". * * @pci: structure with info about the pci * slot which our device is in. * @buf: structure with info about the memory * used for our controller program. * @sglist: scatter-gather list entry * @top_offset: offset within the risc program area for the * first odd frame line * @bottom_offset: offset within the risc program area for the * first even frame line * @bpl: number of data bytes per scan line * @padding: number of extra bytes to add at end of line * @lines: number of scan lines */ int tw68_risc_buffer(struct pci_dev *pci, struct tw68_buf *buf, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines) { u32 instructions, fields; __le32 *rp; fields = 0; if (UNSET != top_offset) fields++; if (UNSET != bottom_offset) fields++; /* * estimate risc mem: worst case is one write per page border + * one write per scan line + syncs + 2 jumps (all 2 dwords). * Padding can cause next bpl to start close to a page border. * First DMA region may be smaller than PAGE_SIZE */ instructions = fields * (1 + (((bpl + padding) * lines) / PAGE_SIZE) + lines) + 4; buf->size = instructions * 8; buf->cpu = dma_alloc_coherent(&pci->dev, buf->size, &buf->dma, GFP_KERNEL); if (buf->cpu == NULL) return -ENOMEM; /* write risc instructions */ rp = buf->cpu; if (UNSET != top_offset) /* generates SYNCO */ rp = tw68_risc_field(rp, sglist, top_offset, 1, bpl, padding, lines, true); if (UNSET != bottom_offset) /* generates SYNCE */ rp = tw68_risc_field(rp, sglist, bottom_offset, 2, bpl, padding, lines, top_offset == UNSET); /* save pointer to jmp instruction address */ buf->jmp = rp; buf->cpu[1] = cpu_to_le32(buf->dma + 8); /* assure risc buffer hasn't overflowed */ BUG_ON((buf->jmp - buf->cpu + 2) * sizeof(buf->cpu[0]) > buf->size); return 0; } #if 0 /* ------------------------------------------------------------------ */ /* debug helper code */ static void tw68_risc_decode(u32 risc, u32 addr) { #define RISC_OP(reg) (((reg) >> 28) & 7) static struct instr_details { char *name; u8 has_data_type; u8 has_byte_info; u8 has_addr; } instr[8] = { [RISC_OP(RISC_SYNCO)] = {"syncOdd", 0, 0, 0}, [RISC_OP(RISC_SYNCE)] = {"syncEven", 0, 0, 0}, [RISC_OP(RISC_JUMP)] = {"jump", 0, 0, 1}, [RISC_OP(RISC_LINESTART)] = {"lineStart", 1, 1, 1}, [RISC_OP(RISC_INLINE)] = {"inline", 1, 1, 1}, }; u32 p; p = RISC_OP(risc); if (!(risc & 0x80000000) || !instr[p].name) { pr_debug("0x%08x [ INVALID ]\n", risc); return; } pr_debug("0x%08x %-9s IRQ=%d", risc, instr[p].name, (risc >> 27) & 1); if (instr[p].has_data_type) pr_debug(" Type=%d", (risc >> 24) & 7); if (instr[p].has_byte_info) pr_debug(" Start=0x%03x Count=%03u", (risc >> 12) & 0xfff, risc & 0xfff); if (instr[p].has_addr) pr_debug(" StartAddr=0x%08x", addr); pr_debug("\n"); } void tw68_risc_program_dump(struct tw68_core *core, struct tw68_buf *buf) { const __le32 *addr; pr_debug("%s: risc_program_dump: risc=%p, buf->cpu=0x%p, buf->jmp=0x%p\n", core->name, buf, buf->cpu, buf->jmp); for (addr = buf->cpu; addr <= buf->jmp; addr += 2) tw68_risc_decode(*addr, *(addr+1)); } #endif |