1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Part of the device driver for Techwell 68xx based cards
6 * Much of this code is derived from the cx88 and sa7134 drivers, which
7 * were in turn derived from the bt87x driver. The original work was by
8 * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
9 * Hans Verkuil, Andy Walls and many others. Their work is gratefully
10 * acknowledged. Full credit goes to them - any problems within this code
13 * Copyright (C) 2009 William M. Brack
15 * Refactored and updated to the latest v4l core frameworks:
17 * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl>
24 * @rp: pointer to current risc program position
25 * @sglist: pointer to "scatter-gather list" of buffer pointers
26 * @offset: offset to target memory buffer
27 * @sync_line: 0 -> no sync, 1 -> odd sync, 2 -> even sync
28 * @bpl: number of bytes per scan line
29 * @padding: number of bytes of padding to add
30 * @lines: number of lines in field
31 * @jump: insert a jump at the start
33 static __le32
*tw68_risc_field(__le32
*rp
, struct scatterlist
*sglist
,
34 unsigned int offset
, u32 sync_line
,
35 unsigned int bpl
, unsigned int padding
,
36 unsigned int lines
, bool jump
)
38 struct scatterlist
*sg
;
39 unsigned int line
, todo
, done
;
42 *(rp
++) = cpu_to_le32(RISC_JUMP
);
46 /* sync instruction */
48 *(rp
++) = cpu_to_le32(RISC_SYNCO
);
50 *(rp
++) = cpu_to_le32(RISC_SYNCE
);
55 for (line
= 0; line
< lines
; line
++) {
56 /* calculate next starting position */
57 while (offset
&& offset
>= sg_dma_len(sg
)) {
58 offset
-= sg_dma_len(sg
);
61 if (bpl
<= sg_dma_len(sg
) - offset
) {
62 /* fits into current chunk */
63 *(rp
++) = cpu_to_le32(RISC_LINESTART
|
64 /* (offset<<12) |*/ bpl
);
65 *(rp
++) = cpu_to_le32(sg_dma_address(sg
) + offset
);
69 * scanline needs to be split. Put the start in
70 * whatever memory remains using RISC_LINESTART,
71 * then the remainder into following addresses
72 * given by the scatter-gather list.
74 todo
= bpl
; /* one full line to be done */
76 done
= (sg_dma_len(sg
) - offset
);
77 *(rp
++) = cpu_to_le32(RISC_LINESTART
|
80 *(rp
++) = cpu_to_le32(sg_dma_address(sg
) + offset
);
83 /* succeeding fragments have no offset */
84 while (todo
> sg_dma_len(sg
)) {
85 *(rp
++) = cpu_to_le32(RISC_INLINE
|
88 *(rp
++) = cpu_to_le32(sg_dma_address(sg
));
89 todo
-= sg_dma_len(sg
);
91 done
+= sg_dma_len(sg
);
94 /* final chunk - offset 0, count 'todo' */
95 *(rp
++) = cpu_to_le32(RISC_INLINE
|
98 *(rp
++) = cpu_to_le32(sg_dma_address(sg
));
111 * This routine is called by tw68-video. It allocates
112 * memory for the dma controller "program" and then fills in that
113 * memory with the appropriate "instructions".
115 * @pci: structure with info about the pci
116 * slot which our device is in.
117 * @buf: structure with info about the memory
118 * used for our controller program.
119 * @sglist: scatter-gather list entry
120 * @top_offset: offset within the risc program area for the
121 * first odd frame line
122 * @bottom_offset: offset within the risc program area for the
123 * first even frame line
124 * @bpl: number of data bytes per scan line
125 * @padding: number of extra bytes to add at end of line
126 * @lines: number of scan lines
128 int tw68_risc_buffer(struct pci_dev
*pci
,
129 struct tw68_buf
*buf
,
130 struct scatterlist
*sglist
,
131 unsigned int top_offset
,
132 unsigned int bottom_offset
,
134 unsigned int padding
,
137 u32 instructions
, fields
;
141 if (UNSET
!= top_offset
)
143 if (UNSET
!= bottom_offset
)
146 * estimate risc mem: worst case is one write per page border +
147 * one write per scan line + syncs + 2 jumps (all 2 dwords).
148 * Padding can cause next bpl to start close to a page border.
149 * First DMA region may be smaller than PAGE_SIZE
151 instructions
= fields
* (1 + (((bpl
+ padding
) * lines
) /
152 PAGE_SIZE
) + lines
) + 4;
153 buf
->size
= instructions
* 8;
154 buf
->cpu
= pci_alloc_consistent(pci
, buf
->size
, &buf
->dma
);
155 if (buf
->cpu
== NULL
)
158 /* write risc instructions */
160 if (UNSET
!= top_offset
) /* generates SYNCO */
161 rp
= tw68_risc_field(rp
, sglist
, top_offset
, 1,
162 bpl
, padding
, lines
, true);
163 if (UNSET
!= bottom_offset
) /* generates SYNCE */
164 rp
= tw68_risc_field(rp
, sglist
, bottom_offset
, 2,
165 bpl
, padding
, lines
, top_offset
== UNSET
);
167 /* save pointer to jmp instruction address */
169 buf
->cpu
[1] = cpu_to_le32(buf
->dma
+ 8);
170 /* assure risc buffer hasn't overflowed */
171 BUG_ON((buf
->jmp
- buf
->cpu
+ 2) * sizeof(buf
->cpu
[0]) > buf
->size
);
176 /* ------------------------------------------------------------------ */
177 /* debug helper code */
179 static void tw68_risc_decode(u32 risc
, u32 addr
)
181 #define RISC_OP(reg) (((reg) >> 28) & 7)
182 static struct instr_details
{
188 [RISC_OP(RISC_SYNCO
)] = {"syncOdd", 0, 0, 0},
189 [RISC_OP(RISC_SYNCE
)] = {"syncEven", 0, 0, 0},
190 [RISC_OP(RISC_JUMP
)] = {"jump", 0, 0, 1},
191 [RISC_OP(RISC_LINESTART
)] = {"lineStart", 1, 1, 1},
192 [RISC_OP(RISC_INLINE
)] = {"inline", 1, 1, 1},
197 if (!(risc
& 0x80000000) || !instr
[p
].name
) {
198 pr_debug("0x%08x [ INVALID ]\n", risc
);
201 pr_debug("0x%08x %-9s IRQ=%d",
202 risc
, instr
[p
].name
, (risc
>> 27) & 1);
203 if (instr
[p
].has_data_type
)
204 pr_debug(" Type=%d", (risc
>> 24) & 7);
205 if (instr
[p
].has_byte_info
)
206 pr_debug(" Start=0x%03x Count=%03u",
207 (risc
>> 12) & 0xfff, risc
& 0xfff);
208 if (instr
[p
].has_addr
)
209 pr_debug(" StartAddr=0x%08x", addr
);
213 void tw68_risc_program_dump(struct tw68_core
*core
, struct tw68_buf
*buf
)
217 pr_debug("%s: risc_program_dump: risc=%p, buf->cpu=0x%p, buf->jmp=0x%p\n",
218 core
->name
, buf
, buf
->cpu
, buf
->jmp
);
219 for (addr
= buf
->cpu
; addr
<= buf
->jmp
; addr
+= 2)
220 tw68_risc_decode(*addr
, *(addr
+1));