3 * Part of the device driver for Techwell 68xx based cards
5 * Much of this code is derived from the cx88 and sa7134 drivers, which
6 * were in turn derived from the bt87x driver. The original work was by
7 * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
8 * Hans Verkuil, Andy Walls and many others. Their work is gratefully
9 * acknowledged. Full credit goes to them - any problems within this code
12 * Copyright (C) 2009 William M. Brack
14 * Refactored and updated to the latest v4l core frameworks:
16 * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl>
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
33 * @rp: pointer to current risc program position
34 * @sglist: pointer to "scatter-gather list" of buffer pointers
35 * @offset: offset to target memory buffer
36 * @sync_line: 0 -> no sync, 1 -> odd sync, 2 -> even sync
37 * @bpl: number of bytes per scan line
38 * @padding: number of bytes of padding to add
39 * @lines: number of lines in field
40 * @jump: insert a jump at the start
42 static __le32
*tw68_risc_field(__le32
*rp
, struct scatterlist
*sglist
,
43 unsigned int offset
, u32 sync_line
,
44 unsigned int bpl
, unsigned int padding
,
45 unsigned int lines
, bool jump
)
47 struct scatterlist
*sg
;
48 unsigned int line
, todo
, done
;
51 *(rp
++) = cpu_to_le32(RISC_JUMP
);
55 /* sync instruction */
57 *(rp
++) = cpu_to_le32(RISC_SYNCO
);
59 *(rp
++) = cpu_to_le32(RISC_SYNCE
);
64 for (line
= 0; line
< lines
; line
++) {
65 /* calculate next starting position */
66 while (offset
&& offset
>= sg_dma_len(sg
)) {
67 offset
-= sg_dma_len(sg
);
70 if (bpl
<= sg_dma_len(sg
) - offset
) {
71 /* fits into current chunk */
72 *(rp
++) = cpu_to_le32(RISC_LINESTART
|
73 /* (offset<<12) |*/ bpl
);
74 *(rp
++) = cpu_to_le32(sg_dma_address(sg
) + offset
);
78 * scanline needs to be split. Put the start in
79 * whatever memory remains using RISC_LINESTART,
80 * then the remainder into following addresses
81 * given by the scatter-gather list.
83 todo
= bpl
; /* one full line to be done */
85 done
= (sg_dma_len(sg
) - offset
);
86 *(rp
++) = cpu_to_le32(RISC_LINESTART
|
89 *(rp
++) = cpu_to_le32(sg_dma_address(sg
) + offset
);
92 /* succeeding fragments have no offset */
93 while (todo
> sg_dma_len(sg
)) {
94 *(rp
++) = cpu_to_le32(RISC_INLINE
|
97 *(rp
++) = cpu_to_le32(sg_dma_address(sg
));
98 todo
-= sg_dma_len(sg
);
100 done
+= sg_dma_len(sg
);
103 /* final chunk - offset 0, count 'todo' */
104 *(rp
++) = cpu_to_le32(RISC_INLINE
|
107 *(rp
++) = cpu_to_le32(sg_dma_address(sg
));
120 * This routine is called by tw68-video. It allocates
121 * memory for the dma controller "program" and then fills in that
122 * memory with the appropriate "instructions".
124 * @pci: structure with info about the pci
125 * slot which our device is in.
126 * @buf: structure with info about the memory
127 * used for our controller program.
128 * @sglist: scatter-gather list entry
129 * @top_offset: offset within the risc program area for the
130 * first odd frame line
131 * @bottom_offset: offset within the risc program area for the
132 * first even frame line
133 * @bpl: number of data bytes per scan line
134 * @padding: number of extra bytes to add at end of line
135 * @lines: number of scan lines
137 int tw68_risc_buffer(struct pci_dev
*pci
,
138 struct tw68_buf
*buf
,
139 struct scatterlist
*sglist
,
140 unsigned int top_offset
,
141 unsigned int bottom_offset
,
143 unsigned int padding
,
146 u32 instructions
, fields
;
150 if (UNSET
!= top_offset
)
152 if (UNSET
!= bottom_offset
)
155 * estimate risc mem: worst case is one write per page border +
156 * one write per scan line + syncs + 2 jumps (all 2 dwords).
157 * Padding can cause next bpl to start close to a page border.
158 * First DMA region may be smaller than PAGE_SIZE
160 instructions
= fields
* (1 + (((bpl
+ padding
) * lines
) /
161 PAGE_SIZE
) + lines
) + 4;
162 buf
->size
= instructions
* 8;
163 buf
->cpu
= pci_alloc_consistent(pci
, buf
->size
, &buf
->dma
);
164 if (buf
->cpu
== NULL
)
167 /* write risc instructions */
169 if (UNSET
!= top_offset
) /* generates SYNCO */
170 rp
= tw68_risc_field(rp
, sglist
, top_offset
, 1,
171 bpl
, padding
, lines
, true);
172 if (UNSET
!= bottom_offset
) /* generates SYNCE */
173 rp
= tw68_risc_field(rp
, sglist
, bottom_offset
, 2,
174 bpl
, padding
, lines
, top_offset
== UNSET
);
176 /* save pointer to jmp instruction address */
178 buf
->cpu
[1] = cpu_to_le32(buf
->dma
+ 8);
179 /* assure risc buffer hasn't overflowed */
180 BUG_ON((buf
->jmp
- buf
->cpu
+ 2) * sizeof(buf
->cpu
[0]) > buf
->size
);
185 /* ------------------------------------------------------------------ */
186 /* debug helper code */
188 static void tw68_risc_decode(u32 risc
, u32 addr
)
190 #define RISC_OP(reg) (((reg) >> 28) & 7)
191 static struct instr_details
{
197 [RISC_OP(RISC_SYNCO
)] = {"syncOdd", 0, 0, 0},
198 [RISC_OP(RISC_SYNCE
)] = {"syncEven", 0, 0, 0},
199 [RISC_OP(RISC_JUMP
)] = {"jump", 0, 0, 1},
200 [RISC_OP(RISC_LINESTART
)] = {"lineStart", 1, 1, 1},
201 [RISC_OP(RISC_INLINE
)] = {"inline", 1, 1, 1},
206 if (!(risc
& 0x80000000) || !instr
[p
].name
) {
207 pr_debug("0x%08x [ INVALID ]\n", risc
);
210 pr_debug("0x%08x %-9s IRQ=%d",
211 risc
, instr
[p
].name
, (risc
>> 27) & 1);
212 if (instr
[p
].has_data_type
)
213 pr_debug(" Type=%d", (risc
>> 24) & 7);
214 if (instr
[p
].has_byte_info
)
215 pr_debug(" Start=0x%03x Count=%03u",
216 (risc
>> 12) & 0xfff, risc
& 0xfff);
217 if (instr
[p
].has_addr
)
218 pr_debug(" StartAddr=0x%08x", addr
);
222 void tw68_risc_program_dump(struct tw68_core
*core
, struct tw68_buf
*buf
)
226 pr_debug("%s: risc_program_dump: risc=%p, buf->cpu=0x%p, buf->jmp=0x%p\n",
227 core
->name
, buf
, buf
->cpu
, buf
->jmp
);
228 for (addr
= buf
->cpu
; addr
<= buf
->jmp
; addr
+= 2)
229 tw68_risc_decode(*addr
, *(addr
+1));