#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/of.h>
+#include <linux/kthread.h>
#include "regs-pxp_v2.h"
static LIST_HEAD(head);
static int timeout_in_ms = 600;
static unsigned int block_size;
+static struct kmem_cache *tx_desc_cache;
struct pxp_dma {
struct dma_device dma;
struct device *dev;
struct pxp_dma pxp_dma;
struct pxp_channel channel[NR_PXP_VIRT_CHANNEL];
- wait_queue_head_t done;
struct work_struct work;
/* describes most recent processing configuration */
/* to turn clock off when pxp is inactive */
struct timer_list clk_timer;
+
+ /* for pxp config dispatch asynchronously*/
+ struct task_struct *dispatch;
+ wait_queue_head_t thread_waitq;
+ struct completion complete;
};
#define to_pxp_dma(d) container_of(d, struct pxp_dma, dma)
#define PXP_DEF_BUFS 2
#define PXP_MIN_PIX 8
-#define PXP_WAITCON ((__raw_readl(pxp->base + HW_PXP_STAT) & \
- BM_PXP_STAT_IRQ) != BM_PXP_STAT_IRQ)
-
static uint32_t pxp_s0_formats[] = {
PXP_PIX_FMT_RGB32,
PXP_PIX_FMT_RGB565,
struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state;
struct pxp_layer_param *olparams_data = &pxp_conf->ol_param[layer_no];
dma_addr_t phys_addr = olparams_data->paddr;
+ u32 pitch = olparams_data->stride ? olparams_data->stride :
+ olparams_data->width;
+
__raw_writel(phys_addr, pxp->base + HW_PXP_AS_BUF);
/* Fixme */
if ((olparams_data->pixel_fmt == PXP_PIX_FMT_BGRA32) |
(olparams_data->pixel_fmt == PXP_PIX_FMT_RGB32)) {
- __raw_writel(olparams_data->width << 2,
+ __raw_writel(pitch << 2,
pxp->base + HW_PXP_AS_PITCH);
} else if (olparams_data->pixel_fmt == PXP_PIX_FMT_RGB565) {
- __raw_writel(olparams_data->width << 1,
+ __raw_writel(pitch << 1,
pxp->base + HW_PXP_AS_PITCH);
} else {
__raw_writel(0, pxp->base + HW_PXP_AS_PITCH);
dma_addr_t Y, U, V;
dma_addr_t Y1, U1, V1;
u32 offset, bpp = 1;
+ u32 pitch = s0_params->stride ? s0_params->stride :
+ s0_params->width;
Y = s0_params->paddr;
__raw_writel(Y1, pxp->base + HW_PXP_PS_BUF);
if ((s0_params->pixel_fmt == PXP_PIX_FMT_YUV420P) ||
(s0_params->pixel_fmt == PXP_PIX_FMT_YVU420P) ||
- (s0_params->pixel_fmt == PXP_PIX_FMT_GREY)) {
+ (s0_params->pixel_fmt == PXP_PIX_FMT_GREY) ||
+ (s0_params->pixel_fmt == PXP_PIX_FMT_YUV422P)) {
/* Set to 1 if YUV format is 4:2:2 rather than 4:2:0 */
int s = 2;
+ if (s0_params->pixel_fmt == PXP_PIX_FMT_YUV422P)
+ s = 1;
offset = proc_data->srect.top * s0_params->width / 4 +
proc_data->srect.left / 2;
U1 = U + offset;
V = U + ((s0_params->width * s0_params->height) >> s);
V1 = V + offset;
- __raw_writel(U1, pxp->base + HW_PXP_PS_UBUF);
- __raw_writel(V1, pxp->base + HW_PXP_PS_VBUF);
+ if (s0_params->pixel_fmt == PXP_PIX_FMT_YVU420P) {
+ __raw_writel(V1, pxp->base + HW_PXP_PS_UBUF);
+ __raw_writel(U1, pxp->base + HW_PXP_PS_VBUF);
+ } else {
+ __raw_writel(U1, pxp->base + HW_PXP_PS_UBUF);
+ __raw_writel(V1, pxp->base + HW_PXP_PS_VBUF);
+ }
} else if ((s0_params->pixel_fmt == PXP_PIX_FMT_NV12) ||
(s0_params->pixel_fmt == PXP_PIX_FMT_NV21) ||
(s0_params->pixel_fmt == PXP_PIX_FMT_NV16) ||
s0_params->pixel_fmt == PXP_PIX_FMT_NV12 ||
s0_params->pixel_fmt == PXP_PIX_FMT_NV21 ||
s0_params->pixel_fmt == PXP_PIX_FMT_NV16 ||
- s0_params->pixel_fmt == PXP_PIX_FMT_NV61) {
- __raw_writel(s0_params->width, pxp->base + HW_PXP_PS_PITCH);
+ s0_params->pixel_fmt == PXP_PIX_FMT_NV61 ||
+ s0_params->pixel_fmt == PXP_PIX_FMT_YUV422P) {
+ __raw_writel(pitch, pxp->base + HW_PXP_PS_PITCH);
}
else if (s0_params->pixel_fmt == PXP_PIX_FMT_GY04)
- __raw_writel(s0_params->width >> 1,
+ __raw_writel(pitch >> 1,
pxp->base + HW_PXP_PS_PITCH);
else if (s0_params->pixel_fmt == PXP_PIX_FMT_RGB32)
- __raw_writel(s0_params->width << 2,
+ __raw_writel(pitch << 2,
pxp->base + HW_PXP_PS_PITCH);
else if (s0_params->pixel_fmt == PXP_PIX_FMT_UYVY ||
s0_params->pixel_fmt == PXP_PIX_FMT_YUYV ||
s0_params->pixel_fmt == PXP_PIX_FMT_VYUY ||
s0_params->pixel_fmt == PXP_PIX_FMT_YVYU)
- __raw_writel(s0_params->width << 1,
+ __raw_writel(pitch << 1,
pxp->base + HW_PXP_PS_PITCH);
else if (s0_params->pixel_fmt == PXP_PIX_FMT_RGB565)
- __raw_writel(s0_params->width << 1,
+ __raw_writel(pitch << 1,
pxp->base + HW_PXP_PS_PITCH);
else
__raw_writel(0, pxp->base + HW_PXP_PS_PITCH);
jiffies + msecs_to_jiffies(timeout_in_ms));
}
-static struct pxp_tx_desc *pxpdma_first_active(struct pxp_channel *pxp_chan)
-{
- return list_entry(pxp_chan->active_list.next, struct pxp_tx_desc, list);
-}
-
static struct pxp_tx_desc *pxpdma_first_queued(struct pxp_channel *pxp_chan)
{
return list_entry(pxp_chan->queue.next, struct pxp_tx_desc, list);
struct pxp_tx_desc *child;
int i = 0;
- /* so far we presume only one transaction on active_list */
/* S0 */
- desc = pxpdma_first_active(pxp_chan);
+ desc = list_first_entry(&head, struct pxp_tx_desc, list);
memcpy(&pxp->pxp_conf_state.s0_param,
&desc->layer_param.s0_param, sizeof(struct pxp_layer_param));
memcpy(&pxp->pxp_conf_state.proc_data,
static void pxpdma_dostart_work(struct pxps *pxp)
{
struct pxp_channel *pxp_chan = NULL;
- unsigned long flags, flags1;
-
- while (__raw_readl(pxp->base + HW_PXP_CTRL) & BM_PXP_CTRL_ENABLE)
- ;
+ unsigned long flags;
+ struct pxp_tx_desc *desc = NULL;
spin_lock_irqsave(&pxp->lock, flags);
- if (list_empty(&head)) {
- pxp->pxp_ongoing = 0;
- spin_unlock_irqrestore(&pxp->lock, flags);
- return;
- }
- pxp_chan = list_entry(head.next, struct pxp_channel, list);
+ desc = list_entry(head.next, struct pxp_tx_desc, list);
+ pxp_chan = to_pxp_channel(desc->txd.chan);
- spin_lock_irqsave(&pxp_chan->lock, flags1);
- if (!list_empty(&pxp_chan->active_list)) {
- struct pxp_tx_desc *desc;
- /* REVISIT */
- desc = pxpdma_first_active(pxp_chan);
- __pxpdma_dostart(pxp_chan);
- }
- spin_unlock_irqrestore(&pxp_chan->lock, flags1);
+ __pxpdma_dostart(pxp_chan);
/* Configure PxP */
pxp_config(pxp, pxp_chan);
spin_unlock_irqrestore(&pxp->lock, flags);
}
-static void pxpdma_dequeue(struct pxp_channel *pxp_chan, struct list_head *list)
+static void pxpdma_dequeue(struct pxp_channel *pxp_chan, struct pxps *pxp)
{
+ unsigned long flags;
struct pxp_tx_desc *desc = NULL;
+
do {
desc = pxpdma_first_queued(pxp_chan);
- list_move_tail(&desc->list, list);
+ spin_lock_irqsave(&pxp->lock, flags);
+ list_move_tail(&desc->list, &head);
+ spin_unlock_irqrestore(&pxp->lock, flags);
} while (!list_empty(&pxp_chan->queue));
}
struct pxp_tx_desc *desc = to_tx_desc(tx);
struct pxp_channel *pxp_chan = to_pxp_channel(tx->chan);
dma_cookie_t cookie;
- unsigned long flags;
dev_dbg(&pxp_chan->dma_chan.dev->device, "received TX\n");
- mutex_lock(&pxp_chan->chan_mutex);
+ /* pxp_chan->lock can be taken under ichan->lock, but not v.v. */
+ spin_lock(&pxp_chan->lock);
cookie = pxp_chan->dma_chan.cookie;
pxp_chan->dma_chan.cookie = cookie;
tx->cookie = cookie;
- /* pxp_chan->lock can be taken under ichan->lock, but not v.v. */
- spin_lock_irqsave(&pxp_chan->lock, flags);
-
/* Here we add the tx descriptor to our PxP task queue. */
list_add_tail(&desc->list, &pxp_chan->queue);
- spin_unlock_irqrestore(&pxp_chan->lock, flags);
+ spin_unlock(&pxp_chan->lock);
dev_dbg(&pxp_chan->dma_chan.dev->device, "done TX\n");
- mutex_unlock(&pxp_chan->chan_mutex);
return cookie;
}
-/* Called with pxp_chan->chan_mutex held */
-static int pxp_desc_alloc(struct pxp_channel *pxp_chan, int n)
-{
- struct pxp_tx_desc *desc = vmalloc(n * sizeof(struct pxp_tx_desc));
-
- if (!desc)
- return -ENOMEM;
-
- pxp_chan->n_tx_desc = n;
- pxp_chan->desc = desc;
- INIT_LIST_HEAD(&pxp_chan->active_list);
- INIT_LIST_HEAD(&pxp_chan->queue);
- INIT_LIST_HEAD(&pxp_chan->free_list);
-
- while (n--) {
- struct dma_async_tx_descriptor *txd = &desc->txd;
-
- memset(txd, 0, sizeof(*txd));
- INIT_LIST_HEAD(&desc->tx_list);
- dma_async_tx_descriptor_init(txd, &pxp_chan->dma_chan);
- txd->tx_submit = pxp_tx_submit;
-
- list_add(&desc->list, &pxp_chan->free_list);
-
- desc++;
- }
-
- return 0;
-}
-
/**
* pxp_init_channel() - initialize a PXP channel.
* @pxp_dma: PXP DMA context.
static int pxp_init_channel(struct pxp_dma *pxp_dma,
struct pxp_channel *pxp_chan)
{
- unsigned long flags;
- struct pxps *pxp = to_pxp(pxp_dma);
- int ret = 0, n_desc = 0;
+ int ret = 0;
/*
* We are using _virtual_ channel here.
* (i.e., pxp_tx_desc) here.
*/
- spin_lock_irqsave(&pxp->lock, flags);
-
- /* max desc nr: S0+OL+OUT = 1+8+1 */
- n_desc = 16;
-
- spin_unlock_irqrestore(&pxp->lock, flags);
-
- if (n_desc && !pxp_chan->desc)
- ret = pxp_desc_alloc(pxp_chan, n_desc);
-
- return ret;
-}
-
-/**
- * pxp_uninit_channel() - uninitialize a PXP channel.
- * @pxp_dma: PXP DMA context.
- * @pchan: pointer to the channel object.
- * @return 0 on success or negative error code on failure.
- */
-static int pxp_uninit_channel(struct pxp_dma *pxp_dma,
- struct pxp_channel *pxp_chan)
-{
- int ret = 0;
-
- if (pxp_chan->desc)
- vfree(pxp_chan->desc);
-
- pxp_chan->desc = NULL;
+ INIT_LIST_HEAD(&pxp_chan->queue);
return ret;
}
struct pxps *pxp = dev_id;
struct pxp_channel *pxp_chan;
struct pxp_tx_desc *desc;
+ struct pxp_tx_desc *child, *_child;
dma_async_tx_callback callback;
void *callback_param;
unsigned long flags;
return IRQ_NONE;
}
- pxp_chan = list_entry(head.next, struct pxp_channel, list);
- list_del_init(&pxp_chan->list);
-
- if (list_empty(&pxp_chan->active_list)) {
- pr_debug("PXP_IRQ pxp_chan->active_list empty. chan_id %d\n",
- pxp_chan->dma_chan.chan_id);
- pxp->pxp_ongoing = 0;
- spin_unlock_irqrestore(&pxp->lock, flags);
- return IRQ_NONE;
- }
-
/* Get descriptor and call callback */
- desc = pxpdma_first_active(pxp_chan);
+ desc = list_entry(head.next, struct pxp_tx_desc, list);
+ pxp_chan = to_pxp_channel(desc->txd.chan);
pxp_chan->completed = desc->txd.cookie;
pxp_chan->status = PXP_CHANNEL_INITIALIZED;
- list_splice_init(&desc->tx_list, &pxp_chan->free_list);
- list_move(&desc->list, &pxp_chan->free_list);
+ list_for_each_entry_safe(child, _child, &desc->tx_list, list) {
+ list_del_init(&child->list);
+ kmem_cache_free(tx_desc_cache, (void *)child);
+ }
+ list_del_init(&desc->list);
+ kmem_cache_free(tx_desc_cache, (void *)desc);
- wake_up(&pxp->done);
+ complete(&pxp->complete);
pxp->pxp_ongoing = 0;
mod_timer(&pxp->clk_timer, jiffies + msecs_to_jiffies(timeout_in_ms));
return IRQ_HANDLED;
}
-/* called with pxp_chan->lock held */
-static struct pxp_tx_desc *pxpdma_desc_get(struct pxp_channel *pxp_chan)
+/* allocate/free dma tx descriptor dynamically*/
+static struct pxp_tx_desc *pxpdma_desc_alloc(struct pxp_channel *pxp_chan)
{
- struct pxp_tx_desc *desc, *_desc;
- struct pxp_tx_desc *ret = NULL;
+ struct pxp_tx_desc *desc = NULL;
+ struct dma_async_tx_descriptor *txd = NULL;
- list_for_each_entry_safe(desc, _desc, &pxp_chan->free_list, list) {
- list_del_init(&desc->list);
- ret = desc;
- break;
- }
+ desc = kmem_cache_alloc(tx_desc_cache, GFP_KERNEL | __GFP_ZERO);
+ if (desc == NULL)
+ return NULL;
- return ret;
-}
+ INIT_LIST_HEAD(&desc->list);
+ INIT_LIST_HEAD(&desc->tx_list);
+ txd = &desc->txd;
+ dma_async_tx_descriptor_init(txd, &pxp_chan->dma_chan);
+ txd->tx_submit = pxp_tx_submit;
-/* called with pxp_chan->lock held */
-static void pxpdma_desc_put(struct pxp_channel *pxp_chan,
- struct pxp_tx_desc *desc)
-{
- if (desc) {
- struct device *dev = &pxp_chan->dma_chan.dev->device;
- struct pxp_tx_desc *child;
-
- list_for_each_entry(child, &desc->tx_list, list)
- dev_info(dev, "moving child desc %p to freelist\n", child);
- list_splice_init(&desc->tx_list, &pxp_chan->free_list);
- dev_info(dev, "moving desc %p to freelist\n", desc);
- list_add(&desc->list, &pxp_chan->free_list);
- }
+ return desc;
}
/* Allocate and initialise a transfer descriptor. */
struct pxp_tx_desc *desc = NULL;
struct pxp_tx_desc *first = NULL, *prev = NULL;
struct scatterlist *sg;
- unsigned long flags;
dma_addr_t phys_addr;
int i;
if (unlikely(sg_len < 2))
return NULL;
- spin_lock_irqsave(&pxp_chan->lock, flags);
for_each_sg(sgl, sg, sg_len, i) {
- desc = pxpdma_desc_get(pxp_chan);
+ desc = pxpdma_desc_alloc(pxp_chan);
if (!desc) {
- pxpdma_desc_put(pxp_chan, first);
- dev_err(chan->device->dev, "Can't get DMA desc.\n");
- spin_unlock_irqrestore(&pxp_chan->lock, flags);
+ dev_err(chan->device->dev, "no enough memory to allocate tx descriptor\n");
return NULL;
}
prev = desc;
}
- spin_unlock_irqrestore(&pxp_chan->lock, flags);
pxp->pxp_conf_state.layer_nr = sg_len;
first->txd.flags = tx_flags;
struct pxp_channel *pxp_chan = to_pxp_channel(chan);
struct pxp_dma *pxp_dma = to_pxp_dma(chan->device);
struct pxps *pxp = to_pxp(pxp_dma);
- unsigned long flags0, flags;
- spin_lock_irqsave(&pxp->lock, flags0);
- spin_lock_irqsave(&pxp_chan->lock, flags);
+ spin_lock(&pxp_chan->lock);
- if (!list_empty(&pxp_chan->queue)) {
- pxpdma_dequeue(pxp_chan, &pxp_chan->active_list);
- pxp_chan->status = PXP_CHANNEL_READY;
- list_add_tail(&pxp_chan->list, &head);
- } else {
- spin_unlock_irqrestore(&pxp_chan->lock, flags);
- spin_unlock_irqrestore(&pxp->lock, flags0);
+ if (list_empty(&pxp_chan->queue)) {
+ spin_unlock(&pxp_chan->lock);
return;
}
- spin_unlock_irqrestore(&pxp_chan->lock, flags);
- spin_unlock_irqrestore(&pxp->lock, flags0);
- pxp_clk_enable(pxp);
- if (!wait_event_interruptible_timeout(pxp->done, PXP_WAITCON, 2 * HZ) ||
- signal_pending(current)) {
- pxp_clk_disable(pxp);
- return;
- }
+ pxpdma_dequeue(pxp_chan, pxp);
+ pxp_chan->status = PXP_CHANNEL_READY;
- spin_lock_irqsave(&pxp->lock, flags);
- pxp->pxp_ongoing = 1;
- spin_unlock_irqrestore(&pxp->lock, flags);
- pxpdma_dostart_work(pxp);
+ spin_unlock(&pxp_chan->lock);
+
+ pxp_clk_enable(pxp);
+ wake_up_interruptible(&pxp->thread_waitq);
}
static void __pxp_terminate_all(struct dma_chan *chan)
{
struct pxp_channel *pxp_chan = to_pxp_channel(chan);
- unsigned long flags;
-
- /* pchan->queue is modified in ISR, have to spinlock */
- spin_lock_irqsave(&pxp_chan->lock, flags);
- list_splice_init(&pxp_chan->queue, &pxp_chan->free_list);
- list_splice_init(&pxp_chan->active_list, &pxp_chan->free_list);
-
- spin_unlock_irqrestore(&pxp_chan->lock, flags);
pxp_chan->status = PXP_CHANNEL_INITIALIZED;
}
if (cmd != DMA_TERMINATE_ALL)
return -ENXIO;
- mutex_lock(&pxp_chan->chan_mutex);
+ spin_lock(&pxp_chan->lock);
__pxp_terminate_all(chan);
- mutex_unlock(&pxp_chan->chan_mutex);
+ spin_unlock(&pxp_chan->lock);
return 0;
}
static void pxp_free_chan_resources(struct dma_chan *chan)
{
struct pxp_channel *pxp_chan = to_pxp_channel(chan);
- struct pxp_dma *pxp_dma = to_pxp_dma(chan->device);
- mutex_lock(&pxp_chan->chan_mutex);
+ spin_lock(&pxp_chan->lock);
__pxp_terminate_all(chan);
pxp_chan->status = PXP_CHANNEL_FREE;
- pxp_uninit_channel(pxp_dma, pxp_chan);
-
- mutex_unlock(&pxp_chan->chan_mutex);
+ spin_unlock(&pxp_chan->lock);
}
static enum dma_status pxp_tx_status(struct dma_chan *chan,
struct dma_chan *dma_chan = &pxp_chan->dma_chan;
spin_lock_init(&pxp_chan->lock);
- mutex_init(&pxp_chan->chan_mutex);
/* Only one EOF IRQ for PxP, shared by all channels */
pxp_chan->eof_irq = pxp->irq;
};
MODULE_DEVICE_TABLE(of, imx_pxpdma_dt_ids);
+static int has_pending_task(struct pxps *pxp, struct pxp_channel *task)
+{
+ int found;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pxp->lock, flags);
+ found = !list_empty(&head);
+ spin_unlock_irqrestore(&pxp->lock, flags);
+
+ return found;
+}
+
+static int pxp_dispatch_thread(void *argv)
+{
+ struct pxps *pxp = (struct pxps *)argv;
+ struct pxp_channel *pending = NULL;
+ unsigned long flags;
+
+ while (!kthread_should_stop()) {
+ int ret;
+ ret = wait_event_interruptible(pxp->thread_waitq,
+ has_pending_task(pxp, pending));
+ if (signal_pending(current))
+ continue;
+
+ if (kthread_should_stop())
+ break;
+
+ spin_lock_irqsave(&pxp->lock, flags);
+ pxp->pxp_ongoing = 1;
+ spin_unlock_irqrestore(&pxp->lock, flags);
+ init_completion(&pxp->complete);
+ pxpdma_dostart_work(pxp);
+ ret = wait_for_completion_timeout(&pxp->complete, 2 * HZ);
+ if (ret == 0) {
+ printk(KERN_EMERG "%s: task is timeout\n\n", __func__);
+ break;
+ }
+ }
+
+ return 0;
+}
+
static int pxp_probe(struct platform_device *pdev)
{
struct pxps *pxp;
dump_pxp_reg(pxp);
INIT_WORK(&pxp->work, clkoff_callback);
- init_waitqueue_head(&pxp->done);
init_timer(&pxp->clk_timer);
pxp->clk_timer.function = pxp_clkoff_timer;
pxp->clk_timer.data = (unsigned long)pxp;
+ /* allocate a kernel thread to dispatch pxp conf */
+ pxp->dispatch = kthread_run(pxp_dispatch_thread, pxp, "pxp_dispatch");
+ if (IS_ERR(pxp->dispatch)) {
+ err = PTR_ERR(pxp->dispatch);
+ goto exit;
+ }
+ init_waitqueue_head(&pxp->thread_waitq);
+ tx_desc_cache = kmem_cache_create("tx_desc", sizeof(struct pxp_tx_desc),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!tx_desc_cache) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
register_pxp_device();
exit:
struct pxps *pxp = platform_get_drvdata(pdev);
unregister_pxp_device();
+ kmem_cache_destroy(tx_desc_cache);
+ kthread_stop(pxp->dispatch);
cancel_work_sync(&pxp->work);
del_timer_sync(&pxp->clk_timer);
clk_disable_unprepare(pxp->clk);