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.
32 * @rp pointer to current risc program position
33 * @sglist pointer to "scatter-gather list" of buffer pointers
34 * @offset offset to target memory buffer
35 * @sync_line 0 -> no sync, 1 -> odd sync, 2 -> even sync
36 * @bpl number of bytes per scan line
37 * @padding number of bytes of padding to add
38 * @lines number of lines in field
39 * @jump insert a jump at the start
41 static __le32
*tw68_risc_field(__le32
*rp
, struct scatterlist
*sglist
,
42 unsigned int offset
, u32 sync_line
,
43 unsigned int bpl
, unsigned int padding
,
44 unsigned int lines
, bool jump
)
46 struct scatterlist
*sg
;
47 unsigned int line
, todo
, done
;
50 *(rp
++) = cpu_to_le32(RISC_JUMP
);
54 /* sync instruction */
56 *(rp
++) = cpu_to_le32(RISC_SYNCO
);
58 *(rp
++) = cpu_to_le32(RISC_SYNCE
);
63 for (line
= 0; line
< lines
; line
++) {
64 /* calculate next starting position */
65 while (offset
&& offset
>= sg_dma_len(sg
)) {
66 offset
-= sg_dma_len(sg
);
69 if (bpl
<= sg_dma_len(sg
) - offset
) {
70 /* fits into current chunk */
71 *(rp
++) = cpu_to_le32(RISC_LINESTART
|
72 /* (offset<<12) |*/ bpl
);
73 *(rp
++) = cpu_to_le32(sg_dma_address(sg
) + offset
);
77 * scanline needs to be split. Put the start in
78 * whatever memory remains using RISC_LINESTART,
79 * then the remainder into following addresses
80 * given by the scatter-gather list.
82 todo
= bpl
; /* one full line to be done */
84 done
= (sg_dma_len(sg
) - offset
);
85 *(rp
++) = cpu_to_le32(RISC_LINESTART
|
88 *(rp
++) = cpu_to_le32(sg_dma_address(sg
) + offset
);
91 /* succeeding fragments have no offset */
92 while (todo
> sg_dma_len(sg
)) {
93 *(rp
++) = cpu_to_le32(RISC_INLINE
|
96 *(rp
++) = cpu_to_le32(sg_dma_address(sg
));
97 todo
-= sg_dma_len(sg
);
99 done
+= sg_dma_len(sg
);
102 /* final chunk - offset 0, count 'todo' */
103 *(rp
++) = cpu_to_le32(RISC_INLINE
|
106 *(rp
++) = cpu_to_le32(sg_dma_address(sg
));
119 * This routine is called by tw68-video. It allocates
120 * memory for the dma controller "program" and then fills in that
121 * memory with the appropriate "instructions".
123 * @pci_dev structure with info about the pci
124 * slot which our device is in.
125 * @risc structure with info about the memory
126 * used for our controller program.
127 * @sglist scatter-gather list entry
128 * @top_offset offset within the risc program area for the
129 * first odd frame line
130 * @bottom_offset offset within the risc program area for the
131 * first even frame line
132 * @bpl number of data bytes per scan line
133 * @padding number of extra bytes to add at end of line
134 * @lines number of scan lines
136 int tw68_risc_buffer(struct pci_dev
*pci
,
137 struct tw68_buf
*buf
,
138 struct scatterlist
*sglist
,
139 unsigned int top_offset
,
140 unsigned int bottom_offset
,
142 unsigned int padding
,
145 u32 instructions
, fields
;
149 if (UNSET
!= top_offset
)
151 if (UNSET
!= bottom_offset
)
154 * estimate risc mem: worst case is one write per page border +
155 * one write per scan line + syncs + 2 jumps (all 2 dwords).
156 * Padding can cause next bpl to start close to a page border.
157 * First DMA region may be smaller than PAGE_SIZE
159 instructions
= fields
* (1 + (((bpl
+ padding
) * lines
) /
160 PAGE_SIZE
) + lines
) + 4;
161 buf
->size
= instructions
* 8;
162 buf
->cpu
= pci_alloc_consistent(pci
, buf
->size
, &buf
->dma
);
163 if (buf
->cpu
== NULL
)
166 /* write risc instructions */
168 if (UNSET
!= top_offset
) /* generates SYNCO */
169 rp
= tw68_risc_field(rp
, sglist
, top_offset
, 1,
170 bpl
, padding
, lines
, true);
171 if (UNSET
!= bottom_offset
) /* generates SYNCE */
172 rp
= tw68_risc_field(rp
, sglist
, bottom_offset
, 2,
173 bpl
, padding
, lines
, top_offset
== UNSET
);
175 /* save pointer to jmp instruction address */
177 buf
->cpu
[1] = cpu_to_le32(buf
->dma
+ 8);
178 /* assure risc buffer hasn't overflowed */
179 BUG_ON((buf
->jmp
- buf
->cpu
+ 2) * sizeof(buf
->cpu
[0]) > buf
->size
);
184 /* ------------------------------------------------------------------ */
185 /* debug helper code */
187 static void tw68_risc_decode(u32 risc
, u32 addr
)
189 #define RISC_OP(reg) (((reg) >> 28) & 7)
190 static struct instr_details
{
196 [RISC_OP(RISC_SYNCO
)] = {"syncOdd", 0, 0, 0},
197 [RISC_OP(RISC_SYNCE
)] = {"syncEven", 0, 0, 0},
198 [RISC_OP(RISC_JUMP
)] = {"jump", 0, 0, 1},
199 [RISC_OP(RISC_LINESTART
)] = {"lineStart", 1, 1, 1},
200 [RISC_OP(RISC_INLINE
)] = {"inline", 1, 1, 1},
205 if (!(risc
& 0x80000000) || !instr
[p
].name
) {
206 pr_debug("0x%08x [ INVALID ]\n", risc
);
209 pr_debug("0x%08x %-9s IRQ=%d",
210 risc
, instr
[p
].name
, (risc
>> 27) & 1);
211 if (instr
[p
].has_data_type
)
212 pr_debug(" Type=%d", (risc
>> 24) & 7);
213 if (instr
[p
].has_byte_info
)
214 pr_debug(" Start=0x%03x Count=%03u",
215 (risc
>> 12) & 0xfff, risc
& 0xfff);
216 if (instr
[p
].has_addr
)
217 pr_debug(" StartAddr=0x%08x", addr
);
221 void tw68_risc_program_dump(struct tw68_core
*core
, struct tw68_buf
*buf
)
225 pr_debug("%s: risc_program_dump: risc=%p, buf->cpu=0x%p, buf->jmp=0x%p\n",
226 core
->name
, buf
, buf
->cpu
, buf
->jmp
);
227 for (addr
= buf
->cpu
; addr
<= buf
->jmp
; addr
+= 2)
228 tw68_risc_decode(*addr
, *(addr
+1));