// 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, unsignedint offset, u32 sync_line, unsignedint bpl, unsignedint padding, unsignedint lines, bool jump)
{ struct scatterlist *sg; unsignedint line, todo, done;
if (jump) {
*(rp++) = cpu_to_le32(RISC_JUMP);
*(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, unsignedint top_offset, unsignedint bottom_offset, unsignedint bpl, unsignedint padding, unsignedint 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);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.