/* * CRUnCTRL is a first register on all CRU supported SoCs so validate * rest of the registers have valid offset being set in cru->info->regs.
*/ if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) ||
WARN_ON(offset != CRUnCTRL && regs[offset] == 0)) return;
/* * CRUnCTRL is a first register on all CRU supported SoCs so validate * rest of the registers have valid offset being set in cru->info->regs.
*/ if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) ||
WARN_ON(offset != CRUnCTRL && regs[offset] == 0)) return 0;
staticvoid rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, int slot, dma_addr_t addr)
{ /* * The address needs to be 512 bytes aligned. Driver should never accept * settings that do not satisfy this in the first place...
*/ if (WARN_ON((addr) & RZG2L_CRU_HW_BUFFER_MASK)) return;
/* Currently, we just use the buffer in 32 bits address */
rzg2l_cru_write(cru, AMnMBxADDRL(slot), addr);
rzg2l_cru_write(cru, AMnMBxADDRH(slot), 0);
cru->buf_addr[slot] = addr;
}
/* * Moves a buffer from the queue to the HW slot. If no buffer is * available use the scratch buffer. The scratch buffer is never * returned to userspace, its only function is to enable the capture * loop to keep running.
*/ staticvoid rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot)
{ struct vb2_v4l2_buffer *vbuf; struct rzg2l_cru_buffer *buf;
dma_addr_t phys_addr;
/* A already populated slot shall never be overwritten. */ if (WARN_ON(cru->queue_buf[slot])) return;
dev_dbg(cru->dev, "Filling HW slot: %d\n", slot);
if (list_empty(&cru->buf_list)) {
cru->queue_buf[slot] = NULL;
phys_addr = cru->scratch_phys;
} else { /* Keep track of buffer we give to HW */
buf = list_entry(cru->buf_list.next, struct rzg2l_cru_buffer, list);
vbuf = &buf->vb;
list_del_init(to_buf_list(vbuf));
cru->queue_buf[slot] = vbuf;
/* Output format */
cru_video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat); if (!cru_video_fmt) {
dev_err(cru->dev, "Invalid pixelformat (0x%x)\n",
cru->format.pixelformat); return -EINVAL;
}
/* If input and output use same colorspace, do bypass mode */ if (cru_ip_fmt->yuv == cru_video_fmt->yuv)
rzg2l_cru_write(cru, info->image_conv,
rzg2l_cru_read(cru, info->image_conv) | ICnMC_CSCTHR); else
rzg2l_cru_write(cru, info->image_conv,
rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_CSCTHR);
/* Set output data format */
rzg2l_cru_write(cru, ICnDMR, cru_video_fmt->icndmr);
/* Disable and clear the interrupt */
cru->info->disable_interrupts(cru);
/* Stop the operation of image conversion */
rzg2l_cru_write(cru, ICnEN, 0);
/* Wait for streaming to stop */ while ((rzg2l_cru_read(cru, ICnMS) & ICnMS_IA) && retries++ < RZG2L_RETRIES) {
spin_unlock_irqrestore(&cru->qlock, flags);
msleep(RZG2L_TIMEOUT_MS);
spin_lock_irqsave(&cru->qlock, flags);
}
icnms = rzg2l_cru_read(cru, ICnMS) & ICnMS_IA; if (icnms)
dev_err(cru->dev, "Failed stop HW, something is seriously broken\n");
cru->state = RZG2L_CRU_DMA_STOPPED;
/* Wait until the FIFO becomes empty */ for (retries = 5; retries > 0; retries--) { if (cru->info->fifo_empty(cru)) break;
usleep_range(10, 20);
}
/* Notify that FIFO is not empty here */ if (!retries)
dev_err(cru->dev, "Failed to empty FIFO\n");
/* Stop AXI bus */
rzg2l_cru_write(cru, AMnAXISTP, AMnAXISTP_AXI_STOP);
/* Wait until the AXI bus stop */ for (retries = 5; retries > 0; retries--) { if (rzg2l_cru_read(cru, AMnAXISTPACK) &
AMnAXISTPACK_AXI_STOP_ACK) break;
usleep_range(10, 20);
}
/* Notify that AXI bus can not stop here */ if (!retries)
dev_err(cru->dev, "Failed to stop AXI bus\n");
/* Cancel the AXI bus stop request */
rzg2l_cru_write(cru, AMnAXISTP, 0);
/* Reset the CRU (AXI-master) */
reset_control_assert(cru->aresetn);
/* Resets the image processing module */
rzg2l_cru_write(cru, CRUnRST, 0);
remote_pad = media_pad_remote_pad_unique(&cru->ip.pads[RZG2L_CRU_IP_SINK]);
ret = v4l2_subdev_call(cru->ip.remote, pad, get_frame_desc, remote_pad->index, &fd); if (ret < 0 && ret != -ENOIOCTLCMD) {
dev_err(cru->dev, "get_frame_desc failed on IP remote subdev\n"); return ret;
} /* If remote subdev does not implement .get_frame_desc default to VC0. */ if (ret == -ENOIOCTLCMD) return 0;
if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
dev_err(cru->dev, "get_frame_desc returned invalid bus type %d\n", fd.type); return -EINVAL;
}
if (!fd.num_entries) {
dev_err(cru->dev, "get_frame_desc returned zero entries\n"); return -EINVAL;
}
/* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ if (cru->state == RZG2L_CRU_DMA_STOPPED) {
dev_dbg(cru->dev, "IRQ while state stopped\n"); goto done;
}
/* Increase stop retries if capture status is 'RZG2L_CRU_DMA_STOPPING' */ if (cru->state == RZG2L_CRU_DMA_STOPPING) { if (irq_status & CRUnINTS_SFS)
dev_dbg(cru->dev, "IRQ while state stopping\n"); goto done;
}
/* Prepare for capture and update state */
amnmbs = rzg2l_cru_read(cru, AMnMBS);
slot = amnmbs & AMnMBS_MBSTS;
/* * AMnMBS.MBSTS indicates the destination of Memory Bank (MB). * Recalculate to get the current transfer complete MB.
*/ if (slot == 0)
slot = cru->num_buf - 1; else
slot--;
/* * To hand buffers back in a known order to userspace start * to capture first from slot 0.
*/ if (cru->state == RZG2L_CRU_DMA_STARTING) { if (slot != 0) {
dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); goto done;
}
/* Prepare for next frame */
rzg2l_cru_fill_hw_slot(cru, slot);
done:
spin_unlock_irqrestore(&cru->qlock, flags);
return IRQ_RETVAL(handled);
}
staticint rzg3e_cru_get_current_slot(struct rzg2l_cru_dev *cru)
{
u64 amnmadrs; int slot;
/* * When AMnMADRSL is read, AMnMADRSH of the higher-order * address also latches the address. * * AMnMADRSH must be read after AMnMADRSL has been read.
*/
amnmadrs = rzg2l_cru_read(cru, AMnMADRSL);
amnmadrs |= (u64)rzg2l_cru_read(cru, AMnMADRSH) << 32;
/* Ensure amnmadrs is within this buffer range */ for (slot = 0; slot < cru->num_buf; slot++) { if (amnmadrs >= cru->buf_addr[slot] &&
amnmadrs < cru->buf_addr[slot] + cru->format.sizeimage) return slot;
}
/* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ if (cru->state == RZG2L_CRU_DMA_STOPPED) {
dev_dbg(cru->dev, "IRQ while state stopped\n"); return IRQ_HANDLED;
}
if (cru->state == RZG2L_CRU_DMA_STOPPING) { if (irq_status & CRUnINTS2_FSxS(0) ||
irq_status & CRUnINTS2_FSxS(1) ||
irq_status & CRUnINTS2_FSxS(2) ||
irq_status & CRUnINTS2_FSxS(3))
dev_dbg(cru->dev, "IRQ while state stopping\n"); return IRQ_HANDLED;
}
slot = rzg3e_cru_get_current_slot(cru); if (slot < 0) return IRQ_HANDLED;
dev_dbg(cru->dev, "Current written slot: %d\n", slot);
cru->buf_addr[slot] = 0;
/* * To hand buffers back in a known order to userspace start * to capture first from slot 0.
*/ if (cru->state == RZG2L_CRU_DMA_STARTING) { if (slot != 0) {
dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); return IRQ_HANDLED;
}
dev_dbg(cru->dev, "Capture start synced!\n");
cru->state = RZG2L_CRU_DMA_RUNNING;
}
ret = pm_runtime_resume_and_get(cru->dev); if (ret) return ret;
ret = clk_prepare_enable(cru->vclk); if (ret) goto err_pm_put;
/* Release reset state */
ret = reset_control_deassert(cru->aresetn); if (ret) {
dev_err(cru->dev, "failed to deassert aresetn\n"); goto err_vclk_disable;
}
ret = reset_control_deassert(cru->presetn); if (ret) {
reset_control_assert(cru->aresetn);
dev_err(cru->dev, "failed to deassert presetn\n"); goto assert_aresetn;
}
/* Allocate scratch buffer */
cru->scratch = dma_alloc_coherent(cru->dev, cru->format.sizeimage,
&cru->scratch_phys, GFP_KERNEL); if (!cru->scratch) {
return_unused_buffers(cru, VB2_BUF_STATE_QUEUED);
dev_err(cru->dev, "Failed to allocate scratch buffer\n");
ret = -ENOMEM; goto assert_presetn;
}
cru->sequence = 0;
ret = rzg2l_cru_set_stream(cru, 1); if (ret) {
return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); goto out;
}
cru->state = RZG2L_CRU_DMA_STARTING;
dev_dbg(cru->dev, "Starting to capture\n"); return 0;
out: if (ret)
dma_free_coherent(cru->dev, cru->format.sizeimage, cru->scratch,
cru->scratch_phys);
assert_presetn:
reset_control_assert(cru->presetn);
switch (pix->field) { case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: case V4L2_FIELD_NONE: case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: break; default:
pix->field = RZG2L_CRU_DEFAULT_FIELD; break;
}
staticvoid rzg2l_cru_try_format(struct rzg2l_cru_dev *cru, struct v4l2_pix_format *pix)
{ /* * The V4L2 specification clearly documents the colorspace fields * as being set by drivers for capture devices. Using the values * supplied by userspace thus wouldn't comply with the API. Until * the API is updated force fixed values.
*/
pix->colorspace = RZG2L_CRU_DEFAULT_COLORSPACE;
pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);
pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);
pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, pix->colorspace,
pix->ycbcr_enc);
rzg2l_cru_v4l2_init(cru);
video_set_drvdata(vdev, cru);
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) {
dev_err(cru->dev, "Failed to register video device\n"); return ret;
}
ret = media_device_register(&cru->mdev); if (ret) {
video_unregister_device(&cru->vdev); return ret;
}
return 0;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.33 Sekunden
(vorverarbeitet)
¤
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.