@@ -1,7 +1,7 @@
config VIDEO_OMAP1
tristate "OMAP1 Camera Interface driver"
depends on VIDEO_DEV && SOC_CAMERA
- depends on ARCH_OMAP1
+ depends on ARCH_OMAP1 && DMA_OMAP
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
---help---
@@ -33,11 +33,12 @@
#include <media/drv-intf/soc_mediabus.h>
#include <media/videobuf2-dma-contig.h>
-#include <linux/omap-dma.h>
+#include <linux/dmaengine.h>
+#include <linux/omap-dmaengine.h>
#define DRIVER_NAME "omap1-camera"
-#define DRIVER_VERSION "0.0.4"
+#define DRIVER_VERSION "0.0.5"
#define OMAP_DMA_CAMERA_IF_RX 20
@@ -115,8 +116,8 @@
#define DMA_BURST_SHIFT (1 + OMAP_DMA_DATA_BURST_4)
#define DMA_BURST_SIZE BIT(DMA_BURST_SHIFT)
-#define DMA_ELEMENT_SHIFT OMAP_DMA_DATA_TYPE_S32
-#define DMA_ELEMENT_SIZE BIT(DMA_ELEMENT_SHIFT)
+#define DMA_ELEMENT_SIZE DMA_SLAVE_BUSWIDTH_4_BYTES
+#define DMA_ELEMENT_SHIFT __fls(DMA_ELEMENT_SIZE)
#define DMA_FRAME_SHIFT (FIFO_SHIFT - 1)
#define DMA_FRAME_SIZE BIT(DMA_FRAME_SHIFT)
@@ -124,7 +125,7 @@
#define THRESHOLD_LEVEL DMA_FRAME_SIZE
#define OMAP1_CAMERA_MIN_BUF_COUNT \
- 3
+ 2
#define MAX_VIDEO_MEM 4 /* arbitrary video memory limit in MB */
@@ -145,7 +146,8 @@ struct omap1_cam_dev {
unsigned int irq;
void __iomem *base;
- int dma_ch;
+ struct dma_chan *dma_chan;
+ unsigned int dma_rq;
struct omap1_cam_platform_data *pdata;
unsigned long pflags;
@@ -156,10 +158,6 @@ struct omap1_cam_dev {
/* lock used to protect videobuf */
spinlock_t lock;
- /* Pointers to DMA buffers */
- struct omap1_cam_buf *active;
- struct omap1_cam_buf *ready;
-
struct vb2_alloc_ctx *alloc_ctx;
int sequence;
@@ -222,6 +220,16 @@ static int omap1_videobuf_setup(struct vb2_queue *vq, unsigned int *count,
return 0;
}
+static int omap1_videobuf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct omap1_cam_buf *buf = vb2_to_omap1_cam_buf(vbuf);
+
+ INIT_LIST_HEAD(&buf->queue);
+
+ return 0;
+}
+
static int omap1_videobuf_prepare(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
@@ -236,96 +244,27 @@ static int omap1_videobuf_prepare(struct vb2_buffer *vb)
vb2_plane_size(vb, 0), size);
return -ENOBUFS;
}
-
vb2_set_plane_payload(vb, 0, size);
return 0;
}
-static void set_dma_dest_params(int dma_ch, struct omap1_cam_buf *buf)
-{
- dma_addr_t dma_addr =
- vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
- unsigned int block_size = vb2_plane_size(&buf->vb.vb2_buf, 0);
-
- omap_set_dma_dest_params(dma_ch,
- OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0);
- omap_set_dma_transfer_params(dma_ch,
- OMAP_DMA_DATA_TYPE_S32, DMA_FRAME_SIZE,
- block_size >> (DMA_FRAME_SHIFT + DMA_ELEMENT_SHIFT),
- DMA_SYNC, 0, 0);
-}
-
-static struct omap1_cam_buf *prepare_next_vb(struct omap1_cam_dev *pcdev)
-{
- struct omap1_cam_buf *buf;
-
- /*
- * If there is already a buffer pointed out by the pcdev->ready,
- * (re)use it, otherwise try to fetch and configure a new one.
- */
- buf = pcdev->ready;
- if (!buf) {
- if (list_empty(&pcdev->capture))
- return buf;
- buf = list_entry(pcdev->capture.next,
- struct omap1_cam_buf, queue);
- pcdev->ready = buf;
- list_del_init(&buf->queue);
- }
-
- /*
- * In CONTIG mode, we can safely enter next buffer parameters
- * into the DMA programming register set after the DMA
- * has already been activated on the previous buffer
- */
- set_dma_dest_params(pcdev->dma_ch, buf);
-
- return buf;
-}
-
-static void start_capture(struct omap1_cam_dev *pcdev)
+static void omap1_dma_complete(void *data)
{
- struct omap1_cam_buf *buf = pcdev->active;
- u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
- u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN;
-
- if (WARN_ON(!buf))
- return;
-
- /*
- * Enable start of frame interrupt, which we will use for activating
- * our end of frame watchdog when capture actually starts.
- */
- mode |= EN_V_UP;
-
- if (unlikely(ctrlclock & LCLK_EN))
- /* stop pixel clock before FIFO reset */
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
- /* reset FIFO */
- CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO);
-
- omap_start_dma(pcdev->dma_ch);
-
- /* (re)enable pixel clock */
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN);
- /* release FIFO reset */
- CAM_WRITE(pcdev, MODE, mode);
-}
-
-static void suspend_capture(struct omap1_cam_dev *pcdev)
-{
- u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
-
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
- omap_stop_dma(pcdev->dma_ch);
-}
+ struct omap1_cam_buf *buf = data;
+ struct soc_camera_device *icd =
+ soc_camera_from_vb2q(buf->vb.vb2_buf.vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
-static void disable_capture(struct omap1_cam_dev *pcdev)
-{
- u32 mode = CAM_READ_CACHE(pcdev, MODE);
+ spin_lock_irq(&pcdev->lock);
+ list_del_init(&buf->queue);
+ spin_unlock_irq(&pcdev->lock);
- CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA));
+ buf->vb.field = V4L2_FIELD_NONE;
+ buf->vb.sequence = pcdev->sequence++;
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
}
static void omap1_videobuf_queue(struct vb2_buffer *vb)
@@ -335,214 +274,87 @@ static void omap1_videobuf_queue(struct vb2_buffer *vb)
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
struct omap1_cam_buf *buf = vb2_to_omap1_cam_buf(vbuf);
- u32 mode;
-
- list_add_tail(&buf->queue, &pcdev->capture);
-
- if (pcdev->active) {
- /*
- * Capture in progress, so don't touch pcdev->ready even if
- * empty. Since the transfer of the DMA programming register set
- * content to the DMA working register set is done automatically
- * by the DMA hardware, this can pretty well happen while we
- * are keeping the lock here. Leave fetching it from the queue
- * to be done when a next DMA interrupt occures instead.
- */
- return;
- }
-
- WARN_ON(pcdev->ready);
-
- buf = prepare_next_vb(pcdev);
- if (WARN_ON(!buf))
- return;
-
- pcdev->active = buf;
- pcdev->ready = NULL;
-
- dev_dbg(icd->parent,
- "%s: capture not active, setup FIFO, start DMA\n", __func__);
- mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK;
- mode |= THRESHOLD_LEVEL << THRESHOLD_SHIFT;
- CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA);
-
- start_capture(pcdev);
-}
-
-static void omap1_videobuf_release(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct omap1_cam_buf *buf = vb2_to_omap1_cam_buf(vbuf);
-
- list_del_init(&buf->queue);
-}
-
-static int omap1_videobuf_init(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct omap1_cam_buf *buf = vb2_to_omap1_cam_buf(vbuf);
-
- INIT_LIST_HEAD(&buf->queue);
- return 0;
-}
-
-static void videobuf_done(struct omap1_cam_dev *pcdev,
- enum vb2_buffer_state result)
-{
- struct omap1_cam_buf *buf = pcdev->active;
- struct vb2_v4l2_buffer *vb;
- struct device *dev = pcdev->soc_host.icd->parent;
-
- if (WARN_ON(!buf)) {
- suspend_capture(pcdev);
- disable_capture(pcdev);
- return;
+ dma_addr_t dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ unsigned int size = vb2_plane_size(vb, 0);
+ struct dma_async_tx_descriptor *dma_desc;
+
+ dma_desc = dmaengine_prep_slave_single(pcdev->dma_chan, dma_addr, size,
+ DMA_DEV_TO_MEM, DMA_CTRL_ACK);
+ if (!dma_desc) {
+ dev_err(icd->parent, "Failed to prepare DMA transfer\n");
+ goto err;
}
- if (result == VB2_BUF_STATE_ERROR)
- suspend_capture(pcdev);
-
- vb = &buf->vb;
- if (!pcdev->ready && result != VB2_BUF_STATE_ERROR) {
- /*
- * No next buffer has been entered into the DMA
- * programming register set on time (could be done only
- * while the previous DMA interurpt was processed, not
- * later), so the last DMA block (whole buffer) is
- * about to be reused by the just autoreinitialized DMA
- * engine, and overwritten with next frame data. Best we
- * can do is stopping the capture as soon as possible,
- * hopefully before the next frame start.
- */
- suspend_capture(pcdev);
+ dma_desc->callback = omap1_dma_complete;
+ dma_desc->callback_param = (void *)buf;
+ if (dma_submit_error(dmaengine_submit(dma_desc))) {
+ dev_err(icd->parent, "DMA submission failed\n");
+ goto err;
}
- vb->vb2_buf.timestamp = ktime_get_ns();
- if (result != VB2_BUF_STATE_ERROR)
- vb->sequence = pcdev->sequence++;
- vb2_buffer_done(&vb->vb2_buf, result);
- /* shift in next buffer */
- buf = pcdev->ready;
- pcdev->active = buf;
- pcdev->ready = NULL;
-
- if (!buf) {
- /*
- * No next buffer was ready on time (see above), so
- * indicate error condition to force capture restart or
- * stop, depending on next buffer already queued or not.
- */
- result = VB2_BUF_STATE_ERROR;
- prepare_next_vb(pcdev);
-
- buf = pcdev->ready;
- pcdev->active = buf;
- pcdev->ready = NULL;
- }
-
- if (!buf) {
- dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__);
- disable_capture(pcdev);
- return;
- }
+ spin_lock_irq(&pcdev->lock);
+ list_add_tail(&buf->queue, &pcdev->capture);
+ spin_unlock_irq(&pcdev->lock);
- /*
- * the current buffer parameters had already
- * been entered into the DMA programming register set while the
- * buffer was fetched with prepare_next_vb(), they may have also
- * been transferred into the runtime set and already active if
- * the DMA still running.
- */
+ if (vb2_is_streaming(vb->vb2_queue))
+ dma_async_issue_pending(pcdev->dma_chan);
- if (result == VB2_BUF_STATE_ERROR) {
- dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n",
- __func__);
- start_capture(pcdev);
- }
+ return;
- /*
- * Finally, try fetching next buffer.
- * That will also enter it into the DMA programming
- * register set, making it ready for next DMA autoreinitialization.
- */
- prepare_next_vb(pcdev);
+err:
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
}
-static void omap1_stop_streaming(struct vb2_queue *vq)
+static int omap1_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
- struct omap1_cam_buf *buf, *tmp;
-
- spin_lock_irq(&pcdev->lock);
-
- list_for_each_entry_safe(buf, tmp, &pcdev->capture, queue) {
- list_del_init(&buf->queue);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- }
-
- if (pcdev->ready)
- videobuf_done(pcdev, VB2_BUF_STATE_ERROR);
- if (pcdev->active)
- videobuf_done(pcdev, VB2_BUF_STATE_ERROR);
-
- spin_unlock_irq(&pcdev->lock);
-}
+ u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ u32 mode = CAM_READ_CACHE(pcdev, MODE);
-static void dma_isr(int channel, unsigned short status, void *data)
-{
- struct omap1_cam_dev *pcdev = data;
- struct omap1_cam_buf *buf = pcdev->active;
- unsigned long flags;
+ mode &= ~THRESHOLD_MASK;
+ mode |= (THRESHOLD_LEVEL << THRESHOLD_SHIFT) | EN_FIFO_FULL | DMA;
+ CAM_WRITE(pcdev, MODE, mode);
- spin_lock_irqsave(&pcdev->lock, flags);
+ if (unlikely(ctrlclock & LCLK_EN))
+ /* stop pixel clock before FIFO reset */
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+ /* reset FIFO */
+ CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO);
- if (WARN_ON(!buf)) {
- suspend_capture(pcdev);
- disable_capture(pcdev);
- goto out;
- }
+ dma_async_issue_pending(pcdev->dma_chan);
- /*
- * Assume we have just managed to collect the
- * whole frame, hopefully before our end of frame watchdog is
- * triggered. Then, all we have to do is disabling the watchdog
- * for this frame, and calling videobuf_done() with success
- * indicated.
- */
- CAM_WRITE(pcdev, MODE, CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN);
- videobuf_done(pcdev, VB2_BUF_STATE_DONE);
+ /* (re)enable pixel clock */
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN);
+ /* release FIFO reset */
+ CAM_WRITE(pcdev, MODE, mode);
-out:
- spin_unlock_irqrestore(&pcdev->lock, flags);
+ return 0;
}
-static irqreturn_t cam_isr(int irq, void *data)
+static irqreturn_t omap1_cam_isr(int irq, void *data)
{
struct omap1_cam_dev *pcdev = data;
- struct device *dev = pcdev->soc_host.icd->parent;
- struct omap1_cam_buf *buf = pcdev->active;
+ struct device *dev;
u32 it_status;
unsigned long flags;
-
- it_status = CAM_READ(pcdev, IT_STATUS);
- if (!it_status)
- return IRQ_NONE;
+ irqreturn_t ret;
spin_lock_irqsave(&pcdev->lock, flags);
- if (WARN_ON(!buf)) {
- dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
- __func__, it_status);
- suspend_capture(pcdev);
- disable_capture(pcdev);
+ ret = IRQ_HANDLED;
+
+ it_status = CAM_READ(pcdev, IT_STATUS);
+ if (!it_status) {
+ ret = IRQ_NONE;
goto out;
}
+ dev = pcdev->soc_host.icd->parent;
+
if (unlikely(it_status & FIFO_FULL)) {
dev_warn(dev, "%s: FIFO overflow\n", __func__);
-
} else if (it_status & V_DOWN) {
/* End of video frame watchdog
* the watchdog is disabled with
@@ -552,7 +364,6 @@ static irqreturn_t cam_isr(int irq, void *data)
*/
dev_notice(dev, "%s: unexpected end of video frame\n",
__func__);
-
} else if (it_status & V_UP) {
u32 mode;
@@ -568,28 +379,47 @@ static irqreturn_t cam_isr(int irq, void *data)
}
CAM_WRITE(pcdev, MODE, mode);
goto out;
-
} else {
dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
__func__, it_status);
goto out;
}
- videobuf_done(pcdev, VB2_BUF_STATE_ERROR);
out:
spin_unlock_irqrestore(&pcdev->lock, flags);
- return IRQ_HANDLED;
+ return ret;
+}
+
+static void omap1_stop_streaming(struct vb2_queue *vq)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ struct omap1_cam_buf *buf, *tmp;
+ u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ u32 mode = CAM_READ_CACHE(pcdev, MODE);
+
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+ dmaengine_terminate_sync(pcdev->dma_chan);
+ CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA));
+
+ spin_lock_irq(&pcdev->lock);
+ list_for_each_entry_safe(buf, tmp, &pcdev->capture, queue) {
+ list_del_init(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irq(&pcdev->lock);
}
static struct vb2_ops omap1_videobuf_ops = {
- .queue_setup = omap1_videobuf_setup,
- .buf_prepare = omap1_videobuf_prepare,
- .buf_queue = omap1_videobuf_queue,
- .buf_cleanup = omap1_videobuf_release,
- .buf_init = omap1_videobuf_init,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
- .stop_streaming = omap1_stop_streaming,
+ .queue_setup = omap1_videobuf_setup,
+ .buf_init = omap1_videobuf_init,
+ .buf_prepare = omap1_videobuf_prepare,
+ .buf_queue = omap1_videobuf_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = omap1_start_streaming,
+ .stop_streaming = omap1_stop_streaming,
};
@@ -671,9 +501,6 @@ static void omap1_cam_clock_stop(struct soc_camera_host *ici)
struct omap1_cam_dev *pcdev = ici->priv;
u32 ctrlclock;
- suspend_capture(pcdev);
- disable_capture(pcdev);
-
sensor_reset(pcdev, true);
/* disable and release system clocks */
@@ -1206,6 +1033,8 @@ static int omap1_cam_probe(struct platform_device *pdev)
void __iomem *base;
unsigned int irq;
int err = 0;
+ dma_cap_mask_t mask;
+ struct dma_slave_config dma_cfg;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
@@ -1260,24 +1089,31 @@ static int omap1_cam_probe(struct platform_device *pdev)
sensor_reset(pcdev, true);
- err = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, DRIVER_NAME,
- dma_isr, (void *)pcdev, &pcdev->dma_ch);
- if (err < 0) {
+ pcdev->dma_rq = OMAP_DMA_CAMERA_IF_RX;
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ pcdev->dma_chan = __dma_request_channel(&mask, omap_dma_filter_fn,
+ (void *)&pcdev->dma_rq);
+ if (!pcdev->dma_chan) {
dev_err(&pdev->dev, "Can't request DMA for OMAP1 Camera\n");
return -EBUSY;
}
- dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_ch);
+ dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chan->chan_id);
/* preconfigure DMA */
- omap_set_dma_src_params(pcdev->dma_ch, OMAP_DMA_PORT_TIPB,
- OMAP_DMA_AMODE_CONSTANT, res->start + REG_CAMDATA,
- 0, 0);
- omap_set_dma_dest_burst_mode(pcdev->dma_ch, OMAP_DMA_DATA_BURST_4);
- /* setup DMA autoinitialization */
- omap_dma_link_lch(pcdev->dma_ch, pcdev->dma_ch);
-
- err = devm_request_irq(&pdev->dev, pcdev->irq, cam_isr, 0, DRIVER_NAME,
- pcdev);
+ memset(&dma_cfg, 0, sizeof(dma_cfg));
+ dma_cfg.direction = DMA_DEV_TO_MEM;
+ dma_cfg.src_addr = res->start + REG_CAMDATA;
+ dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dma_cfg.src_maxburst = DMA_FRAME_SIZE;
+ err = dmaengine_slave_config(pcdev->dma_chan, &dma_cfg);
+ if (err) {
+ dev_err(&pdev->dev, "DMA slave configuration failed\n");
+ goto exit_free_dma;
+ }
+
+ err = devm_request_irq(&pdev->dev, pcdev->irq, omap1_cam_isr, 0,
+ DRIVER_NAME, pcdev);
if (err) {
dev_err(&pdev->dev, "Camera interrupt register failed\n");
goto exit_free_dma;
@@ -1310,7 +1146,7 @@ static int omap1_cam_probe(struct platform_device *pdev)
exit_free_ctx:
vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
exit_free_dma:
- omap_free_dma(pcdev->dma_ch);
+ dma_release_channel(pcdev->dma_chan);
return err;
}
@@ -1320,7 +1156,7 @@ static int omap1_cam_remove(struct platform_device *pdev)
struct omap1_cam_dev *pcdev = container_of(soc_host,
struct omap1_cam_dev, soc_host);
- omap_free_dma(pcdev->dma_ch);
+ dma_release_channel(pcdev->dma_chan);
soc_camera_host_unregister(soc_host);
Created and tested on Amstrad Delta on top of Linux-4.7-rc3 with "staging: media: omap1: convert to videobuf2" applied. Signed-off-by: Janusz Krzysztofik <jmkrzyszt@gmail.com> --- drivers/staging/media/omap1/Kconfig | 2 +- drivers/staging/media/omap1/omap1_camera.c | 432 +++++++++-------------------- 2 files changed, 135 insertions(+), 299 deletions(-)