* - DL swap
*/
+#define VSP1_DL_HEADER_SIZE 76
#define VSP1_DL_BODY_SIZE (2 * 4 * 256)
#define VSP1_DL_NUM_LISTS 3
+#define VSP1_DLH_INT_ENABLE (1 << 1)
+#define VSP1_DLH_AUTO_START (1 << 0)
+
+struct vsp1_dl_header {
+ u32 num_lists;
+ struct {
+ u32 num_bytes;
+ u32 addr;
+ } lists[8];
+ u32 next_header;
+ u32 flags;
+} __attribute__((__packed__));
+
struct vsp1_dl_entry {
u32 addr;
u32 data;
struct vsp1_dl_manager *dlm;
+ struct vsp1_dl_header *header;
struct vsp1_dl_entry *body;
dma_addr_t dma;
size_t size;
int reg_count;
};
+enum vsp1_dl_mode {
+ VSP1_DL_MODE_HEADER,
+ VSP1_DL_MODE_HEADERLESS,
+};
+
/**
* struct vsp1_dl_manager - Display List manager
+ * @index: index of the related WPF
+ * @mode: display list operation mode (header or headerless)
* @vsp1: the VSP1 device
* @lock: protects the active, queued and pending lists
* @free: array of all free display lists
* @pending: list waiting to be queued to the hardware
*/
struct vsp1_dl_manager {
+ unsigned int index;
+ enum vsp1_dl_mode mode;
struct vsp1_device *vsp1;
spinlock_t lock;
static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm)
{
struct vsp1_dl_list *dl;
+ size_t header_size;
+
+ /* The body needs to be aligned on a 8 bytes boundary, pad the header
+ * size to allow allocating both in a single operation.
+ */
+ header_size = dlm->mode == VSP1_DL_MODE_HEADER
+ ? ALIGN(sizeof(struct vsp1_dl_header), 8)
+ : 0;
dl = kzalloc(sizeof(*dl), GFP_KERNEL);
if (!dl)
return NULL;
dl->dlm = dlm;
- dl->size = VSP1_DL_BODY_SIZE;
+ dl->size = header_size + VSP1_DL_BODY_SIZE;
- dl->body = dma_alloc_wc(dlm->vsp1->dev, dl->size, &dl->dma, GFP_KERNEL);
- if (!dl->body) {
+ dl->header = dma_alloc_wc(dlm->vsp1->dev, dl->size, &dl->dma,
+ GFP_KERNEL);
+ if (!dl->header) {
kfree(dl);
return NULL;
}
+ if (dlm->mode == VSP1_DL_MODE_HEADER) {
+ memset(dl->header, 0, sizeof(*dl->header));
+ dl->header->lists[0].addr = dl->dma + header_size;
+ dl->header->flags = VSP1_DLH_INT_ENABLE;
+ }
+
+ dl->body = ((void *)dl->header) + header_size;
+
return dl;
}
static void vsp1_dl_list_free(struct vsp1_dl_list *dl)
{
- dma_free_wc(dl->dlm->vsp1->dev, dl->size, dl->body, dl->dma);
+ dma_free_wc(dl->dlm->vsp1->dev, dl->size, dl->header, dl->dma);
kfree(dl);
}
spin_lock_irqsave(&dlm->lock, flags);
+ if (dl->dlm->mode == VSP1_DL_MODE_HEADER) {
+ /* Program the hardware with the display list body address and
+ * size. In header mode the caller guarantees that the hardware
+ * is idle at this point.
+ */
+ dl->header->lists[0].num_bytes = dl->reg_count * 8;
+ vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma);
+
+ dlm->active = dl;
+ goto done;
+ }
+
/* Once the UPD bit has been set the hardware can start processing the
* display list at any time and we can't touch the address and size
* registers. In that case mark the update as pending, it will be
vsp1_dl_list_put(dlm->active);
dlm->active = NULL;
+ /* Header mode is used for mem-to-mem pipelines only. We don't need to
+ * perform any operation as there can't be any new display list queued
+ * in that case.
+ */
+ if (dlm->mode == VSP1_DL_MODE_HEADER)
+ goto done;
+
/* The UPD bit set indicates that the commit operation raced with the
* interrupt and occurred after the frame end event and UPD clear but
* before interrupt processing. The hardware hasn't taken the update
}
struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
+ unsigned int index,
unsigned int prealloc)
{
struct vsp1_dl_manager *dlm;
if (!dlm)
return NULL;
+ dlm->index = index;
+ dlm->mode = index == 0 && !vsp1->info->uapi
+ ? VSP1_DL_MODE_HEADERLESS : VSP1_DL_MODE_HEADER;
dlm->vsp1 = vsp1;
spin_lock_init(&dlm->lock);