Re: [PATCH v2 3/3] gpu: ipu-v3: image-convert: Wait for all EOFs before completing a tile
Hi Philipp, On 6/26/20 2:38 AM, Philipp Zabel wrote: Hi Steve, On Thu, 2020-06-25 at 11:13 -0700, Steve Longerbeam wrote: Use a bit-mask of EOF irqs to determine when all required idmac channel EOFs have been received for a tile conversion, and only do tile completion processing after all EOFs have been received. Otherwise it was found that a conversion would stall after the completion of a tile and the start of the next tile, because the input/read idmac channel had not completed and entered idle state, thus locking up the channel when attempting to re-start it for the next tile. Do I understand correctly that there are cases where the output channel EOF IRQ has triggered and the next tile processing is kicked off before the input channel EOF IRQ triggers even without rotation? Yes. What is the cause of this? It would seem that the read channel EOF should occur before the write channel EOF, but there are cases seen where the opposite occurs. Maybe this has to do with idmac channel priorities, the IC PP read/write channels are set to the same priority (low), in which case the IPU should resort to round-robin when handling requests on those channels. Maybe the EOF irq is not signalled until after the IPU has updated CPMEM with status info after the transfers complete, and round-robin selects the write channel before the read channel for the CPMEM updates? Do you have any way to reproduce this? Yes, try a scaling only conversion, 1920x1080.422p -> 1024x768.422p. No rotation needed. Steve regards Philipp Fixes: 0537db801bb01 ("gpu: ipu-v3: image-convert: reconfigure IC per tile") Signed-off-by: Steve Longerbeam --- Changes in v2: - need to clear eof_mask at completion of every tile, not just in convert_start(). --- drivers/gpu/ipu-v3/ipu-image-convert.c | 109 +++-- 1 file changed, 82 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index f8b031ded3cf..aa1d4b6d278f 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -137,6 +137,17 @@ struct ipu_image_convert_ctx; struct ipu_image_convert_chan; struct ipu_image_convert_priv; +enum eof_irq_mask { + EOF_IRQ_IN = BIT(0), + EOF_IRQ_ROT_IN = BIT(1), + EOF_IRQ_OUT = BIT(2), + EOF_IRQ_ROT_OUT = BIT(3), +}; + +#define EOF_IRQ_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT) +#define EOF_IRQ_ROT_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT | \ + EOF_IRQ_ROT_IN | EOF_IRQ_ROT_OUT) + struct ipu_image_convert_ctx { struct ipu_image_convert_chan *chan; @@ -173,6 +184,9 @@ struct ipu_image_convert_ctx { /* where to place converted tile in dest image */ unsigned int out_tile_map[MAX_TILES]; + /* mask of completed EOF irqs at every tile conversion */ + enum eof_irq_mask eof_mask; + struct list_head list; }; @@ -189,6 +203,8 @@ struct ipu_image_convert_chan { struct ipuv3_channel *rotation_out_chan; /* the IPU end-of-frame irqs */ + int in_eof_irq; + int rot_in_eof_irq; int out_eof_irq; int rot_out_eof_irq; @@ -1380,6 +1396,9 @@ static int convert_start(struct ipu_image_convert_run *run, unsigned int tile) dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n", __func__, chan->ic_task, ctx, run, tile, dst_tile); + /* clear EOF irq mask */ + ctx->eof_mask = 0; + if (ipu_rot_mode_is_irt(ctx->rot_mode)) { /* swap width/height for resizer */ dest_width = d_image->tile[dst_tile].height; @@ -1615,7 +1634,7 @@ static bool ic_settings_changed(struct ipu_image_convert_ctx *ctx) } /* hold irqlock when calling */ -static irqreturn_t do_irq(struct ipu_image_convert_run *run) +static irqreturn_t do_tile_complete(struct ipu_image_convert_run *run) { struct ipu_image_convert_ctx *ctx = run->ctx; struct ipu_image_convert_chan *chan = ctx->chan; @@ -1700,6 +1719,7 @@ static irqreturn_t do_irq(struct ipu_image_convert_run *run) ctx->cur_buf_num ^= 1; } + ctx->eof_mask = 0; /* clear EOF irq mask for next tile */ ctx->next_tile++; return IRQ_HANDLED; done: @@ -1715,8 +1735,9 @@ static irqreturn_t eof_irq(int irq, void *data) struct ipu_image_convert_priv *priv = chan->priv; struct ipu_image_convert_ctx *ctx; struct ipu_image_convert_run *run; + irqreturn_t ret = IRQ_HANDLED; + bool tile_complete = false; unsigned long flags; - irqreturn_t ret; spin_lock_irqsave(>irqlock, flags); @@ -1729,27 +1750,33 @@ static irqreturn_t eof_irq(int irq, void *data) ctx = run->ctx; - if (irq == chan->out_eof_irq) { - if (ipu_rot_mode_is_irt(ctx->
[PATCH v2 3/3] gpu: ipu-v3: image-convert: Wait for all EOFs before completing a tile
Use a bit-mask of EOF irqs to determine when all required idmac channel EOFs have been received for a tile conversion, and only do tile completion processing after all EOFs have been received. Otherwise it was found that a conversion would stall after the completion of a tile and the start of the next tile, because the input/read idmac channel had not completed and entered idle state, thus locking up the channel when attempting to re-start it for the next tile. Fixes: 0537db801bb01 ("gpu: ipu-v3: image-convert: reconfigure IC per tile") Signed-off-by: Steve Longerbeam --- Changes in v2: - need to clear eof_mask at completion of every tile, not just in convert_start(). --- drivers/gpu/ipu-v3/ipu-image-convert.c | 109 +++-- 1 file changed, 82 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index f8b031ded3cf..aa1d4b6d278f 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -137,6 +137,17 @@ struct ipu_image_convert_ctx; struct ipu_image_convert_chan; struct ipu_image_convert_priv; +enum eof_irq_mask { + EOF_IRQ_IN = BIT(0), + EOF_IRQ_ROT_IN = BIT(1), + EOF_IRQ_OUT = BIT(2), + EOF_IRQ_ROT_OUT = BIT(3), +}; + +#define EOF_IRQ_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT) +#define EOF_IRQ_ROT_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT | \ + EOF_IRQ_ROT_IN | EOF_IRQ_ROT_OUT) + struct ipu_image_convert_ctx { struct ipu_image_convert_chan *chan; @@ -173,6 +184,9 @@ struct ipu_image_convert_ctx { /* where to place converted tile in dest image */ unsigned int out_tile_map[MAX_TILES]; + /* mask of completed EOF irqs at every tile conversion */ + enum eof_irq_mask eof_mask; + struct list_head list; }; @@ -189,6 +203,8 @@ struct ipu_image_convert_chan { struct ipuv3_channel *rotation_out_chan; /* the IPU end-of-frame irqs */ + int in_eof_irq; + int rot_in_eof_irq; int out_eof_irq; int rot_out_eof_irq; @@ -1380,6 +1396,9 @@ static int convert_start(struct ipu_image_convert_run *run, unsigned int tile) dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n", __func__, chan->ic_task, ctx, run, tile, dst_tile); + /* clear EOF irq mask */ + ctx->eof_mask = 0; + if (ipu_rot_mode_is_irt(ctx->rot_mode)) { /* swap width/height for resizer */ dest_width = d_image->tile[dst_tile].height; @@ -1615,7 +1634,7 @@ static bool ic_settings_changed(struct ipu_image_convert_ctx *ctx) } /* hold irqlock when calling */ -static irqreturn_t do_irq(struct ipu_image_convert_run *run) +static irqreturn_t do_tile_complete(struct ipu_image_convert_run *run) { struct ipu_image_convert_ctx *ctx = run->ctx; struct ipu_image_convert_chan *chan = ctx->chan; @@ -1700,6 +1719,7 @@ static irqreturn_t do_irq(struct ipu_image_convert_run *run) ctx->cur_buf_num ^= 1; } + ctx->eof_mask = 0; /* clear EOF irq mask for next tile */ ctx->next_tile++; return IRQ_HANDLED; done: @@ -1715,8 +1735,9 @@ static irqreturn_t eof_irq(int irq, void *data) struct ipu_image_convert_priv *priv = chan->priv; struct ipu_image_convert_ctx *ctx; struct ipu_image_convert_run *run; + irqreturn_t ret = IRQ_HANDLED; + bool tile_complete = false; unsigned long flags; - irqreturn_t ret; spin_lock_irqsave(>irqlock, flags); @@ -1729,27 +1750,33 @@ static irqreturn_t eof_irq(int irq, void *data) ctx = run->ctx; - if (irq == chan->out_eof_irq) { - if (ipu_rot_mode_is_irt(ctx->rot_mode)) { - /* this is a rotation op, just ignore */ - ret = IRQ_HANDLED; - goto out; - } - } else if (irq == chan->rot_out_eof_irq) { + if (irq == chan->in_eof_irq) { + ctx->eof_mask |= EOF_IRQ_IN; + } else if (irq == chan->out_eof_irq) { + ctx->eof_mask |= EOF_IRQ_OUT; + } else if (irq == chan->rot_in_eof_irq || + irq == chan->rot_out_eof_irq) { if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { /* this was NOT a rotation op, shouldn't happen */ dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n"); - ret = IRQ_HANDLED; goto out; } + ctx->eof_mask |= (irq == chan->rot_in_eof_irq) ? + EOF_IRQ_ROT_IN : EOF_IRQ_ROT_OUT; } else { dev_err(priv->ipu->dev, "
[PATCH 3/3] gpu: ipu-v3: image-convert: Wait for all EOFs before completing a tile
Use a bit-mask of EOF irqs to determine when all required idmac channel EOFs have been received for a tile conversion, and only do tile completion processing after all EOFs have been received. Otherwise it was found that a conversion would stall after the completion of a tile and the start of the next tile, because the input/read idmac channel had not completed and entered idle state, thus locking up the channel when attempting to re-start it for the next tile. Fixes: 0537db801bb01 ("gpu: ipu-v3: image-convert: reconfigure IC per tile") Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-image-convert.c | 108 ++--- 1 file changed, 81 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index f8b031ded3cf..43e82eb79a08 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -137,6 +137,17 @@ struct ipu_image_convert_ctx; struct ipu_image_convert_chan; struct ipu_image_convert_priv; +enum eof_irq_mask { + EOF_IRQ_IN = BIT(0), + EOF_IRQ_ROT_IN = BIT(1), + EOF_IRQ_OUT = BIT(2), + EOF_IRQ_ROT_OUT = BIT(3), +}; + +#define EOF_IRQ_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT) +#define EOF_IRQ_ROT_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT | \ + EOF_IRQ_ROT_IN | EOF_IRQ_ROT_OUT) + struct ipu_image_convert_ctx { struct ipu_image_convert_chan *chan; @@ -173,6 +184,9 @@ struct ipu_image_convert_ctx { /* where to place converted tile in dest image */ unsigned int out_tile_map[MAX_TILES]; + /* mask of completed EOF irqs at every tile conversion */ + enum eof_irq_mask eof_mask; + struct list_head list; }; @@ -189,6 +203,8 @@ struct ipu_image_convert_chan { struct ipuv3_channel *rotation_out_chan; /* the IPU end-of-frame irqs */ + int in_eof_irq; + int rot_in_eof_irq; int out_eof_irq; int rot_out_eof_irq; @@ -1380,6 +1396,9 @@ static int convert_start(struct ipu_image_convert_run *run, unsigned int tile) dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n", __func__, chan->ic_task, ctx, run, tile, dst_tile); + /* clear EOF irq mask */ + ctx->eof_mask = 0; + if (ipu_rot_mode_is_irt(ctx->rot_mode)) { /* swap width/height for resizer */ dest_width = d_image->tile[dst_tile].height; @@ -1615,7 +1634,7 @@ static bool ic_settings_changed(struct ipu_image_convert_ctx *ctx) } /* hold irqlock when calling */ -static irqreturn_t do_irq(struct ipu_image_convert_run *run) +static irqreturn_t do_tile_complete(struct ipu_image_convert_run *run) { struct ipu_image_convert_ctx *ctx = run->ctx; struct ipu_image_convert_chan *chan = ctx->chan; @@ -1715,8 +1734,9 @@ static irqreturn_t eof_irq(int irq, void *data) struct ipu_image_convert_priv *priv = chan->priv; struct ipu_image_convert_ctx *ctx; struct ipu_image_convert_run *run; + irqreturn_t ret = IRQ_HANDLED; + bool tile_complete = false; unsigned long flags; - irqreturn_t ret; spin_lock_irqsave(>irqlock, flags); @@ -1729,27 +1749,33 @@ static irqreturn_t eof_irq(int irq, void *data) ctx = run->ctx; - if (irq == chan->out_eof_irq) { - if (ipu_rot_mode_is_irt(ctx->rot_mode)) { - /* this is a rotation op, just ignore */ - ret = IRQ_HANDLED; - goto out; - } - } else if (irq == chan->rot_out_eof_irq) { + if (irq == chan->in_eof_irq) { + ctx->eof_mask |= EOF_IRQ_IN; + } else if (irq == chan->out_eof_irq) { + ctx->eof_mask |= EOF_IRQ_OUT; + } else if (irq == chan->rot_in_eof_irq || + irq == chan->rot_out_eof_irq) { if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { /* this was NOT a rotation op, shouldn't happen */ dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n"); - ret = IRQ_HANDLED; goto out; } + ctx->eof_mask |= (irq == chan->rot_in_eof_irq) ? + EOF_IRQ_ROT_IN : EOF_IRQ_ROT_OUT; } else { dev_err(priv->ipu->dev, "Received unknown irq %d\n", irq); ret = IRQ_NONE; goto out; } - ret = do_irq(run); + if (ipu_rot_mode_is_irt(ctx->rot_mode)) + tile_complete = (ctx->eof_mask == EOF_IRQ_ROT_COMPLETE); + else + tile_complete = (ctx->eof_mask == EOF_IRQ_COMPLETE); + + if (tile_complete) +
[PATCH 1/3] gpu: ipu-v3: Restore RGB32, BGR32
RGB32 and BGR32 formats were inadvertently removed from the switch statement in ipu_pixelformat_to_colorspace(). Restore them. Fixes: a59957172b0c ("gpu: ipu-v3: enable remaining 32-bit RGB V4L2 pixel formats") Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index ee2a025e54cf..b3dae9ec1a38 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -124,6 +124,8 @@ enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) case V4L2_PIX_FMT_RGBX32: case V4L2_PIX_FMT_ARGB32: case V4L2_PIX_FMT_XRGB32: + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: return IPUV3_COLORSPACE_RGB; default: return IPUV3_COLORSPACE_UNKNOWN; -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 2/3] gpu: ipu-v3: image-convert: Combine rotate/no-rotate irq handlers
Combine the rotate_irq() and norotate_irq() handlers into a single eof_irq() handler. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-image-convert.c | 58 +- 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index eeca50d9a1ee..f8b031ded3cf 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -1709,9 +1709,10 @@ static irqreturn_t do_irq(struct ipu_image_convert_run *run) return IRQ_WAKE_THREAD; } -static irqreturn_t norotate_irq(int irq, void *data) +static irqreturn_t eof_irq(int irq, void *data) { struct ipu_image_convert_chan *chan = data; + struct ipu_image_convert_priv *priv = chan->priv; struct ipu_image_convert_ctx *ctx; struct ipu_image_convert_run *run; unsigned long flags; @@ -1728,45 +1729,26 @@ static irqreturn_t norotate_irq(int irq, void *data) ctx = run->ctx; - if (ipu_rot_mode_is_irt(ctx->rot_mode)) { - /* this is a rotation operation, just ignore */ - spin_unlock_irqrestore(>irqlock, flags); - return IRQ_HANDLED; - } - - ret = do_irq(run); -out: - spin_unlock_irqrestore(>irqlock, flags); - return ret; -} - -static irqreturn_t rotate_irq(int irq, void *data) -{ - struct ipu_image_convert_chan *chan = data; - struct ipu_image_convert_priv *priv = chan->priv; - struct ipu_image_convert_ctx *ctx; - struct ipu_image_convert_run *run; - unsigned long flags; - irqreturn_t ret; - - spin_lock_irqsave(>irqlock, flags); - - /* get current run and its context */ - run = chan->current_run; - if (!run) { + if (irq == chan->out_eof_irq) { + if (ipu_rot_mode_is_irt(ctx->rot_mode)) { + /* this is a rotation op, just ignore */ + ret = IRQ_HANDLED; + goto out; + } + } else if (irq == chan->rot_out_eof_irq) { + if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { + /* this was NOT a rotation op, shouldn't happen */ + dev_err(priv->ipu->dev, + "Unexpected rotation interrupt\n"); + ret = IRQ_HANDLED; + goto out; + } + } else { + dev_err(priv->ipu->dev, "Received unknown irq %d\n", irq); ret = IRQ_NONE; goto out; } - ctx = run->ctx; - - if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { - /* this was NOT a rotation operation, shouldn't happen */ - dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n"); - spin_unlock_irqrestore(>irqlock, flags); - return IRQ_HANDLED; - } - ret = do_irq(run); out: spin_unlock_irqrestore(>irqlock, flags); @@ -1859,7 +1841,7 @@ static int get_ipu_resources(struct ipu_image_convert_chan *chan) chan->out_chan, IPU_IRQ_EOF); - ret = request_threaded_irq(chan->out_eof_irq, norotate_irq, do_bh, + ret = request_threaded_irq(chan->out_eof_irq, eof_irq, do_bh, 0, "ipu-ic", chan); if (ret < 0) { dev_err(priv->ipu->dev, "could not acquire irq %d\n", @@ -1872,7 +1854,7 @@ static int get_ipu_resources(struct ipu_image_convert_chan *chan) chan->rotation_out_chan, IPU_IRQ_EOF); - ret = request_threaded_irq(chan->rot_out_eof_irq, rotate_irq, do_bh, + ret = request_threaded_irq(chan->rot_out_eof_irq, eof_irq, do_bh, 0, "ipu-ic", chan); if (ret < 0) { dev_err(priv->ipu->dev, "could not acquire irq %d\n", -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH] gpu: ipu-v3: image-convert: Wait for channels before disabling
Hi Philpp, Please disregard this patch. A better solution to a busy wait with a spin lock held is to wait for all required EOF interrupts before doing tile completion processing. I will submit a new patch series. Steve On 6/9/20 5:51 PM, Steve Longerbeam wrote: Call ipu_idmac_wait_busy() on each idmac channel to busy wait for the channel to be idle before disabling. Otherwise it was found that a conversion would stall after the completion of a tile and the start of the next tile. Fixes: 0537db801bb01 ("gpu: ipu-v3: image-convert: reconfigure IC per tile") Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-image-convert.c | 21 + 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index eeca50d9a1ee..f0938015d2fd 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -1251,6 +1251,19 @@ static int get_run_count(struct ipu_image_convert_ctx *ctx, return count; } +static void stop_channel(struct ipu_image_convert_chan *chan, +struct ipuv3_channel *channel) +{ + struct ipu_image_convert_priv *priv = chan->priv; + int ret; + + ret = ipu_idmac_wait_busy(channel, 1); + if (ret == -ETIMEDOUT) + dev_warn(priv->ipu->dev, "IDMAC timeout\n"); + + ipu_idmac_disable_channel(channel); +} + static void convert_stop(struct ipu_image_convert_run *run) { struct ipu_image_convert_ctx *ctx = run->ctx; @@ -1262,12 +1275,12 @@ static void convert_stop(struct ipu_image_convert_run *run) /* disable IC tasks and the channels */ ipu_ic_task_disable(chan->ic); - ipu_idmac_disable_channel(chan->in_chan); - ipu_idmac_disable_channel(chan->out_chan); + stop_channel(chan, chan->in_chan); + stop_channel(chan, chan->out_chan); if (ipu_rot_mode_is_irt(ctx->rot_mode)) { - ipu_idmac_disable_channel(chan->rotation_in_chan); - ipu_idmac_disable_channel(chan->rotation_out_chan); + stop_channel(chan, chan->rotation_in_chan); + stop_channel(chan, chan->rotation_out_chan); ipu_idmac_unlink(chan->out_chan, chan->rotation_in_chan); } ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH] gpu: ipu-v3: image-convert: Wait for channels before disabling
Call ipu_idmac_wait_busy() on each idmac channel to busy wait for the channel to be idle before disabling. Otherwise it was found that a conversion would stall after the completion of a tile and the start of the next tile. Fixes: 0537db801bb01 ("gpu: ipu-v3: image-convert: reconfigure IC per tile") Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-image-convert.c | 21 + 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index eeca50d9a1ee..f0938015d2fd 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -1251,6 +1251,19 @@ static int get_run_count(struct ipu_image_convert_ctx *ctx, return count; } +static void stop_channel(struct ipu_image_convert_chan *chan, +struct ipuv3_channel *channel) +{ + struct ipu_image_convert_priv *priv = chan->priv; + int ret; + + ret = ipu_idmac_wait_busy(channel, 1); + if (ret == -ETIMEDOUT) + dev_warn(priv->ipu->dev, "IDMAC timeout\n"); + + ipu_idmac_disable_channel(channel); +} + static void convert_stop(struct ipu_image_convert_run *run) { struct ipu_image_convert_ctx *ctx = run->ctx; @@ -1262,12 +1275,12 @@ static void convert_stop(struct ipu_image_convert_run *run) /* disable IC tasks and the channels */ ipu_ic_task_disable(chan->ic); - ipu_idmac_disable_channel(chan->in_chan); - ipu_idmac_disable_channel(chan->out_chan); + stop_channel(chan, chan->in_chan); + stop_channel(chan, chan->out_chan); if (ipu_rot_mode_is_irt(ctx->rot_mode)) { - ipu_idmac_disable_channel(chan->rotation_in_chan); - ipu_idmac_disable_channel(chan->rotation_out_chan); + stop_channel(chan, chan->rotation_in_chan); + stop_channel(chan, chan->rotation_out_chan); ipu_idmac_unlink(chan->out_chan, chan->rotation_in_chan); } -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH] gpu: ipu-v3: image-convert: Enable double write reduction
For the write channels with 4:2:0 subsampled YUV formats, avoid chroma overdraw by only writing chroma for even lines (skip odd chroma rows). This reduces necessary write memory bandwidth by at least 25% (more with rotation enabled). Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-image-convert.c | 9 + 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index 36e88434513a..3036e01d8d42 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -1279,6 +1279,15 @@ static void init_idmac_channel(struct ipu_image_convert_ctx *ctx, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); + /* +* Skip writing U and V components to odd rows in the output +* channels for planar 4:2:0. +*/ + if ((channel == chan->out_chan || +channel == chan->rotation_out_chan) && + image->fmt->planar && image->fmt->uv_height_dec == 2) + ipu_cpmem_skip_odd_chroma_rows(channel); + if (channel == chan->rotation_in_chan || channel == chan->rotation_out_chan) { burst_size = 8; -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 2/3] gpu: ipu-v3: image-convert: Fix input bytesperline for packed formats
The input bytesperline calculation for packed pixel formats was incorrect. The min/max clamping values must be multiplied by the packed bits-per-pixel. This was causing corrupted converted images when the input format was RGB4 (probably also other input packed formats). Fixes: d966e23d61a2c ("gpu: ipu-v3: image-convert: fix bytesperline adjustment") Reported-by: Harsha Manjula Mallikarjun Suggested-by: Harsha Manjula Mallikarjun Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-image-convert.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index 36eb4c77ad91..4dfdbd1adf0d 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -1933,7 +1933,9 @@ void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, clamp_align(in->pix.width, 2 << w_align_in, MAX_W, w_align_in) : clamp_align((in->pix.width * infmt->bpp) >> 3, - 2 << w_align_in, MAX_W, w_align_in); + ((2 << w_align_in) * infmt->bpp) >> 3, + (MAX_W * infmt->bpp) >> 3, + w_align_in); in->pix.sizeimage = infmt->planar ? (in->pix.height * in->pix.bytesperline * infmt->bpp) >> 3 : in->pix.height * in->pix.bytesperline; -- 2.17.1
[PATCH 3/3] gpu: ipu-v3: image-convert: Fix image downsize coefficients
The output of the IC downsizer unit in both dimensions must be <= 1024 before being passed to the IC resizer unit. This was causing corrupted images when: input_dim > 1024, and input_dim / 2 < output_dim < input_dim Some broken examples were 1920x1080 -> 1024x768 and 1920x1080 -> 1280x1080. Fixes: 70b9b6b3bcb21 ("gpu: ipu-v3: image-convert: calculate per-tile resize coefficients") Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-image-convert.c | 6 -- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index 4dfdbd1adf0d..e744f3527ce1 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -400,12 +400,14 @@ static int calc_image_resize_coefficients(struct ipu_image_convert_ctx *ctx, if (WARN_ON(resized_width == 0 || resized_height == 0)) return -EINVAL; - while (downsized_width >= resized_width * 2) { + while (downsized_width > 1024 || + downsized_width >= resized_width * 2) { downsized_width >>= 1; downsize_coeff_h++; } - while (downsized_height >= resized_height * 2) { + while (downsized_height > 1024 || + downsized_height >= resized_height * 2) { downsized_height >>= 1; downsize_coeff_v++; } -- 2.17.1
[PATCH 1/3] gpu: ipu-v3: image-convert: Fix input bytesperline width/height align
The output width and height alignment values were being used in the input bytesperline calculation. Fix by separating local vars w_align and h_align into w_align_in, h_align_in, w_align_out, and h_align_out. Fixes: d966e23d61a2c ("gpu: ipu-v3: image-convert: fix bytesperline adjustment") Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-image-convert.c | 32 +- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index 36e88434513a..36eb4c77ad91 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -1876,7 +1876,8 @@ void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, enum ipu_rotate_mode rot_mode) { const struct ipu_image_pixfmt *infmt, *outfmt; - u32 w_align, h_align; + u32 w_align_out, h_align_out; + u32 w_align_in, h_align_in; infmt = get_format(in->pix.pixelformat); outfmt = get_format(out->pix.pixelformat); @@ -1908,22 +1909,31 @@ void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, } /* align input width/height */ - w_align = ilog2(tile_width_align(IMAGE_CONVERT_IN, infmt, rot_mode)); - h_align = ilog2(tile_height_align(IMAGE_CONVERT_IN, infmt, rot_mode)); - in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, w_align); - in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, h_align); + w_align_in = ilog2(tile_width_align(IMAGE_CONVERT_IN, infmt, + rot_mode)); + h_align_in = ilog2(tile_height_align(IMAGE_CONVERT_IN, infmt, +rot_mode)); + in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, + w_align_in); + in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, +h_align_in); /* align output width/height */ - w_align = ilog2(tile_width_align(IMAGE_CONVERT_OUT, outfmt, rot_mode)); - h_align = ilog2(tile_height_align(IMAGE_CONVERT_OUT, outfmt, rot_mode)); - out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, w_align); - out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, h_align); + w_align_out = ilog2(tile_width_align(IMAGE_CONVERT_OUT, outfmt, +rot_mode)); + h_align_out = ilog2(tile_height_align(IMAGE_CONVERT_OUT, outfmt, + rot_mode)); + out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, +w_align_out); + out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, + h_align_out); /* set input/output strides and image sizes */ in->pix.bytesperline = infmt->planar ? - clamp_align(in->pix.width, 2 << w_align, MAX_W, w_align) : + clamp_align(in->pix.width, 2 << w_align_in, MAX_W, + w_align_in) : clamp_align((in->pix.width * infmt->bpp) >> 3, - 2 << w_align, MAX_W, w_align); + 2 << w_align_in, MAX_W, w_align_in); in->pix.sizeimage = infmt->planar ? (in->pix.height * in->pix.bytesperline * infmt->bpp) >> 3 : in->pix.height * in->pix.bytesperline; -- 2.17.1
[PATCH v8 3/5] gpu: ipu-v3: ipu-ic-csc: Add support for limited range encoding
Add support for encodings to or from limited range quantization. Signed-off-by: Steve Longerbeam --- Changes in v7: - hard-code the coefficients instead of deriving the limited range coefficients from the full2full coefficients on the fly with fixed-point math. - add support for RGB limited-range. --- drivers/gpu/ipu-v3/ipu-ic-csc.c | 180 +--- 1 file changed, 166 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic-csc.c b/drivers/gpu/ipu-v3/ipu-ic-csc.c index 5fb469cd64fe..8e9150b1d668 100644 --- a/drivers/gpu/ipu-v3/ipu-ic-csc.c +++ b/drivers/gpu/ipu-v3/ipu-ic-csc.c @@ -10,6 +10,10 @@ #include #include "ipu-prv.h" +#define QUANT_MAP(q) \ + ((q) == V4L2_QUANTIZATION_FULL_RANGE || \ +(q) == V4L2_QUANTIZATION_DEFAULT ? 0 : 1) + /* identity matrix */ static const struct ipu_ic_csc_params identity = { .coeff = { @@ -21,12 +25,87 @@ static const struct ipu_ic_csc_params identity = { .scale = 2, }; +/* + * RGB full-range to RGB limited-range + * + * R_lim = 0.8588 * R_full + 16 + * G_lim = 0.8588 * G_full + 16 + * B_lim = 0.8588 * B_full + 16 + */ +static const struct ipu_ic_csc_params rgbf2rgbl = { + .coeff = { + { 220,0,0, }, + {0, 220,0, }, + {0,0, 220, }, + }, + .offset = { 64, 64, 64, }, + .scale = 1, +}; + +/* + * RGB limited-range to RGB full-range + * + * R_full = 1.1644 * (R_lim - 16) + * G_full = 1.1644 * (G_lim - 16) + * B_full = 1.1644 * (B_lim - 16) + */ +static const struct ipu_ic_csc_params rgbl2rgbf = { + .coeff = { + { 149,0,0, }, + {0, 149,0, }, + {0,0, 149, }, + }, + .offset = { -37, -37, -37, }, + .scale = 2, +}; + +/* + * YUV full-range to YUV limited-range + * + * Y_lim = 0.8588 * Y_full + 16 + * Cb_lim = 0.8784 * (Cb_full - 128) + 128 + * Cr_lim = 0.8784 * (Cr_full - 128) + 128 + */ +static const struct ipu_ic_csc_params yuvf2yuvl = { + .coeff = { + { 220,0,0, }, + {0, 225,0, }, + {0,0, 225, }, + }, + .offset = { 64, 62, 62, }, + .scale = 1, + .sat = true, +}; + +/* + * YUV limited-range to YUV full-range + * + * Y_full = 1.1644 * (Y_lim - 16) + * Cb_full = 1.1384 * (Cb_lim - 128) + 128 + * Cr_full = 1.1384 * (Cr_lim - 128) + 128 + */ +static const struct ipu_ic_csc_params yuvl2yuvf = { + .coeff = { + { 149,0,0, }, + {0, 146,0, }, + {0,0, 146, }, + }, + .offset = { -37, -35, -35, }, + .scale = 2, +}; + static const struct ipu_ic_csc_params *rgb2rgb[] = { , + , + , + , }; static const struct ipu_ic_csc_params *yuv2yuv[] = { , + , + , + , }; /* @@ -46,6 +125,41 @@ static const struct ipu_ic_csc_params rgbf2yuvf_601 = { .scale = 1, }; +/* BT.601 RGB full-range to YUV limited-range */ +static const struct ipu_ic_csc_params rgbf2yuvl_601 = { + .coeff = { + { 66, 129, 25, }, + { -38, -74, 112, }, + { 112, -94, -18, }, + }, + .offset = { 64, 512, 512, }, + .scale = 1, + .sat = true, +}; + +/* BT.601 RGB limited-range to YUV full-range */ +static const struct ipu_ic_csc_params rgbl2yuvf_601 = { + .coeff = { + { 89, 175, 34, }, + { -50, -99, 149, }, + { 149, -125, -24, }, + }, + .offset = { -75, 512, 512, }, + .scale = 1, +}; + +/* BT.601 RGB limited-range to YUV limited-range */ +static const struct ipu_ic_csc_params rgbl2yuvl_601 = { + .coeff = { + { 77, 150, 29, }, + { -44, -87, 131, }, + { 131, -110, -21, }, + }, + .offset = { 0, 512, 512, }, + .scale = 1, + .sat = true, +}; + /* * BT.601 YUV full-range to RGB full-range * @@ -69,39 +183,77 @@ static const struct ipu_ic_csc_params yuvf2rgbf_601 = { .scale = 2, }; +/* BT.601 YUV full-range to RGB limited-range */ +static const struct ipu_ic_csc_params yuvf2rgbl_601 = { + .coeff = { + { 110,0, 154, }, + { 110, -38, -78, }, + { 110, 195,0, }, + }, + .offset = { -276, 265, -358, }, + .scale = 2, +}; + +/* BT.601 YUV limited-range to RGB full-range */ +static const struct ipu_ic_csc_params yuvl2rgbf_601 = { + .coeff = { + { 75,0, 102, }, + { 75, -25, -52, }, + { 75, 129,0, }, + }, + .offset = { -223, 136, -277, }, + .scale = 3, +}; + +/* BT.601 YUV limited-range to RGB limited-range */ +static const struct ipu_ic_csc_params yuvl2rgbl_601 = { +
[PATCH v8 1/5] gpu: ipu-v3: ipu-ic: Fix saturation bit offset in TPMEM
The saturation bit was being set at bit 9 in the second 32-bit word of the TPMEM CSC. This isn't correct, the saturation bit is bit 42, which is bit 10 of the second word. Fixes: 1aa8ea0d2bd5d ("gpu: ipu-v3: Add Image Converter unit") Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Cc: sta...@vger.kernel.org --- drivers/gpu/ipu-v3/ipu-ic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 594c3cbc8291..18816ccf600e 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -257,7 +257,7 @@ static int init_csc(struct ipu_ic *ic, writel(param, base++); param = ((a[0] & 0x1fe0) >> 5) | (params->scale << 8) | - (params->sat << 9); + (params->sat << 10); writel(param, base++); param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) | -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v8 2/5] gpu: ipu-v3: ipu-ic: Fully describe colorspace conversions
Only providing the input and output RGB/YUV space to the IC task init functions is not sufficient. To fully characterize a colorspace conversion, the Y'CbCr encoding standard, and quantization also need to be specified. Define a 'struct ipu_ic_colorspace' that includes all the above. This allows to actually enforce the fact that the IC: - can only encode to/from YUV and RGB full range. A follow-up patch will remove this restriction. - can only encode using BT.601 standard. A follow-up patch will add Rec.709 encoding support. The determination of the CSC coefficients based on the input/output 'struct ipu_ic_colorspace' are moved to a new exported function ipu_ic_calc_csc(), and 'struct ic_csc_params' is exported as 'struct ipu_ic_csc_params'. ipu_ic_calc_csc() fills a 'struct ipu_ic_csc' with the input/output 'struct ipu_ic_colorspace' and the calculated 'struct ic_csc_params' from those input/output colorspaces. The functions ipu_ic_task_init(_rsc)() now take a filled 'struct ipu_ic_csc'. The existing CSC coefficient tables and ipu_ic_calc_csc() are moved to a new module ipu-ic-csc.c. This is in preparation for adding more coefficient tables for limited range quantization and more encoding standards. The existing ycbcr2rgb and inverse rgb2ycbcr tables defined the BT.601 Y'CbCr encoding coefficients. The rgb2ycbcr table specifically described the BT.601 encoding from full range RGB to full range YUV. Table comments have been added in ipu-ic-csc.c to make this more clear. The ycbcr2rgb inverse table described encoding YUV limited range to RGB full range. To be consistent with the rgb2ycbcr table, this table is converted to YUV full range to RGB full range, and the comments are expanded in ipu-ic-csc.c. The ic_csc_rgb2rgb table was just an identity matrix, so it is renamed 'identity' in ipu-ic-csc.c. Signed-off-by: Steve Longerbeam --- Changes in v8: - remove Fixes: and cc: stable. This patch is too difficult to backport to stable trees. Changes in v7: - squashed with "gpu: ipu-v3: ipu-ic: Fix BT.601 coefficients". - moved the coefficient tables and calc_csc_coeffs() to a new module ipu-ic-csc.c, and added exported ipu_ic_calc_csc() to it. - drop v4l2_colorspace (chromaticities) from 'struct ipu_ic_colorspace'. It's implied that xfer_func (gamma function) must be the same for input and output, so make that implicit for chromaticities too. - drop passing priv to calc_csc_coeffs(), was only used to print error messages. - removed the inverse_encode boolean in calc_csc_coeffs(). - express negative coefficients as true signed int's, for better readability. - tweak inverse coeff in comments, no change to rounded table values. --- drivers/gpu/ipu-v3/Makefile | 4 +- drivers/gpu/ipu-v3/ipu-ic-csc.c | 129 ++ drivers/gpu/ipu-v3/ipu-ic.c | 138 +++- drivers/gpu/ipu-v3/ipu-image-convert.c | 28 ++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 34 - include/video/imx-ipu-v3.h | 56 +++- 6 files changed, 271 insertions(+), 118 deletions(-) create mode 100644 drivers/gpu/ipu-v3/ipu-ic-csc.c diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile index 7cc8b47e488b..5fe5ef20701a 100644 --- a/drivers/gpu/ipu-v3/Makefile +++ b/drivers/gpu/ipu-v3/Makefile @@ -2,8 +2,8 @@ obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ - ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-image-convert.o \ - ipu-smfc.o ipu-vdi.o + ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-ic-csc.o \ + ipu-image-convert.o ipu-smfc.o ipu-vdi.o ifdef CONFIG_DRM imx-ipu-v3-objs += ipu-pre.o ipu-prg.o diff --git a/drivers/gpu/ipu-v3/ipu-ic-csc.c b/drivers/gpu/ipu-v3/ipu-ic-csc.c new file mode 100644 index ..5fb469cd64fe --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-ic-csc.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Mentor Graphics Inc. + */ + +#include +#include +#include +#include +#include +#include "ipu-prv.h" + +/* identity matrix */ +static const struct ipu_ic_csc_params identity = { + .coeff = { + { 128,0,0, }, + {0, 128,0, }, + {0,0, 128, }, + }, + .offset = { 0, 0, 0, }, + .scale = 2, +}; + +static const struct ipu_ic_csc_params *rgb2rgb[] = { + , +}; + +static const struct ipu_ic_csc_params *yuv2yuv[] = { + , +}; + +/* + * BT.601 RGB full-range to YUV full-range + * + * Y = .2990 * R + .5870 * G + .1140 * B + * U = -.1687 * R - .3313 * G + .5000 * B + 128 + * V = .5000 * R - .4187 * G - .0813 * B + 128 + */ +static const struct ipu_ic_csc_params rgbf2yuvf_601 = { + .coeff = { + { 77, 150, 29, }, + { -43, -85, 128, }, + { 128, -107, -21, }, + }, +
[PATCH v8 4/5] gpu: ipu-v3: ipu-ic-csc: Add support for Rec.709 encoding
Add support for Rec.709 encoding and inverse encoding. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- Changes in v7: - moved CSC tables to new module ipu-ic-csc.c. - express negative coefficients as true signed int's, for better readability. Changes in v5: - moved API changes to a previous patch. - moved CSC coeff calc to new function calc_csc_coeffs(). Changes in v4: - fix compile error. Chnges in v3: - none. Changes in v2: - only return "Unsupported YCbCr encoding" error if inf != outf, since if inf == outf, the identity matrix can be used. Reported by Tim Harvey. --- drivers/gpu/ipu-v3/ipu-ic-csc.c | 139 ++-- 1 file changed, 134 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic-csc.c b/drivers/gpu/ipu-v3/ipu-ic-csc.c index 8e9150b1d668..09e94aa12c40 100644 --- a/drivers/gpu/ipu-v3/ipu-ic-csc.c +++ b/drivers/gpu/ipu-v3/ipu-ic-csc.c @@ -230,14 +230,133 @@ static const struct ipu_ic_csc_params *yuv2rgb_601[] = { _601, }; +/* + * REC.709 encoding from RGB full range to YUV full range: + * + * Y = .2126 * R + .7152 * G + .0722 * B + * U = -.1146 * R - .3854 * G + .5000 * B + 128 + * V = .5000 * R - .4542 * G - .0458 * B + 128 + */ +static const struct ipu_ic_csc_params rgbf2yuvf_709 = { + .coeff = { + { 54, 183, 19 }, + { -29, -99, 128 }, + { 128, -116, -12 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + +/* Rec.709 RGB full-range to YUV limited-range */ +static const struct ipu_ic_csc_params rgbf2yuvl_709 = { + .coeff = { + { 47, 157, 16, }, + { -26, -87, 112, }, + { 112, -102, -10, }, + }, + .offset = { 64, 512, 512, }, + .scale = 1, + .sat = true, +}; + +/* Rec.709 RGB limited-range to YUV full-range */ +static const struct ipu_ic_csc_params rgbl2yuvf_709 = { + .coeff = { + { 63, 213, 22, }, + { -34, -115, 149, }, + { 149, -135, -14, }, + }, + .offset = { -75, 512, 512, }, + .scale = 1, +}; + +/* Rec.709 RGB limited-range to YUV limited-range */ +static const struct ipu_ic_csc_params rgbl2yuvl_709 = { + .coeff = { + { 54, 183, 18, }, + { -30, -101, 131, }, + { 131, -119, -12, }, + }, + .offset = { 0, 512, 512, }, + .scale = 1, + .sat = true, +}; + +/* + * Inverse REC.709 encoding from YUV full range to RGB full range: + * + * R = 1. * Y + 0 * (Cb - 128) + 1.5748 * (Cr - 128) + * G = 1. * Y - .1873 * (Cb - 128) - .4681 * (Cr - 128) + * B = 1. * Y + 1.8556 * (Cb - 128) + 0 * (Cr - 128) + * + * equivalently (factoring out the offsets): + * + * R = 1. * Y + 0 * Cb + 1.5748 * Cr - 201.574 + * G = 1. * Y - .1873 * Cb - .4681 * Cr + 83.891 + * B = 1. * Y + 1.8556 * Cb + 0 * Cr - 237.517 + */ +static const struct ipu_ic_csc_params yuvf2rgbf_709 = { + .coeff = { + { 128, 0, 202 }, + { 128, -24, -60 }, + { 128, 238, 0 }, + }, + .offset = { -403, 168, -475 }, + .scale = 2, +}; + +/* Rec.709 YUV full-range to RGB limited-range */ +static const struct ipu_ic_csc_params yuvf2rgbl_709 = { + .coeff = { + { 110,0, 173, }, + { 110, -21, -51, }, + { 110, 204,0, }, + }, + .offset = { -314, 176, -376, }, + .scale = 2, +}; + +/* Rec.709 YUV limited-range to RGB full-range */ +static const struct ipu_ic_csc_params yuvl2rgbf_709 = { + .coeff = { + { 75,0, 115, }, + { 75, -14, -34, }, + { 75, 135,0, }, + }, + .offset = { -248, 77, -289, }, + .scale = 3, +}; + +/* Rec.709 YUV limited-range to RGB limited-range */ +static const struct ipu_ic_csc_params yuvl2rgbl_709 = { + .coeff = { + { 128,0, 197, }, + { 128, -23, -59, }, + { 128, 232,0, }, + }, + .offset = { -394, 164, -464, }, + .scale = 2, +}; + +static const struct ipu_ic_csc_params *rgb2yuv_709[] = { + _709, + _709, + _709, + _709, +}; + +static const struct ipu_ic_csc_params *yuv2rgb_709[] = { + _709, + _709, + _709, + _709, +}; + static int calc_csc_coeffs(struct ipu_ic_csc *csc) { const struct ipu_ic_csc_params **params_tbl; int tbl_idx; - if (csc->out_cs.enc != V4L2_YCBCR_ENC_601) - return -ENOTSUPP; - tbl_idx = (QUANT_MAP(csc->in_cs.quant) << 1) | QUANT_MAP(csc->out_cs.quant); @@ -250,8 +369,18 @@ static int calc_csc_coeffs(struct ipu_ic_csc *csc) /* YUV <-> RGB encoding is required */ - params_tbl = (csc->in_cs.cs == IPUV3_COLORSPACE_YUV) ? - yuv2rgb_601
[PATCH v7 2/5] gpu: ipu-v3: ipu-ic: Fully describe colorspace conversions
Only providing the input and output RGB/YUV space to the IC task init functions is not sufficient. To fully characterize a colorspace conversion, the Y'CbCr encoding standard, and quantization also need to be specified. Define a 'struct ipu_ic_colorspace' that includes all the above. This allows to actually enforce the fact that the IC: - can only encode to/from YUV and RGB full range. A follow-up patch will remove this restriction. - can only encode using BT.601 standard. A follow-up patch will add Rec.709 encoding support. The determination of the CSC coefficients based on the input/output 'struct ipu_ic_colorspace' are moved to a new exported function ipu_ic_calc_csc(), and 'struct ic_csc_params' is exported as 'struct ipu_ic_csc_params'. ipu_ic_calc_csc() fills a 'struct ipu_ic_csc' with the input/output 'struct ipu_ic_colorspace' and the calculated 'struct ic_csc_params' from those input/output colorspaces. The functions ipu_ic_task_init(_rsc)() now take a filled 'struct ipu_ic_csc'. The existing CSC coefficient tables and ipu_ic_calc_csc() are moved to a new module ipu-ic-csc.c. This is in preparation for adding more coefficient tables for limited range quantization and more encoding standards. The existing ycbcr2rgb and inverse rgb2ycbcr tables defined the BT.601 Y'CbCr encoding coefficients. The rgb2ycbcr table specifically described the BT.601 encoding from full range RGB to full range YUV. Table comments have been added in ipu-ic-csc.c to make this more clear. The ycbcr2rgb inverse table described encoding YUV limited range to RGB full range. To be consistent with the rgb2ycbcr table, this table is converted to YUV full range to RGB full range, and the comments are expanded in ipu-ic-csc.c. The ic_csc_rgb2rgb table was just an identity matrix, so it is renamed 'identity' in ipu-ic-csc.c. Fixes: 1aa8ea0d2bd5d ("gpu: ipu-v3: Add Image Converter unit") Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- Changes in v7: - squashed with "gpu: ipu-v3: ipu-ic: Fix BT.601 coefficients". - moved the coefficient tables and calc_csc_coeffs() to a new module ipu-ic-csc.c, and added exported ipu_ic_calc_csc() to it. - drop v4l2_colorspace (chromaticities) from 'struct ipu_ic_colorspace'. It's implied that xfer_func (gamma function) must be the same for input and output, so make that implicit for chromaticities too. - drop passing priv to calc_csc_coeffs(), was only used to print error messages. - removed the inverse_encode boolean in calc_csc_coeffs(). - express negative coefficients as true signed int's, for better readability. - tweak inverse coeff in comments, no change to rounded table values. --- drivers/gpu/ipu-v3/Makefile | 4 +- drivers/gpu/ipu-v3/ipu-ic-csc.c | 129 ++ drivers/gpu/ipu-v3/ipu-ic.c | 138 +++- drivers/gpu/ipu-v3/ipu-image-convert.c | 28 ++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 34 - include/video/imx-ipu-v3.h | 56 +++- 6 files changed, 271 insertions(+), 118 deletions(-) create mode 100644 drivers/gpu/ipu-v3/ipu-ic-csc.c diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile index 7cc8b47e488b..5fe5ef20701a 100644 --- a/drivers/gpu/ipu-v3/Makefile +++ b/drivers/gpu/ipu-v3/Makefile @@ -2,8 +2,8 @@ obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ - ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-image-convert.o \ - ipu-smfc.o ipu-vdi.o + ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-ic-csc.o \ + ipu-image-convert.o ipu-smfc.o ipu-vdi.o ifdef CONFIG_DRM imx-ipu-v3-objs += ipu-pre.o ipu-prg.o diff --git a/drivers/gpu/ipu-v3/ipu-ic-csc.c b/drivers/gpu/ipu-v3/ipu-ic-csc.c new file mode 100644 index ..5fb469cd64fe --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-ic-csc.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Mentor Graphics Inc. + */ + +#include +#include +#include +#include +#include +#include "ipu-prv.h" + +/* identity matrix */ +static const struct ipu_ic_csc_params identity = { + .coeff = { + { 128,0,0, }, + {0, 128,0, }, + {0,0, 128, }, + }, + .offset = { 0, 0, 0, }, + .scale = 2, +}; + +static const struct ipu_ic_csc_params *rgb2rgb[] = { + , +}; + +static const struct ipu_ic_csc_params *yuv2yuv[] = { + , +}; + +/* + * BT.601 RGB full-range to YUV full-range + * + * Y = .2990 * R + .5870 * G + .1140 * B + * U = -.1687 * R - .3313 * G + .5000 * B + 128 + * V = .5000 * R - .4187 * G - .0813 * B + 128 + */ +static const struct ipu_ic_csc_params rgbf2yuvf_601 = { + .coeff = { + { 77, 150, 29, }, + { -43, -85, 128, }, + { 128, -107, -21, }, + }, +
[PATCH v7 1/5] gpu: ipu-v3: ipu-ic: Fix saturation bit offset in TPMEM
The saturation bit was being set at bit 9 in the second 32-bit word of the TPMEM CSC. This isn't correct, the saturation bit is bit 42, which is bit 10 of the second word. Fixes: 1aa8ea0d2bd5d ("gpu: ipu-v3: Add Image Converter unit") Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Cc: sta...@vger.kernel.org --- drivers/gpu/ipu-v3/ipu-ic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 594c3cbc8291..18816ccf600e 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -257,7 +257,7 @@ static int init_csc(struct ipu_ic *ic, writel(param, base++); param = ((a[0] & 0x1fe0) >> 5) | (params->scale << 8) | - (params->sat << 9); + (params->sat << 10); writel(param, base++); param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) | -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v7 3/5] gpu: ipu-v3: ipu-ic-csc: Add support for limited range encoding
Add support for encodings to or from limited range quantization. Signed-off-by: Steve Longerbeam --- Changes in v7: - hard-code the coefficients instead of deriving the limited range coefficients from the full2full coefficients on the fly with fixed-point math. - add support for RGB limited-range. --- drivers/gpu/ipu-v3/ipu-ic-csc.c | 180 +--- 1 file changed, 166 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic-csc.c b/drivers/gpu/ipu-v3/ipu-ic-csc.c index 5fb469cd64fe..8e9150b1d668 100644 --- a/drivers/gpu/ipu-v3/ipu-ic-csc.c +++ b/drivers/gpu/ipu-v3/ipu-ic-csc.c @@ -10,6 +10,10 @@ #include #include "ipu-prv.h" +#define QUANT_MAP(q) \ + ((q) == V4L2_QUANTIZATION_FULL_RANGE || \ +(q) == V4L2_QUANTIZATION_DEFAULT ? 0 : 1) + /* identity matrix */ static const struct ipu_ic_csc_params identity = { .coeff = { @@ -21,12 +25,87 @@ static const struct ipu_ic_csc_params identity = { .scale = 2, }; +/* + * RGB full-range to RGB limited-range + * + * R_lim = 0.8588 * R_full + 16 + * G_lim = 0.8588 * G_full + 16 + * B_lim = 0.8588 * B_full + 16 + */ +static const struct ipu_ic_csc_params rgbf2rgbl = { + .coeff = { + { 220,0,0, }, + {0, 220,0, }, + {0,0, 220, }, + }, + .offset = { 64, 64, 64, }, + .scale = 1, +}; + +/* + * RGB limited-range to RGB full-range + * + * R_full = 1.1644 * (R_lim - 16) + * G_full = 1.1644 * (G_lim - 16) + * B_full = 1.1644 * (B_lim - 16) + */ +static const struct ipu_ic_csc_params rgbl2rgbf = { + .coeff = { + { 149,0,0, }, + {0, 149,0, }, + {0,0, 149, }, + }, + .offset = { -37, -37, -37, }, + .scale = 2, +}; + +/* + * YUV full-range to YUV limited-range + * + * Y_lim = 0.8588 * Y_full + 16 + * Cb_lim = 0.8784 * (Cb_full - 128) + 128 + * Cr_lim = 0.8784 * (Cr_full - 128) + 128 + */ +static const struct ipu_ic_csc_params yuvf2yuvl = { + .coeff = { + { 220,0,0, }, + {0, 225,0, }, + {0,0, 225, }, + }, + .offset = { 64, 62, 62, }, + .scale = 1, + .sat = true, +}; + +/* + * YUV limited-range to YUV full-range + * + * Y_full = 1.1644 * (Y_lim - 16) + * Cb_full = 1.1384 * (Cb_lim - 128) + 128 + * Cr_full = 1.1384 * (Cr_lim - 128) + 128 + */ +static const struct ipu_ic_csc_params yuvl2yuvf = { + .coeff = { + { 149,0,0, }, + {0, 146,0, }, + {0,0, 146, }, + }, + .offset = { -37, -35, -35, }, + .scale = 2, +}; + static const struct ipu_ic_csc_params *rgb2rgb[] = { , + , + , + , }; static const struct ipu_ic_csc_params *yuv2yuv[] = { , + , + , + , }; /* @@ -46,6 +125,41 @@ static const struct ipu_ic_csc_params rgbf2yuvf_601 = { .scale = 1, }; +/* BT.601 RGB full-range to YUV limited-range */ +static const struct ipu_ic_csc_params rgbf2yuvl_601 = { + .coeff = { + { 66, 129, 25, }, + { -38, -74, 112, }, + { 112, -94, -18, }, + }, + .offset = { 64, 512, 512, }, + .scale = 1, + .sat = true, +}; + +/* BT.601 RGB limited-range to YUV full-range */ +static const struct ipu_ic_csc_params rgbl2yuvf_601 = { + .coeff = { + { 89, 175, 34, }, + { -50, -99, 149, }, + { 149, -125, -24, }, + }, + .offset = { -75, 512, 512, }, + .scale = 1, +}; + +/* BT.601 RGB limited-range to YUV limited-range */ +static const struct ipu_ic_csc_params rgbl2yuvl_601 = { + .coeff = { + { 77, 150, 29, }, + { -44, -87, 131, }, + { 131, -110, -21, }, + }, + .offset = { 0, 512, 512, }, + .scale = 1, + .sat = true, +}; + /* * BT.601 YUV full-range to RGB full-range * @@ -69,39 +183,77 @@ static const struct ipu_ic_csc_params yuvf2rgbf_601 = { .scale = 2, }; +/* BT.601 YUV full-range to RGB limited-range */ +static const struct ipu_ic_csc_params yuvf2rgbl_601 = { + .coeff = { + { 110,0, 154, }, + { 110, -38, -78, }, + { 110, 195,0, }, + }, + .offset = { -276, 265, -358, }, + .scale = 2, +}; + +/* BT.601 YUV limited-range to RGB full-range */ +static const struct ipu_ic_csc_params yuvl2rgbf_601 = { + .coeff = { + { 75,0, 102, }, + { 75, -25, -52, }, + { 75, 129,0, }, + }, + .offset = { -223, 136, -277, }, + .scale = 3, +}; + +/* BT.601 YUV limited-range to RGB limited-range */ +static const struct ipu_ic_csc_params yuvl2rgbl_601 = { +
[PATCH v7 4/5] gpu: ipu-v3: ipu-ic-csc: Add support for Rec.709 encoding
Add support for Rec.709 encoding and inverse encoding. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- Changes in v7: - moved CSC tables to new module ipu-ic-csc.c. - express negative coefficients as true signed int's, for better readability. Changes in v5: - moved API changes to a previous patch. - moved CSC coeff calc to new function calc_csc_coeffs(). Changes in v4: - fix compile error. Chnges in v3: - none. Changes in v2: - only return "Unsupported YCbCr encoding" error if inf != outf, since if inf == outf, the identity matrix can be used. Reported by Tim Harvey. --- drivers/gpu/ipu-v3/ipu-ic-csc.c | 139 ++-- 1 file changed, 134 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic-csc.c b/drivers/gpu/ipu-v3/ipu-ic-csc.c index 8e9150b1d668..09e94aa12c40 100644 --- a/drivers/gpu/ipu-v3/ipu-ic-csc.c +++ b/drivers/gpu/ipu-v3/ipu-ic-csc.c @@ -230,14 +230,133 @@ static const struct ipu_ic_csc_params *yuv2rgb_601[] = { _601, }; +/* + * REC.709 encoding from RGB full range to YUV full range: + * + * Y = .2126 * R + .7152 * G + .0722 * B + * U = -.1146 * R - .3854 * G + .5000 * B + 128 + * V = .5000 * R - .4542 * G - .0458 * B + 128 + */ +static const struct ipu_ic_csc_params rgbf2yuvf_709 = { + .coeff = { + { 54, 183, 19 }, + { -29, -99, 128 }, + { 128, -116, -12 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + +/* Rec.709 RGB full-range to YUV limited-range */ +static const struct ipu_ic_csc_params rgbf2yuvl_709 = { + .coeff = { + { 47, 157, 16, }, + { -26, -87, 112, }, + { 112, -102, -10, }, + }, + .offset = { 64, 512, 512, }, + .scale = 1, + .sat = true, +}; + +/* Rec.709 RGB limited-range to YUV full-range */ +static const struct ipu_ic_csc_params rgbl2yuvf_709 = { + .coeff = { + { 63, 213, 22, }, + { -34, -115, 149, }, + { 149, -135, -14, }, + }, + .offset = { -75, 512, 512, }, + .scale = 1, +}; + +/* Rec.709 RGB limited-range to YUV limited-range */ +static const struct ipu_ic_csc_params rgbl2yuvl_709 = { + .coeff = { + { 54, 183, 18, }, + { -30, -101, 131, }, + { 131, -119, -12, }, + }, + .offset = { 0, 512, 512, }, + .scale = 1, + .sat = true, +}; + +/* + * Inverse REC.709 encoding from YUV full range to RGB full range: + * + * R = 1. * Y + 0 * (Cb - 128) + 1.5748 * (Cr - 128) + * G = 1. * Y - .1873 * (Cb - 128) - .4681 * (Cr - 128) + * B = 1. * Y + 1.8556 * (Cb - 128) + 0 * (Cr - 128) + * + * equivalently (factoring out the offsets): + * + * R = 1. * Y + 0 * Cb + 1.5748 * Cr - 201.574 + * G = 1. * Y - .1873 * Cb - .4681 * Cr + 83.891 + * B = 1. * Y + 1.8556 * Cb + 0 * Cr - 237.517 + */ +static const struct ipu_ic_csc_params yuvf2rgbf_709 = { + .coeff = { + { 128, 0, 202 }, + { 128, -24, -60 }, + { 128, 238, 0 }, + }, + .offset = { -403, 168, -475 }, + .scale = 2, +}; + +/* Rec.709 YUV full-range to RGB limited-range */ +static const struct ipu_ic_csc_params yuvf2rgbl_709 = { + .coeff = { + { 110,0, 173, }, + { 110, -21, -51, }, + { 110, 204,0, }, + }, + .offset = { -314, 176, -376, }, + .scale = 2, +}; + +/* Rec.709 YUV limited-range to RGB full-range */ +static const struct ipu_ic_csc_params yuvl2rgbf_709 = { + .coeff = { + { 75,0, 115, }, + { 75, -14, -34, }, + { 75, 135,0, }, + }, + .offset = { -248, 77, -289, }, + .scale = 3, +}; + +/* Rec.709 YUV limited-range to RGB limited-range */ +static const struct ipu_ic_csc_params yuvl2rgbl_709 = { + .coeff = { + { 128,0, 197, }, + { 128, -23, -59, }, + { 128, 232,0, }, + }, + .offset = { -394, 164, -464, }, + .scale = 2, +}; + +static const struct ipu_ic_csc_params *rgb2yuv_709[] = { + _709, + _709, + _709, + _709, +}; + +static const struct ipu_ic_csc_params *yuv2rgb_709[] = { + _709, + _709, + _709, + _709, +}; + static int calc_csc_coeffs(struct ipu_ic_csc *csc) { const struct ipu_ic_csc_params **params_tbl; int tbl_idx; - if (csc->out_cs.enc != V4L2_YCBCR_ENC_601) - return -ENOTSUPP; - tbl_idx = (QUANT_MAP(csc->in_cs.quant) << 1) | QUANT_MAP(csc->out_cs.quant); @@ -250,8 +369,18 @@ static int calc_csc_coeffs(struct ipu_ic_csc *csc) /* YUV <-> RGB encoding is required */ - params_tbl = (csc->in_cs.cs == IPUV3_COLORSPACE_YUV) ? - yuv2rgb_601
Re: [PATCH v6 5/7] gpu: ipu-v3: ipu-ic: Add support for limited range encoding
On 3/8/19 3:57 AM, Philipp Zabel wrote: On Thu, 2019-03-07 at 15:33 -0800, Steve Longerbeam wrote: Add support for the following conversions: - YUV full-range to YUV limited-range - YUV limited-range to YUV full-range - YUV limited-range to RGB full-range - RGB full-range to YUV limited-range The last two conversions require operating on the YUV full-range encoding and inverse encoding coefficients, with the YUV-to-YUV limited<->full coefficients. The formula to convert is M_c = M_a * M_b O_c = M_a * O_b + O_a For calculating the RGB full-range to YUV limited-range coefficients: [M_a, O_a] = YUV full-range to YUV limited-range coefficients. [M_b, O_b] = RGB full-range to YUV full-range coefficients. For calculating the YUV limited-range to RGB full-range coefficients: [M_a, O_a] = YUV full-range to RGB full-range coefficients. [M_b, O_b] = YUV limited-range to YUV full-range coefficients. The calculation of [M_c, O_c] is carried out by the function transform_coeffs(). In the future if RGB limited range encoding is required, the same function can be used. And cascaded to create all combinations of encoding for YUV limited/full range <-> RGB limited/full range, passing the output coefficients from one call as the input for the next. For example, to create YUV full-range to RGB limited-range coefficients: [M_a, O_a] = RGB full-range to RGB limited-range coefficients. [M_b, O_b] = YUV full-range to RGB full-range coefficients. and that output sent as input to create YUV limited-range to RGB limited-range coefficients: [M_a, O_a] = YUV full-range to RGB limited-range coefficients. [M_b, O_b] = YUV limited-range to YUV full-range coefficients. Signed-off-by: Steve Longerbeam I'm not a big fan of this. Wouldn't it be much easier to compute all necessary task parameter sets offline with high precision, and store the precomputed sets in the compact representation? I am thinking of when support might be added for the other encoding standards. With this transform function, only two new task parameter structs need to be added, one for yuv-full-to-rgb-full, and one for rgb-full-to-yuv-full. Without transform_coeffs(), four structs would have to be added (adding encoding to and from yuv-limited). And if rgb-limited support is added, it would mean a total of eight new structs for a new encoding standard. But with transform_coeffs(), still only the two structs above are needed, and the function would compute the others automatically in runtime. Steve --- drivers/gpu/ipu-v3/ipu-ic.c | 281 +--- 1 file changed, 263 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 1460901af9b5..a7dd85f8d832 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -178,10 +178,10 @@ static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset) } struct ic_encode_coeff { - s16 coeff[3][3];/* signed 9-bit integer coefficients */ - s16 offset[3]; /* signed 11+2-bit fixed point offset */ - u8 scale:2; /* scale coefficients * 2^(scale-1) */ - bool sat:1; /* saturate to (16, 235(Y) / 240(U, V)) */ + int coeff[3][3];/* signed 9-bit integer coefficients */ + int offset[3]; /* signed 13-bit integer offset */ + int scale; /* scale coefficients * 2^(scale-1) */ + bool sat; /* saturate to (16, 235(Y) / 240(U, V)) */ }; /* @@ -277,6 +277,231 @@ static const struct ic_encode_coeff ic_encode_ycbcr2rgb_709 = { .scale = 2, }; +/* + * YUV full range to YUV limited range: + * + * Y_lim = 0.8588 * Y_full + 16 + * Cb_lim = 0.8784 * (Cb_full - 128) + 128 + * Cr_lim = 0.8784 * (Cr_full - 128) + 128 + */ +static const struct ic_encode_coeff ic_encode_ycbcr_full2lim = { + .coeff = { + { 219, 0, 0 }, + { 0, 224, 0 }, + { 0, 0, 224 }, + }, + .offset = { 64, 62, 62 }, + .scale = 1, +}; + +/* + * YUV limited range to YUV full range: + * + * Y_full = 1.1644 * (Y_lim - 16) + * Cb_full = 1.1384 * (Cb_lim - 128) + 128 + * Cr_full = 1.1384 * (Cr_lim - 128) + 128 + */ +static const struct ic_encode_coeff ic_encode_ycbcr_lim2full = { + .coeff = { + { 149, 0, 0 }, + { 0, 145, 0 }, + { 0, 0, 145 }, + }, + .offset = { -37, -35, -35 }, + .scale = 2, +}; + +/* + * RGB full range to RGB limited range: + * + * R_lim = 0.8588 * R_full + 16 + * G_lim = 0.8588 * G_full + 16 + * B_lim = 0.8588 * B_full + 16 + */ +static const struct ic_encode_coeff +ic_encode_rgb_full2lim __maybe_unused = { + .coeff = { + { 220, 0, 0 }, + { 0, 220, 0 }, + { 0, 0, 220 }, + }, + .offset = { 64, 64, 64 }, + .scale = 1, +}; + +/* + * RGB limited range to RG
Re: [PATCH v6 3/7] gpu: ipu-v3: ipu-ic: Fully describe colorspace conversions
On 3/8/19 3:46 AM, Philipp Zabel wrote: On Thu, 2019-03-07 at 15:33 -0800, Steve Longerbeam wrote: Only providing the input and output RGB/YUV space to the IC task init functions is not sufficient. To fully characterize a colorspace conversion, the colorspace (chromaticities), Y'CbCr encoding standard, and quantization also need to be specified. Define a 'struct ipu_ic_colorspace' that includes all the above, and pass the input and output ipu_ic_colorspace to the IC task init functions. This allows to actually enforce the fact that the IC: - can only encode to/from YUV full range (follow-up patch will remove this restriction). - can only encode to/from RGB full range. - can only encode using BT.601 standard (follow-up patch will add Rec.709 encoding support). - cannot convert colorspaces from input to output, the input and output colorspace chromaticities must be the same. The determination of the CSC coefficients based on the input/output colorspace parameters are moved to a new function calc_csc_coeffs(), called by init_csc(). Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-ic.c | 136 +--- drivers/gpu/ipu-v3/ipu-image-convert.c | 27 ++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 22 +++- include/video/imx-ipu-v3.h | 37 +- 4 files changed, 154 insertions(+), 68 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index b63a2826b629..c4048c921801 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -146,8 +146,10 @@ struct ipu_ic { const struct ic_task_regoffs *reg; const struct ic_task_bitfields *bit; - enum ipu_color_space in_cs, g_in_cs; - enum ipu_color_space out_cs; + struct ipu_ic_colorspace in_cs; + struct ipu_ic_colorspace g_in_cs; + struct ipu_ic_colorspace out_cs; + bool graphics; bool rotation; bool in_use; @@ -235,42 +237,83 @@ static const struct ic_encode_coeff ic_encode_ycbcr2rgb_601 = { .scale = 2, }; +static int calc_csc_coeffs(struct ipu_ic_priv *priv, + struct ic_encode_coeff *coeff_out, + const struct ipu_ic_colorspace *in, + const struct ipu_ic_colorspace *out) +{ + bool inverse_encode; + + if (in->colorspace != out->colorspace) { + dev_err(priv->ipu->dev, "Cannot convert colorspaces\n"); + return -ENOTSUPP; + } I don't think this is useful enough to warrant having the colorspace field in ipu_ic_colorspace. Let the caller make sure of this, same as for xfer_func. Ok, for xfer_func it is implicit that the gamma function must be the same for input and output, so I agree it might as well be implicit for chromaticities too. + if (out->enc != V4L2_YCBCR_ENC_601) { + dev_err(priv->ipu->dev, "Only BT.601 encoding supported\n"); + return -ENOTSUPP; + } This is only important if out->cs is IPUV3_COLORSPACE_YUV, right? If the output is RGB this field shouldn't matter. It matters for encoding YUV to RGB, or the inverse RGB to YUV. The encoding standard doesn't matter only if no encoding/inverse encoding is requested (YUV to YUV or RGB to RGB). + + if ((in->cs == IPUV3_COLORSPACE_YUV && +in->quant != V4L2_QUANTIZATION_FULL_RANGE) || + (out->cs == IPUV3_COLORSPACE_YUV && +out->quant != V4L2_QUANTIZATION_FULL_RANGE)) { + dev_err(priv->ipu->dev, "Limited range YUV not supported\n"); + return -ENOTSUPP; + } + + if ((in->cs == IPUV3_COLORSPACE_RGB && +in->quant != V4L2_QUANTIZATION_FULL_RANGE) || + (out->cs == IPUV3_COLORSPACE_RGB && +out->quant != V4L2_QUANTIZATION_FULL_RANGE)) { + dev_err(priv->ipu->dev, "Limited range RGB not supported\n"); + return -ENOTSUPP; + } + + if (in->cs == out->cs) { + *coeff_out = ic_encode_identity; + + return 0; + } + + inverse_encode = (in->cs == IPUV3_COLORSPACE_YUV); What does inverse_encode mean in this context? It means YUV to RGB. At this point in the function it is determined that encoding or inverse encoding is requested. + + *coeff_out = inverse_encode ? + ic_encode_ycbcr2rgb_601 : ic_encode_rgb2ycbcr_601; + + return 0; +} + static int init_csc(struct ipu_ic *ic, - enum ipu_color_space inf, - enum ipu_color_space outf, + const struct ipu_ic_colorspace *in, + const struct ipu_ic_colorspace *out, int csc_index) { struct ipu_ic_priv *priv = ic->priv; - const stru
Re: [PATCH v6 2/7] gpu: ipu-v3: ipu-ic: Fix BT.601 coefficients
On 3/8/19 2:23 AM, Philipp Zabel wrote: Hi Steve, On Thu, 2019-03-07 at 15:33 -0800, Steve Longerbeam wrote: The ycbcr2rgb and inverse rgb2ycbcr tables define the BT.601 Y'CbCr encoding coefficients. The rgb2ycbcr table specifically describes the BT.601 encoding from full range RGB to full range YUV. Add table comments to make this more clear. The ycbcr2rgb inverse table describes encoding YUV limited range to RGB full range. To be consistent with the rgb2ycbcr table, convert this to YUV full range to RGB full range, and adjust/expand on the comments. The ic_csc_rgb2rgb table is just an identity matrix, so rename to ic_encode_identity. Fixes: 1aa8ea0d2bd5d ("gpu: ipu-v3: Add Image Converter unit") Suggested-by: Philipp Zabel Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- drivers/gpu/ipu-v3/ipu-ic.c | 61 ++--- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 18816ccf600e..b63a2826b629 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -175,7 +175,7 @@ static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset) writel(value, ic->priv->base + offset); } -struct ic_csc_params { +struct ic_encode_coeff { This less accurate. These are called IC (Task) Parameters in the reference manual, the 64-bit aligned words are called CSC words. Beside the coefficients, this structure also contains the coefficient scale, the offsets, and the saturation mode flag. It seemed to me the renaming was more clear, but I agree the former name conforms better to the manual nomenclature. I will revert this renaming. s16 coeff[3][3];/* signed 9-bit integer coefficients */ s16 offset[3]; /* signed 11+2-bit fixed point offset */ u8 scale:2; /* scale coefficients * 2^(scale-1) */ @@ -183,13 +183,15 @@ struct ic_csc_params { }; /* - * Y = R * .299 + G * .587 + B * .114; - * U = R * -.169 + G * -.332 + B * .500 + 128.; - * V = R * .500 + G * -.419 + B * -.0813 + 128.; + * BT.601 encoding from RGB full range to YUV full range: + * + * Y = .2990 * R + .5870 * G + .1140 * B + * U = -.1687 * R - .3313 * G + .5000 * B + 128 + * V = .5000 * R - .4187 * G - .0813 * B + 128 */ -static const struct ic_csc_params ic_csc_rgb2ycbcr = { +static const struct ic_encode_coeff ic_encode_rgb2ycbcr_601 = { .coeff = { - { 77, 150, 29 }, + { 77, 150, 29 }, { 469, 427, 128 }, { 128, 405, 491 }, We could subtract 512 from the negative values, to improve readability. Agreed. }, @@ -197,8 +199,11 @@ static const struct ic_csc_params ic_csc_rgb2ycbcr = { .scale = 1, }; -/* transparent RGB->RGB matrix for graphics combining */ -static const struct ic_csc_params ic_csc_rgb2rgb = { +/* + * identity matrix, used for transparent RGB->RGB graphics + * combining. + */ +static const struct ic_encode_coeff ic_encode_identity = { .coeff = { { 128, 0, 0 }, { 0, 128, 0 }, @@ -208,17 +213,25 @@ static const struct ic_csc_params ic_csc_rgb2rgb = { }; /* - * R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); - * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); - * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); + * Inverse BT.601 encoding from YUV full range to RGB full range: + * + * R = 1. * Y + 0 * (Cb - 128) + 1.4020 * (Cr - 128) + * G = 1. * Y - .3442 * (Cb - 128) - 0.7142 * (Cr - 128) Should that be ^ .3441 and ^ .7141 ? The coefficients and offsets after rounding should end up the same. Ok. Also, let's consistently either add the leading zero, or leave it out. Yes. + * B = 1. * Y + 1.7720 * (Cb - 128) + 0 * (Cr - 128) + * + * equivalently (factoring out the offsets): + * + * R = 1. * Y + 0 * Cb + 1.4020 * Cr - 179.456 + * G = 1. * Y - .3442 * Cb - 0.7142 * Cr + 135.475 + * B = 1. * Y + 1.7720 * Cb + 0 * Cr - 226.816 */ -static const struct ic_csc_params ic_csc_ycbcr2rgb = { +static const struct ic_encode_coeff ic_encode_ycbcr2rgb_601 = { .coeff = { - { 149, 0, 204 }, - { 149, 462, 408 }, - { 149, 255, 0 }, + { 128, 0, 179 }, + { 128, 468, 421 }, + { 128, 227, 0 }, }, - .offset = { -446, 266, -554 }, + .offset = { -359, 271, -454 }, These seem to be correct. Again, I think this would be easier to read if the negative coefficients were written with a sign as well. .scale = 2, }; @@ -228,7 +241,7 @@ static int init_csc(struct ipu_ic *ic, int csc_index) { struct ipu_ic_priv *priv = ic->priv; - const struct ic_csc_params *params; + const struct ic_encode_coeff *coeff; u32 __i
[PATCH v6 5/7] gpu: ipu-v3: ipu-ic: Add support for limited range encoding
Add support for the following conversions: - YUV full-range to YUV limited-range - YUV limited-range to YUV full-range - YUV limited-range to RGB full-range - RGB full-range to YUV limited-range The last two conversions require operating on the YUV full-range encoding and inverse encoding coefficients, with the YUV-to-YUV limited<->full coefficients. The formula to convert is M_c = M_a * M_b O_c = M_a * O_b + O_a For calculating the RGB full-range to YUV limited-range coefficients: [M_a, O_a] = YUV full-range to YUV limited-range coefficients. [M_b, O_b] = RGB full-range to YUV full-range coefficients. For calculating the YUV limited-range to RGB full-range coefficients: [M_a, O_a] = YUV full-range to RGB full-range coefficients. [M_b, O_b] = YUV limited-range to YUV full-range coefficients. The calculation of [M_c, O_c] is carried out by the function transform_coeffs(). In the future if RGB limited range encoding is required, the same function can be used. And cascaded to create all combinations of encoding for YUV limited/full range <-> RGB limited/full range, passing the output coefficients from one call as the input for the next. For example, to create YUV full-range to RGB limited-range coefficients: [M_a, O_a] = RGB full-range to RGB limited-range coefficients. [M_b, O_b] = YUV full-range to RGB full-range coefficients. and that output sent as input to create YUV limited-range to RGB limited-range coefficients: [M_a, O_a] = YUV full-range to RGB limited-range coefficients. [M_b, O_b] = YUV limited-range to YUV full-range coefficients. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-ic.c | 281 +--- 1 file changed, 263 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 1460901af9b5..a7dd85f8d832 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -178,10 +178,10 @@ static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset) } struct ic_encode_coeff { - s16 coeff[3][3];/* signed 9-bit integer coefficients */ - s16 offset[3]; /* signed 11+2-bit fixed point offset */ - u8 scale:2; /* scale coefficients * 2^(scale-1) */ - bool sat:1; /* saturate to (16, 235(Y) / 240(U, V)) */ + int coeff[3][3];/* signed 9-bit integer coefficients */ + int offset[3]; /* signed 13-bit integer offset */ + int scale; /* scale coefficients * 2^(scale-1) */ + bool sat; /* saturate to (16, 235(Y) / 240(U, V)) */ }; /* @@ -277,6 +277,231 @@ static const struct ic_encode_coeff ic_encode_ycbcr2rgb_709 = { .scale = 2, }; +/* + * YUV full range to YUV limited range: + * + * Y_lim = 0.8588 * Y_full + 16 + * Cb_lim = 0.8784 * (Cb_full - 128) + 128 + * Cr_lim = 0.8784 * (Cr_full - 128) + 128 + */ +static const struct ic_encode_coeff ic_encode_ycbcr_full2lim = { + .coeff = { + { 219, 0, 0 }, + { 0, 224, 0 }, + { 0, 0, 224 }, + }, + .offset = { 64, 62, 62 }, + .scale = 1, +}; + +/* + * YUV limited range to YUV full range: + * + * Y_full = 1.1644 * (Y_lim - 16) + * Cb_full = 1.1384 * (Cb_lim - 128) + 128 + * Cr_full = 1.1384 * (Cr_lim - 128) + 128 + */ +static const struct ic_encode_coeff ic_encode_ycbcr_lim2full = { + .coeff = { + { 149, 0, 0 }, + { 0, 145, 0 }, + { 0, 0, 145 }, + }, + .offset = { -37, -35, -35 }, + .scale = 2, +}; + +/* + * RGB full range to RGB limited range: + * + * R_lim = 0.8588 * R_full + 16 + * G_lim = 0.8588 * G_full + 16 + * B_lim = 0.8588 * B_full + 16 + */ +static const struct ic_encode_coeff +ic_encode_rgb_full2lim __maybe_unused = { + .coeff = { + { 220, 0, 0 }, + { 0, 220, 0 }, + { 0, 0, 220 }, + }, + .offset = { 64, 64, 64 }, + .scale = 1, +}; + +/* + * RGB limited range to RGB full range: + * + * R_full = 1.1644 * (R_lim - 16) + * G_full = 1.1644 * (G_lim - 16) + * B_full = 1.1644 * (B_lim - 16) + */ +static const struct ic_encode_coeff +ic_encode_rgb_lim2full __maybe_unused = { + .coeff = { + { 149, 0, 0 }, + { 0, 149, 0 }, + { 0, 0, 149 }, + }, + .offset = { -37, -37, -37 }, + .scale = 2, +}; + +/* + * Convert a coefficient and scale value in TPMEM register format + * to a signed int times 256 (fix the radix point). The TPMEM register + * coefficient format is a signed 9-bit value (sign bit at bit 8, + * mantissa = coeff * 2 ^ (8 - scale - 1)). + */ +static int coeff_fix(int coeff, int scale) +{ + if (coeff >= 256) + coeff -= 512; + if (scale == 0) + return DIV_ROUND_CLOSEST(coeff, 2); + return coeff << (scale - 1); +} + +/* + * Convert a signed int coefficie
[PATCH v6 1/7] gpu: ipu-v3: ipu-ic: Fix saturation bit offset in TPMEM
The saturation bit was being set at bit 9 in the second 32-bit word of the TPMEM CSC. This isn't correct, the saturation bit is bit 42, which is bit 10 of the second word. Fixes: 1aa8ea0d2bd5d ("gpu: ipu-v3: Add Image Converter unit") Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- drivers/gpu/ipu-v3/ipu-ic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 594c3cbc8291..18816ccf600e 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -257,7 +257,7 @@ static int init_csc(struct ipu_ic *ic, writel(param, base++); param = ((a[0] & 0x1fe0) >> 5) | (params->scale << 8) | - (params->sat << 9); + (params->sat << 10); writel(param, base++); param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) | -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v6 2/7] gpu: ipu-v3: ipu-ic: Fix BT.601 coefficients
The ycbcr2rgb and inverse rgb2ycbcr tables define the BT.601 Y'CbCr encoding coefficients. The rgb2ycbcr table specifically describes the BT.601 encoding from full range RGB to full range YUV. Add table comments to make this more clear. The ycbcr2rgb inverse table describes encoding YUV limited range to RGB full range. To be consistent with the rgb2ycbcr table, convert this to YUV full range to RGB full range, and adjust/expand on the comments. The ic_csc_rgb2rgb table is just an identity matrix, so rename to ic_encode_identity. Fixes: 1aa8ea0d2bd5d ("gpu: ipu-v3: Add Image Converter unit") Suggested-by: Philipp Zabel Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- drivers/gpu/ipu-v3/ipu-ic.c | 61 ++--- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 18816ccf600e..b63a2826b629 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -175,7 +175,7 @@ static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset) writel(value, ic->priv->base + offset); } -struct ic_csc_params { +struct ic_encode_coeff { s16 coeff[3][3];/* signed 9-bit integer coefficients */ s16 offset[3]; /* signed 11+2-bit fixed point offset */ u8 scale:2; /* scale coefficients * 2^(scale-1) */ @@ -183,13 +183,15 @@ struct ic_csc_params { }; /* - * Y = R * .299 + G * .587 + B * .114; - * U = R * -.169 + G * -.332 + B * .500 + 128.; - * V = R * .500 + G * -.419 + B * -.0813 + 128.; + * BT.601 encoding from RGB full range to YUV full range: + * + * Y = .2990 * R + .5870 * G + .1140 * B + * U = -.1687 * R - .3313 * G + .5000 * B + 128 + * V = .5000 * R - .4187 * G - .0813 * B + 128 */ -static const struct ic_csc_params ic_csc_rgb2ycbcr = { +static const struct ic_encode_coeff ic_encode_rgb2ycbcr_601 = { .coeff = { - { 77, 150, 29 }, + { 77, 150, 29 }, { 469, 427, 128 }, { 128, 405, 491 }, }, @@ -197,8 +199,11 @@ static const struct ic_csc_params ic_csc_rgb2ycbcr = { .scale = 1, }; -/* transparent RGB->RGB matrix for graphics combining */ -static const struct ic_csc_params ic_csc_rgb2rgb = { +/* + * identity matrix, used for transparent RGB->RGB graphics + * combining. + */ +static const struct ic_encode_coeff ic_encode_identity = { .coeff = { { 128, 0, 0 }, { 0, 128, 0 }, @@ -208,17 +213,25 @@ static const struct ic_csc_params ic_csc_rgb2rgb = { }; /* - * R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); - * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); - * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); + * Inverse BT.601 encoding from YUV full range to RGB full range: + * + * R = 1. * Y + 0 * (Cb - 128) + 1.4020 * (Cr - 128) + * G = 1. * Y - .3442 * (Cb - 128) - 0.7142 * (Cr - 128) + * B = 1. * Y + 1.7720 * (Cb - 128) + 0 * (Cr - 128) + * + * equivalently (factoring out the offsets): + * + * R = 1. * Y + 0 * Cb + 1.4020 * Cr - 179.456 + * G = 1. * Y - .3442 * Cb - 0.7142 * Cr + 135.475 + * B = 1. * Y + 1.7720 * Cb + 0 * Cr - 226.816 */ -static const struct ic_csc_params ic_csc_ycbcr2rgb = { +static const struct ic_encode_coeff ic_encode_ycbcr2rgb_601 = { .coeff = { - { 149, 0, 204 }, - { 149, 462, 408 }, - { 149, 255, 0 }, + { 128, 0, 179 }, + { 128, 468, 421 }, + { 128, 227, 0 }, }, - .offset = { -446, 266, -554 }, + .offset = { -359, 271, -454 }, .scale = 2, }; @@ -228,7 +241,7 @@ static int init_csc(struct ipu_ic *ic, int csc_index) { struct ipu_ic_priv *priv = ic->priv; - const struct ic_csc_params *params; + const struct ic_encode_coeff *coeff; u32 __iomem *base; const u16 (*c)[3]; const u16 *a; @@ -238,26 +251,26 @@ static int init_csc(struct ipu_ic *ic, (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) - params = _csc_ycbcr2rgb; + coeff = _encode_ycbcr2rgb_601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) - params = _csc_rgb2ycbcr; + coeff = _encode_rgb2ycbcr_601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) - params = _csc_rgb2rgb; + coeff = _encode_identity; else { dev_err(priv->ipu->dev, "Unsupported color space conversion\n"); return -EINVAL; } /* Cast to unsigned */ - c = (const u16 (*)[3])params->coeff; - a = (const u16 *)param
[PATCH v6 4/7] gpu: ipu-v3: ipu-ic: Add support for Rec.709 encoding
Add support for Rec.709 encoding and inverse encoding. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- Changes in v5: - moved API changes to a previous patch. - moved CSC coeff calc to new function calc_csc_coeffs(). Changes in v4: - fix compile error. Chnges in v3: - none. Changes in v2: - only return "Unsupported YCbCr encoding" error if inf != outf, since if inf == outf, the identity matrix can be used. Reported by Tim Harvey. --- drivers/gpu/ipu-v3/ipu-ic.c | 63 - 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index c4048c921801..1460901af9b5 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -214,6 +214,23 @@ static const struct ic_encode_coeff ic_encode_identity = { .scale = 2, }; +/* + * REC.709 encoding from RGB full range to YUV full range: + * + * Y = .2126 * R + .7152 * G + .0722 * B + * U = -.1146 * R - .3854 * G + .5000 * B + 128 + * V = .5000 * R - .4542 * G - .0458 * B + 128 + */ +static const struct ic_encode_coeff ic_encode_rgb2ycbcr_709 = { + .coeff = { + { 54, 183, 19 }, + { 483, 413, 128 }, + { 128, 396, 500 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + /* * Inverse BT.601 encoding from YUV full range to RGB full range: * @@ -237,11 +254,35 @@ static const struct ic_encode_coeff ic_encode_ycbcr2rgb_601 = { .scale = 2, }; +/* + * Inverse REC.709 encoding from YUV full range to RGB full range: + * + * R = 1. * Y + 0 * (Cb - 128) + 1.5748 * (Cr - 128) + * G = 1. * Y - .1873 * (Cb - 128) - .4681 * (Cr - 128) + * B = 1. * Y + 1.8556 * (Cb - 128) + 0 * (Cr - 128) + * + * equivalently (factoring out the offsets): + * + * R = 1. * Y + 0 * Cb + 1.5748 * Cr - 201.574 + * G = 1. * Y - .1873 * Cb - .4681 * Cr + 83.891 + * B = 1. * Y + 1.8556 * Cb + 0 * Cr - 237.517 + */ +static const struct ic_encode_coeff ic_encode_ycbcr2rgb_709 = { + .coeff = { + { 128, 0, 202 }, + { 128, 488, 452 }, + { 128, 238, 0 }, + }, + .offset = { -403, 168, -475 }, + .scale = 2, +}; + static int calc_csc_coeffs(struct ipu_ic_priv *priv, struct ic_encode_coeff *coeff_out, const struct ipu_ic_colorspace *in, const struct ipu_ic_colorspace *out) { + const struct ic_encode_coeff *encode_coeff; bool inverse_encode; if (in->colorspace != out->colorspace) { @@ -249,11 +290,6 @@ static int calc_csc_coeffs(struct ipu_ic_priv *priv, return -ENOTSUPP; } - if (out->enc != V4L2_YCBCR_ENC_601) { - dev_err(priv->ipu->dev, "Only BT.601 encoding supported\n"); - return -ENOTSUPP; - } - if ((in->cs == IPUV3_COLORSPACE_YUV && in->quant != V4L2_QUANTIZATION_FULL_RANGE) || (out->cs == IPUV3_COLORSPACE_YUV && @@ -278,8 +314,21 @@ static int calc_csc_coeffs(struct ipu_ic_priv *priv, inverse_encode = (in->cs == IPUV3_COLORSPACE_YUV); - *coeff_out = inverse_encode ? - ic_encode_ycbcr2rgb_601 : ic_encode_rgb2ycbcr_601; + switch (out->enc) { + case V4L2_YCBCR_ENC_601: + encode_coeff = inverse_encode ? + _encode_ycbcr2rgb_601 : _encode_rgb2ycbcr_601; + break; + case V4L2_YCBCR_ENC_709: + encode_coeff = inverse_encode ? + _encode_ycbcr2rgb_709 : _encode_rgb2ycbcr_709; + break; + default: + dev_err(priv->ipu->dev, "Unsupported YCbCr encoding\n"); + return -ENOTSUPP; + } + + *coeff_out = *encode_coeff; return 0; } -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v6 3/7] gpu: ipu-v3: ipu-ic: Fully describe colorspace conversions
Only providing the input and output RGB/YUV space to the IC task init functions is not sufficient. To fully characterize a colorspace conversion, the colorspace (chromaticities), Y'CbCr encoding standard, and quantization also need to be specified. Define a 'struct ipu_ic_colorspace' that includes all the above, and pass the input and output ipu_ic_colorspace to the IC task init functions. This allows to actually enforce the fact that the IC: - can only encode to/from YUV full range (follow-up patch will remove this restriction). - can only encode to/from RGB full range. - can only encode using BT.601 standard (follow-up patch will add Rec.709 encoding support). - cannot convert colorspaces from input to output, the input and output colorspace chromaticities must be the same. The determination of the CSC coefficients based on the input/output colorspace parameters are moved to a new function calc_csc_coeffs(), called by init_csc(). Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-ic.c | 136 +--- drivers/gpu/ipu-v3/ipu-image-convert.c | 27 ++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 22 +++- include/video/imx-ipu-v3.h | 37 +- 4 files changed, 154 insertions(+), 68 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index b63a2826b629..c4048c921801 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -146,8 +146,10 @@ struct ipu_ic { const struct ic_task_regoffs *reg; const struct ic_task_bitfields *bit; - enum ipu_color_space in_cs, g_in_cs; - enum ipu_color_space out_cs; + struct ipu_ic_colorspace in_cs; + struct ipu_ic_colorspace g_in_cs; + struct ipu_ic_colorspace out_cs; + bool graphics; bool rotation; bool in_use; @@ -235,42 +237,83 @@ static const struct ic_encode_coeff ic_encode_ycbcr2rgb_601 = { .scale = 2, }; +static int calc_csc_coeffs(struct ipu_ic_priv *priv, + struct ic_encode_coeff *coeff_out, + const struct ipu_ic_colorspace *in, + const struct ipu_ic_colorspace *out) +{ + bool inverse_encode; + + if (in->colorspace != out->colorspace) { + dev_err(priv->ipu->dev, "Cannot convert colorspaces\n"); + return -ENOTSUPP; + } + + if (out->enc != V4L2_YCBCR_ENC_601) { + dev_err(priv->ipu->dev, "Only BT.601 encoding supported\n"); + return -ENOTSUPP; + } + + if ((in->cs == IPUV3_COLORSPACE_YUV && +in->quant != V4L2_QUANTIZATION_FULL_RANGE) || + (out->cs == IPUV3_COLORSPACE_YUV && +out->quant != V4L2_QUANTIZATION_FULL_RANGE)) { + dev_err(priv->ipu->dev, "Limited range YUV not supported\n"); + return -ENOTSUPP; + } + + if ((in->cs == IPUV3_COLORSPACE_RGB && +in->quant != V4L2_QUANTIZATION_FULL_RANGE) || + (out->cs == IPUV3_COLORSPACE_RGB && +out->quant != V4L2_QUANTIZATION_FULL_RANGE)) { + dev_err(priv->ipu->dev, "Limited range RGB not supported\n"); + return -ENOTSUPP; + } + + if (in->cs == out->cs) { + *coeff_out = ic_encode_identity; + + return 0; + } + + inverse_encode = (in->cs == IPUV3_COLORSPACE_YUV); + + *coeff_out = inverse_encode ? + ic_encode_ycbcr2rgb_601 : ic_encode_rgb2ycbcr_601; + + return 0; +} + static int init_csc(struct ipu_ic *ic, - enum ipu_color_space inf, - enum ipu_color_space outf, + const struct ipu_ic_colorspace *in, + const struct ipu_ic_colorspace *out, int csc_index) { struct ipu_ic_priv *priv = ic->priv; - const struct ic_encode_coeff *coeff; + struct ic_encode_coeff coeff; u32 __iomem *base; const u16 (*c)[3]; const u16 *a; u32 param; + int ret; + + ret = calc_csc_coeffs(priv, , in, out); + if (ret) + return ret; base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); - if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) - coeff = _encode_ycbcr2rgb_601; - else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) - coeff = _encode_rgb2ycbcr_601; - else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) - coeff = _encode_identity; - else { - dev_err(priv->ipu->dev, "Unsupported color space conversion\n"); - return -EINVAL; -
[PATCH v5 5/7] gpu: ipu-v3: ipu-ic: Add support for limited range encoding
Add support for the following conversions: - YUV full-range to YUV limited-range - YUV limited-range to YUV full-range - YUV limited-range to RGB full-range - RGB full-range to YUV limited-range The last two conversions require operating on the YUV full-range encoding and inverse encoding coefficients, with the YUV-to-YUV limited<->full coefficients. The formula to convert is M_c = M_a * M_b O_c = M_a * O_b + O_a For calculating the RGB full-range to YUV limited-range coefficients: [M_a, O_a] = YUV full-range to YUV limited-range coefficients. [M_b, O_b] = RGB full-range to YUV full-range coefficients. For calculating the YUV limited-range to RGB full-range coefficients: [M_a, O_a] = YUV full-range to RGB full-range coefficients. [M_b, O_b] = YUV limited-range to YUV full-range coefficients. The calculation of [M_c, O_c] is carried out by the function transform_coeffs(). In the future if RGB limited range encoding is required, the same function can be used. And cascaded to create all combinations of encoding for YUV limited/full range <-> RGB limited/full range, passing the output coefficients from one call as the input for the next. For example, to create YUV full-range to RGB limited-range coefficients: [M_a, O_a] = RGB full-range to RGB limited-range coefficients. [M_b, O_b] = YUV full-range to RGB full-range coefficients. and that output sent as input to create YUV limited-range to RGB limited-range coefficients: [M_a, O_a] = YUV full-range to RGB limited-range coefficients. [M_b, O_b] = YUV limited-range to YUV full-range coefficients. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-ic.c | 281 +--- 1 file changed, 263 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 012ea2239e97..861f43556df4 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -178,10 +178,10 @@ static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset) } struct ic_encode_coeff { - s16 coeff[3][3];/* signed 9-bit integer coefficients */ - s16 offset[3]; /* signed 11+2-bit fixed point offset */ - u8 scale:2; /* scale coefficients * 2^(scale-1) */ - bool sat:1; /* saturate to (16, 235(Y) / 240(U, V)) */ + int coeff[3][3];/* signed 9-bit integer coefficients */ + int offset[3]; /* signed 13-bit integer offset */ + int scale; /* scale coefficients * 2^(scale-1) */ + bool sat; /* saturate to (16, 235(Y) / 240(U, V)) */ }; /* @@ -277,6 +277,231 @@ static const struct ic_encode_coeff ic_encode_ycbcr2rgb_709 = { .scale = 2, }; +/* + * YUV full range to YUV limited range: + * + * Y_lim = 0.8588 * Y_full + 16 + * Cb_lim = 0.8784 * (Cb_full - 128) + 128 + * Cr_lim = 0.8784 * (Cr_full - 128) + 128 + */ +static const struct ic_encode_coeff ic_encode_ycbcr_full2lim = { + .coeff = { + { 219, 0, 0 }, + { 0, 224, 0 }, + { 0, 0, 224 }, + }, + .offset = { 64, 62, 62 }, + .scale = 1, +}; + +/* + * YUV limited range to YUV full range: + * + * Y_full = 1.1644 * (Y_lim - 16) + * Cb_full = 1.1384 * (Cb_lim - 128) + 128 + * Cr_full = 1.1384 * (Cr_lim - 128) + 128 + */ +static const struct ic_encode_coeff ic_encode_ycbcr_lim2full = { + .coeff = { + { 149, 0, 0 }, + { 0, 145, 0 }, + { 0, 0, 145 }, + }, + .offset = { -37, -35, -35 }, + .scale = 2, +}; + +/* + * RGB full range to RGB limited range: + * + * R_lim = 0.8588 * R_full + 16 + * G_lim = 0.8588 * G_full + 16 + * B_lim = 0.8588 * B_full + 16 + */ +static const struct ic_encode_coeff +ic_encode_rgb_full2lim __maybe_unused = { + .coeff = { + { 219, 0, 0 }, + { 0, 219, 0 }, + { 0, 0, 219 }, + }, + .offset = { 64, 64, 64 }, + .scale = 1, +}; + +/* + * RGB limited range to RGB full range: + * + * R_full = 1.1644 * (R_lim - 16) + * G_full = 1.1644 * (G_lim - 16) + * B_full = 1.1644 * (B_lim - 16) + */ +static const struct ic_encode_coeff +ic_encode_rgb_lim2full __maybe_unused = { + .coeff = { + { 149, 0, 0 }, + { 0, 149, 0 }, + { 0, 0, 149 }, + }, + .offset = { -37, -37, -37 }, + .scale = 2, +}; + +/* + * Convert a coefficient and scale value in TPMEM register format + * to a signed int times 256 (fix the radix point). The TPMEM register + * coefficient format is a signed 9-bit value (sign bit at bit 8, + * mantissa = coeff * 2 ^ (8 - scale - 1)). + */ +static int coeff_fix(int coeff, int scale) +{ + if (coeff >= 256) + coeff -= 512; + if (scale == 0) + return DIV_ROUND_CLOSEST(coeff, 2); + return coeff << (scale - 1); +} + +/* + * Convert a signed int coefficie
[PATCH v5 2/7] gpu: ipu-v3: ipu-ic: Fix BT.601 coefficients
The ycbcr2rgb and inverse rgb2ycbcr tables define the BT.601 Y'CbCr encoding coefficients. The rgb2ycbcr table specifically describes the BT.601 encoding from full range RGB to full range YUV. Add table comments to make this more clear. The ycbcr2rgb inverse table describes encoding YUV limited range to RGB full range. To be consistent with the rgb2ycbcr table, convert this to YUV full range to RGB full range, and adjust/expand on the comments. The ic_csc_rgb2rgb table is just an identity matrix, so rename to ic_encode_identity. Fixes: 1aa8ea0d2bd5d ("gpu: ipu-v3: Add Image Converter unit") Suggested-by: Philipp Zabel Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- drivers/gpu/ipu-v3/ipu-ic.c | 63 ++--- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 18816ccf600e..71a0409093e6 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -175,7 +175,7 @@ static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset) writel(value, ic->priv->base + offset); } -struct ic_csc_params { +struct ic_encode_coeff { s16 coeff[3][3];/* signed 9-bit integer coefficients */ s16 offset[3]; /* signed 11+2-bit fixed point offset */ u8 scale:2; /* scale coefficients * 2^(scale-1) */ @@ -183,22 +183,27 @@ struct ic_csc_params { }; /* - * Y = R * .299 + G * .587 + B * .114; - * U = R * -.169 + G * -.332 + B * .500 + 128.; - * V = R * .500 + G * -.419 + B * -.0813 + 128.; + * BT.601 encoding from RGB full range to YUV full range: + * + * Y = .2990 * R + .5870 * G + .1140 * B + * U = -.1687 * R - .3313 * G + .5000 * B + 128 + * V = .5000 * R - .4187 * G - .0813 * B + 128 */ -static const struct ic_csc_params ic_csc_rgb2ycbcr = { +static const struct ic_encode_coeff ic_encode_rgb2ycbcr_601 = { .coeff = { - { 77, 150, 29 }, - { 469, 427, 128 }, + { 76, 150, 29 }, + { 469, 428, 128 }, { 128, 405, 491 }, }, .offset = { 0, 512, 512 }, .scale = 1, }; -/* transparent RGB->RGB matrix for graphics combining */ -static const struct ic_csc_params ic_csc_rgb2rgb = { +/* + * identity matrix, used for transparent RGB->RGB graphics + * combining. + */ +static const struct ic_encode_coeff ic_encode_identity = { .coeff = { { 128, 0, 0 }, { 0, 128, 0 }, @@ -208,17 +213,25 @@ static const struct ic_csc_params ic_csc_rgb2rgb = { }; /* - * R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); - * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); - * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); + * Inverse BT.601 encoding from YUV full range to RGB full range: + * + * R = 1. * Y + 0 * (Cb - 128) + 1.4020 * (Cr - 128) + * G = 1. * Y - .3442 * (Cb - 128) - 0.7142 * (Cr - 128) + * B = 1. * Y + 1.7720 * (Cb - 128) + 0 * (Cr - 128) + * + * equivalently (factoring out the offsets): + * + * R = 1. * Y + 0 * Cb + 1.4020 * Cr - 179.456 + * G = 1. * Y - .3442 * Cb - 0.7142 * Cr + 135.475 + * B = 1. * Y + 1.7720 * Cb + 0 * Cr - 226.816 */ -static const struct ic_csc_params ic_csc_ycbcr2rgb = { +static const struct ic_encode_coeff ic_encode_ycbcr2rgb_601 = { .coeff = { - { 149, 0, 204 }, - { 149, 462, 408 }, - { 149, 255, 0 }, + { 128, 0, 179 }, + { 128, 468, 421 }, + { 128, 226, 0 }, }, - .offset = { -446, 266, -554 }, + .offset = { -359, 271, -454 }, .scale = 2, }; @@ -228,7 +241,7 @@ static int init_csc(struct ipu_ic *ic, int csc_index) { struct ipu_ic_priv *priv = ic->priv; - const struct ic_csc_params *params; + const struct ic_encode_coeff *coeff; u32 __iomem *base; const u16 (*c)[3]; const u16 *a; @@ -238,26 +251,26 @@ static int init_csc(struct ipu_ic *ic, (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) - params = _csc_ycbcr2rgb; + coeff = _encode_ycbcr2rgb_601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) - params = _csc_rgb2ycbcr; + coeff = _encode_rgb2ycbcr_601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) - params = _csc_rgb2rgb; + coeff = _encode_identity; else { dev_err(priv->ipu->dev, "Unsupported color space conversion\n"); return -EINVAL; } /* Cast to unsigned */ - c = (const u16 (*)[3])params->coeff; - a = (const u16 *)params->of
[PATCH v5 3/7] gpu: ipu-v3: ipu-ic: Fully describe colorspace conversions
Only providing the input and output RGB/YUV space to the IC task init functions is not sufficient. To fully characterize a colorspace conversion, the colorspace (chromaticities), Y'CbCr encoding standard, and quantization also need to be specified. Define a 'struct ipu_ic_colorspace' that includes all the above, and pass the input and output ipu_ic_colorspace to the IC task init functions. This allows to actually enforce the fact that the IC: - can only encode to/from YUV full range (follow-up patch will remove this restriction). - can only encode to/from RGB full range. - can only encode using BT.601 standard (follow-up patch will add Rec.709 encoding support). - cannot convert colorspaces from input to output, the input and output colorspace chromaticities must be the same. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-ic.c | 101 drivers/gpu/ipu-v3/ipu-image-convert.c | 27 -- drivers/staging/media/imx/imx-ic-prpencvf.c | 22 +++-- include/video/imx-ipu-v3.h | 37 +-- 4 files changed, 127 insertions(+), 60 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 71a0409093e6..02043f23f411 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -146,8 +146,10 @@ struct ipu_ic { const struct ic_task_regoffs *reg; const struct ic_task_bitfields *bit; - enum ipu_color_space in_cs, g_in_cs; - enum ipu_color_space out_cs; + struct ipu_ic_colorspace in_cs; + struct ipu_ic_colorspace g_in_cs; + struct ipu_ic_colorspace out_cs; + bool graphics; bool rotation; bool in_use; @@ -236,8 +238,8 @@ static const struct ic_encode_coeff ic_encode_ycbcr2rgb_601 = { }; static int init_csc(struct ipu_ic *ic, - enum ipu_color_space inf, - enum ipu_color_space outf, + const struct ipu_ic_colorspace *in, + const struct ipu_ic_colorspace *out, int csc_index) { struct ipu_ic_priv *priv = ic->priv; @@ -247,19 +249,41 @@ static int init_csc(struct ipu_ic *ic, const u16 *a; u32 param; + if (in->colorspace != out->colorspace) { + dev_err(priv->ipu->dev, "Cannot convert colorspaces\n"); + return -ENOTSUPP; + } + + if (out->enc != V4L2_YCBCR_ENC_601) { + dev_err(priv->ipu->dev, "Only BT.601 encoding supported\n"); + return -ENOTSUPP; + } + + if ((in->cs == IPUV3_COLORSPACE_YUV && +in->quant != V4L2_QUANTIZATION_FULL_RANGE) || + (out->cs == IPUV3_COLORSPACE_YUV && +out->quant != V4L2_QUANTIZATION_FULL_RANGE)) { + dev_err(priv->ipu->dev, "Limited range YUV not supported\n"); + return -ENOTSUPP; + } + + if ((in->cs == IPUV3_COLORSPACE_RGB && +in->quant != V4L2_QUANTIZATION_FULL_RANGE) || + (out->cs == IPUV3_COLORSPACE_RGB && +out->quant != V4L2_QUANTIZATION_FULL_RANGE)) { + dev_err(priv->ipu->dev, "Limited range RGB not supported\n"); + return -ENOTSUPP; + } + base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); - if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) + if (in->cs == out->cs) + coeff = _encode_identity; + else if (in->cs == IPUV3_COLORSPACE_YUV) coeff = _encode_ycbcr2rgb_601; - else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) + else coeff = _encode_rgb2ycbcr_601; - else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) - coeff = _encode_identity; - else { - dev_err(priv->ipu->dev, "Unsupported color space conversion\n"); - return -EINVAL; - } /* Cast to unsigned */ c = (const u16 (*)[3])coeff->coeff; @@ -357,14 +381,14 @@ void ipu_ic_task_enable(struct ipu_ic *ic) if (ic->rotation) ic_conf |= ic->bit->ic_conf_rot_en; - if (ic->in_cs != ic->out_cs) + if (ic->in_cs.cs != ic->out_cs.cs) ic_conf |= ic->bit->ic_conf_csc1_en; if (ic->graphics) { ic_conf |= ic->bit->ic_conf_cmb_en; ic_conf |= ic->bit->ic_conf_csc1_en; - if (ic->g_in_cs != ic->out_cs) + if (ic->g_in_cs.cs != ic->out_cs.cs) ic_conf |= ic->bit->ic_conf_csc2_en; } @@ -399,7 +423,7 @@ void ipu_ic_task_disable(struct ipu_ic *ic) EXPORT_SYMBOL_GPL(i
[PATCH v5 1/7] gpu: ipu-v3: ipu-ic: Fix saturation bit offset in TPMEM
The saturation bit was being set at bit 9 in the second 32-bit word of the TPMEM CSC. This isn't correct, the saturation bit is bit 42, which is bit 10 of the second word. Fixes: 1aa8ea0d2bd5d ("gpu: ipu-v3: Add Image Converter unit") Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- drivers/gpu/ipu-v3/ipu-ic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 594c3cbc8291..18816ccf600e 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -257,7 +257,7 @@ static int init_csc(struct ipu_ic *ic, writel(param, base++); param = ((a[0] & 0x1fe0) >> 5) | (params->scale << 8) | - (params->sat << 9); + (params->sat << 10); writel(param, base++); param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) | -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v5 4/7] gpu: ipu-v3: ipu-ic: Add support for Rec.709 encoding
Add support for Rec.709 encoding and inverse encoding. The determination of the CSC coefficients based on the input/output colorspace parameters are moved to a new function calc_csc_coeffs(). Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- Changes in v5: - moved API changes to a previous patch. - moved CSC coeff calc to new function calc_csc_coeffs(). Changes in v4: - fix compile error. Chnges in v3: - none. Changes in v2: - only return "Unsupported YCbCr encoding" error if inf != outf, since if inf == outf, the identity matrix can be used. Reported by Tim Harvey. --- drivers/gpu/ipu-v3/ipu-ic.c | 120 1 file changed, 94 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 02043f23f411..012ea2239e97 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -214,6 +214,23 @@ static const struct ic_encode_coeff ic_encode_identity = { .scale = 2, }; +/* + * REC.709 encoding from RGB full range to YUV full range: + * + * Y = .2126 * R + .7152 * G + .0722 * B + * U = -.1146 * R - .3854 * G + .5000 * B + 128 + * V = .5000 * R - .4542 * G - .0458 * B + 128 + */ +static const struct ic_encode_coeff ic_encode_rgb2ycbcr_709 = { + .coeff = { + { 54, 183, 19 }, + { 483, 413, 128 }, + { 128, 396, 500 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + /* * Inverse BT.601 encoding from YUV full range to RGB full range: * @@ -237,28 +254,42 @@ static const struct ic_encode_coeff ic_encode_ycbcr2rgb_601 = { .scale = 2, }; -static int init_csc(struct ipu_ic *ic, - const struct ipu_ic_colorspace *in, - const struct ipu_ic_colorspace *out, - int csc_index) +/* + * Inverse REC.709 encoding from YUV full range to RGB full range: + * + * R = 1. * Y + 0 * (Cb - 128) + 1.5748 * (Cr - 128) + * G = 1. * Y - .1873 * (Cb - 128) - .4681 * (Cr - 128) + * B = 1. * Y + 1.8556 * (Cb - 128) + 0 * (Cr - 128) + * + * equivalently (factoring out the offsets): + * + * R = 1. * Y + 0 * Cb + 1.5748 * Cr - 201.574 + * G = 1. * Y - .1873 * Cb - .4681 * Cr + 83.891 + * B = 1. * Y + 1.8556 * Cb + 0 * Cr - 237.517 + */ +static const struct ic_encode_coeff ic_encode_ycbcr2rgb_709 = { + .coeff = { + { 128, 0, 202 }, + { 128, 488, 452 }, + { 128, 238, 0 }, + }, + .offset = { -403, 168, -475 }, + .scale = 2, +}; + +static int calc_csc_coeffs(struct ipu_ic_priv *priv, + struct ic_encode_coeff *coeff_out, + const struct ipu_ic_colorspace *in, + const struct ipu_ic_colorspace *out) { - struct ipu_ic_priv *priv = ic->priv; - const struct ic_encode_coeff *coeff; - u32 __iomem *base; - const u16 (*c)[3]; - const u16 *a; - u32 param; + const struct ic_encode_coeff *encode_coeff; + bool inverse_encode; if (in->colorspace != out->colorspace) { dev_err(priv->ipu->dev, "Cannot convert colorspaces\n"); return -ENOTSUPP; } - if (out->enc != V4L2_YCBCR_ENC_601) { - dev_err(priv->ipu->dev, "Only BT.601 encoding supported\n"); - return -ENOTSUPP; - } - if ((in->cs == IPUV3_COLORSPACE_YUV && in->quant != V4L2_QUANTIZATION_FULL_RANGE) || (out->cs == IPUV3_COLORSPACE_YUV && @@ -275,26 +306,63 @@ static int init_csc(struct ipu_ic *ic, return -ENOTSUPP; } + if (in->cs == out->cs) { + *coeff_out = ic_encode_identity; + + return 0; + } + + inverse_encode = (in->cs == IPUV3_COLORSPACE_YUV); + + switch (out->enc) { + case V4L2_YCBCR_ENC_601: + encode_coeff = inverse_encode ? + _encode_ycbcr2rgb_601 : _encode_rgb2ycbcr_601; + break; + case V4L2_YCBCR_ENC_709: + encode_coeff = inverse_encode ? + _encode_ycbcr2rgb_709 : _encode_rgb2ycbcr_709; + break; + default: + dev_err(priv->ipu->dev, "Unsupported YCbCr encoding\n"); + return -ENOTSUPP; + } + + *coeff_out = *encode_coeff; + + return 0; +} + +static int init_csc(struct ipu_ic *ic, + const struct ipu_ic_colorspace *in, + const struct ipu_ic_colorspace *out, + int csc_index) +{ + struct ipu_ic_priv *priv = ic->priv; + struct ic_encode_coeff coeff; + u32 __iomem *base; + const u16 (*c)[3]; + const u16 *a; + u32 param; + int ret; + + ret
Re: [PATCH v4 1/4] gpu: ipu-v3: ipu-ic: Rename yuv2rgb encoding matrices
On 2/13/19 2:35 AM, Philipp Zabel wrote: On Tue, 2019-02-12 at 09:42 -0800, Steve Longerbeam wrote: [...] But what about this "SAT_MODE" field in the IC task parameter memory? That just controls the saturation. The result after the matrix multiplication is either saturated to [0..255] or to [16..235]/[16..240] when converting from the internal representation to the 8 bit output. By saturation I think you mean clipped to those ranges? Yes, thanks. I didn't realize it sounds weird to use saturated this way. See:https://en.wikipedia.org/wiki/Saturation_arithmetic Ok, saturation can mean the same thing as clipping/clamping. Thanks for the article. I tested a RGB->YUV pipeline with the .sat bit set in the BT.601 rgb2yuv table, with the following pipeline on the SabreSD: 'ov5640 1-003c':0       [fmt:RGB565_2X8_LE/1024x768@1/30 field:none colorspace:srgb xfer:srgb ycbcr:601 quantization:full-range] 'imx6-mipi-csi2':0       [fmt:RGB565_2X8_LE/1024x768 field:none colorspace:srgb xfer:srgb ycbcr:601 quantization:full-range] 'imx6-mipi-csi2':2       [fmt:RGB565_2X8_LE/1024x768 field:none colorspace:srgb xfer:srgb ycbcr:601 quantization:full-range] 'ipu1_csi1':0       [fmt:RGB565_2X8_LE/1024x768@1/30 field:none colorspace:srgb xfer:srgb ycbcr:601 quantization:full-range        crop.bounds:(0,0)/1024x768        crop:(0,0)/1024x768        compose.bounds:(0,0)/1024x768        compose:(0,0)/1024x768] 'ipu1_csi1':1       [fmt:ARGB_1X32/1024x768@1/30 field:none colorspace:srgb xfer:srgb ycbcr:601 quantization:full-range] 'ipu1_ic_prp':0       [fmt:ARGB_1X32/1024x768@1/30 field:none colorspace:srgb xfer:srgb ycbcr:601 quantization:full-range] 'ipu1_ic_prp':1       [fmt:ARGB_1X32/1024x768@1/30 field:none colorspace:srgb xfer:srgb ycbcr:601 quantization:full-range] 'ipu1_ic_prpenc':0       [fmt:ARGB_1X32/1024x768@1/30 field:none colorspace:srgb xfer:srgb ycbcr:601 quantization:full-range] 'ipu1_ic_prpenc':1       [fmt:AYUV8_1X32/800x600@1/30 field:none colorspace:srgb xfer:srgb ycbcr:601 quantization:lim-range] /dev/video0:0 Format Video Capture:    Width/Height : 800/600    Pixel Format : 'YV12'    Field : None    Bytes per Line   : 800    Size Image   : 72    Colorspace   : sRGB    Transfer Function : sRGB    YCbCr/HSV Encoding: ITU-R 601    Quantization : Limited Range    Flags : The result being that the captured image colors are all off (there's a bright pink shade to the images). But I discovered the init_csc() function was not setting the saturation bit at the correct bit offset within the TPMEM. The saturation bit is bit 42, or bit 10 of the second 32-bit word. But the code was writing to bit 9 of the second word. After correcting this, saturation is working fine. I have added another patch that fixes this for v5 series. SAT_MODE should be set for conversions to YUV limited range so that the coefficients can be rounded to the closest value. Well, we have already rounded the coefficients to the nearest int in the tables. Do you mean the final result (coeff * color component + offset) is rounded? The manual says so: "The final calculation result is limited according to the SAT_MODE parameter and rounded to 8 bits", but that's not what I meant. Still, I might have been mistaken. I think due to the fact that the coefficients are multiplied by up to 255 (max pixel value) and then effectively divided by 256 when converting to 8 bit, the only way to overflow limited range is if two coefficients are rounded away from zero in the calculation of a single component. This doesn't seem to happen in practice. A constructed example, conversion to YUV limited range with carefully chosen coefficients. Y = R * .1817 + G * .6153 + B * .0618 + 16; Note that .1817 + .6153 + .0618 < 219/255. With rounded coefficients though: Y = (R * 47 + G * 158 + B * 16 + (64 << 6)) / 256 = 236.136 Yes, for a rec.709 conversion and max/worst-case RGB signal = (255,255,255). But the rec.709 coefficients for Y are actually Y = (R * 47 + G * 157 + B * 16 + (16 << 8)) / 256 which for RGB = (255,255,255), Y = 235.14, which doesn't overflow limited range. Steve ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v4 1/4] gpu: ipu-v3: ipu-ic: Rename yuv2rgb encoding matrices
On 2/12/19 2:17 AM, Philipp Zabel wrote: Hi Steve, On Mon, 2019-02-11 at 10:24 -0800, Steve Longerbeam wrote: [...] Looking more closely at these coefficients now, I see you are right, they are the BT.601 YUV full-range coefficients (Y range 0 to 1, U and V range -0.5 to 0.5). Well, not even that -- the coefficients are not being scaled to the limited ranges, but the 0.5 offset (128) _is_ being added to U/V, but no offset for Y. So it is even more messed up. Your corrected coefficients and offsets look correct to me: Y coefficients scaled to (235 - 16) / 255 and U/V coefficients scaled to (240 - 16) / 255, and add the offsets for both Y and U/V. But what about this "SAT_MODE" field in the IC task parameter memory? That just controls the saturation. The result after the matrix multiplication is either saturated to [0..255] or to [16..235]/[16..240] when converting from the internal representation to the 8 bit output. By saturation I think you mean clipped to those ranges? According to the manual the hardware will automatically convert the written coefficients to the correct limited ranges. Where did you get that from? "The final calculation result is limited according to the SAT_MODE parameter and rounded to 8 bits." I see no mention of coefficients being modified. Well, as is often the case with this manual, I was interpreting based on poorly written information. By "final calculation result is limited according to the SAT_MODE parameter" I interpreted that to mean the hardware enables scaling from full range to limited range. But I concede that it more likely means it clips the output to those ranges. I see there is a "sat" field defined in the struct but is not being set in the tables. So what should we do, define the full range coefficients, and make use of SAT_MODE h/w feature, or scale/offset the coefficients ourselves and not use SAT_MODE? I'm inclined to do the former. SAT_MODE should be set for conversions to YUV limited range so that the coefficients can be rounded to the closest value. Well, we have already rounded the coefficients to the nearest int in the tables. Do you mean the final result (coeff * color component + offset) is rounded? Otherwise we'd have to round towards zero, possibly with a larger error, to make sure the results are inside the valid ranges. Makes sense, I will turn on that bit for limited range YUV output for v5, so that the final color component result is clipped to limited range and rounded. Steve ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v4 3/4] gpu: ipu-v3: ipu-ic: Add support for BT.709 encoding
On 2/12/19 3:34 AM, Philipp Zabel wrote: Hi Steve, On Mon, 2019-02-11 at 17:20 -0800, Steve Longerbeam wrote: [...] Should we support YUV BT.601 <-> YUV REC.709 conversions? That would require separate encodings for input and output. How about if we pass the input and output encodings to the init ic task functions, but for now require they be the same? We can support transcoding in a later series. [...] Again, I think for now, just include input/output quantization but require full range for RGB and limited range for YUV. Yes, that is fine. I'd just like to avoid unnecessary interface changes between ipu-v3 and imx-media. So if we have to change it right now, why not plan ahead. Agreed! But that really balloons the arguments to ipu_ic_task_init_*(). Should we create an ipu_ic_task_init structure? I wonder if we should just expose struct ic_csc_params I had basically the same idea. I wasn't thinking of creating a helper to fill in the params but sure, I'll add that. Steve and provide a helper to fill it given colorspace and V4L2 encoding/quantization parameters. Something like: struct ipu_ic_csc_params csc; imx_media_init_ic_csc_params(, in_cs, in_encoding, in_quantization, out_cs, out_encoding, out_quantization); ipu_ic_task_init(ic, in_width, in_height, out_width, out_height, ); // or ipu_ic_task_init_rsc(ic, rsc, ); regards Philipp ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v4 1/4] gpu: ipu-v3: ipu-ic: Rename yuv2rgb encoding matrices
On 2/11/19 1:58 AM, Philipp Zabel wrote: On Fri, 2019-02-08 at 17:47 -0800, Steve Longerbeam wrote: The ycbcr2rgb and inverse rgb2ycbcr matrices define the BT.601 encoding coefficients, so rename them to indicate that. And add some comments to make clear these are BT.601 coefficients encoding between YUV limited range and RGB full range. The ic_csc_rgb2rgb matrix is just an identity matrix, so rename to ic_csc_identity. No functional changes. Signed-off-by: Steve Longerbeam --- Changes in v2: - rename ic_csc_rgb2rgb matrix to ic_csc_identity. --- drivers/gpu/ipu-v3/ipu-ic.c | 21 ++--- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 594c3cbc8291..3ef61f0b509b 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -183,11 +183,13 @@ struct ic_csc_params { }; /* + * BT.601 encoding from RGB full range to YUV limited range: + * * Y = R * .299 + G * .587 + B * .114; * U = R * -.169 + G * -.332 + B * .500 + 128.; * V = R * .500 + G * -.419 + B * -.0813 + 128.; Hm, this is a conversion to full range BT.601. For limited range, the matrix coefficients 0.2990Â Â 0.5870Â Â 0.1140 -0.1687 -0.3313Â Â 0.5000 0.5000 -0.4187 -0.0813 should be multiplied with 219/255 (Y) and 224/255 (U,V), respectively: Y = R * .2568Â + G * .5041 + B * .0979 + 16; U = R * -.1482 + G * -.2910Â + B *Â .4392 + 128; V = R * .4392 + G * -.3678 + B * -.0714 + 128; */ -static const struct ic_csc_params ic_csc_rgb2ycbcr = { +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt601 = { .coeff = { { 77, 150, 29 }, { 469, 427, 128 }, @@ -197,8 +199,11 @@ static const struct ic_csc_params ic_csc_rgb2ycbcr = { .scale = 1, }; -/* transparent RGB->RGB matrix for graphics combining */ -static const struct ic_csc_params ic_csc_rgb2rgb = { +/* + * identity matrix, used for transparent RGB->RGB graphics + * combining. + */ +static const struct ic_csc_params ic_csc_identity = { .coeff = { { 128, 0, 0 }, { 0, 128, 0 }, @@ -208,11 +213,13 @@ static const struct ic_csc_params ic_csc_rgb2rgb = { }; /* + * Inverse BT.601 encoding from YUV limited range to RGB full range: + * * R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */ This looks correct. Agreed, the coefficients in the comments are correct, but the actual table values were a bit off. I will correct them for v5 (Green offset should be 272 in the table, not 266). Steve -static const struct ic_csc_params ic_csc_ycbcr2rgb = { +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .coeff = { { 149, 0, 204 }, { 149, 462, 408 }, @@ -238,11 +245,11 @@ static int init_csc(struct ipu_ic *ic, (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) - params = _csc_ycbcr2rgb; + params = _csc_ycbcr2rgb_bt601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) - params = _csc_rgb2ycbcr; + params = _csc_rgb2ycbcr_bt601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) - params = _csc_rgb2rgb; + params = _csc_identity; else { dev_err(priv->ipu->dev, "Unsupported color space conversion\n"); return -EINVAL; regards Philipp ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v4 1/4] gpu: ipu-v3: ipu-ic: Rename yuv2rgb encoding matrices
Hi Philipp, On 2/11/19 1:58 AM, Philipp Zabel wrote: On Fri, 2019-02-08 at 17:47 -0800, Steve Longerbeam wrote: The ycbcr2rgb and inverse rgb2ycbcr matrices define the BT.601 encoding coefficients, so rename them to indicate that. And add some comments to make clear these are BT.601 coefficients encoding between YUV limited range and RGB full range. The ic_csc_rgb2rgb matrix is just an identity matrix, so rename to ic_csc_identity. No functional changes. Signed-off-by: Steve Longerbeam --- Changes in v2: - rename ic_csc_rgb2rgb matrix to ic_csc_identity. --- drivers/gpu/ipu-v3/ipu-ic.c | 21 ++--- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 594c3cbc8291..3ef61f0b509b 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -183,11 +183,13 @@ struct ic_csc_params { }; /* + * BT.601 encoding from RGB full range to YUV limited range: + * * Y = R * .299 + G * .587 + B * .114; * U = R * -.169 + G * -.332 + B * .500 + 128.; * V = R * .500 + G * -.419 + B * -.0813 + 128.; Hm, this is a conversion to full range BT.601. For limited range, the matrix coefficients 0.2990Â Â 0.5870Â Â 0.1140 -0.1687 -0.3313Â Â 0.5000 0.5000 -0.4187 -0.0813 should be multiplied with 219/255 (Y) and 224/255 (U,V), respectively: Y = R * .2568Â + G * .5041 + B * .0979 + 16; U = R * -.1482 + G * -.2910Â + B *Â .4392 + 128; V = R * .4392 + G * -.3678 + B * -.0714 + 128; Looking more closely at these coefficients now, I see you are right, they are the BT.601 YUV full-range coefficients (Y range 0 to 1, U and V range -0.5 to 0.5). Well, not even that -- the coefficients are not being scaled to the limited ranges, but the 0.5 offset (128) _is_ being added to U/V, but no offset for Y. So it is even more messed up. Your corrected coefficients and offsets look correct to me: Y coefficients scaled to (235 - 16) / 255 and U/V coefficients scaled to (240 - 16)Â / 255, and add the offsets for both Y and U/V. But what about this "SAT_MODE" field in the IC task parameter memory? According to the manual the hardware will automatically convert the written coefficients to the correct limited ranges. I see there is a "sat" field defined in the struct but is not being set in the tables. So what should we do, define the full range coefficients, and make use of SAT_MODE h/w feature, or scale/offset the coefficients ourselves and not use SAT_MODE? I'm inclined to do the former. Steve */ -static const struct ic_csc_params ic_csc_rgb2ycbcr = { +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt601 = { .coeff = { { 77, 150, 29 }, { 469, 427, 128 }, @@ -197,8 +199,11 @@ static const struct ic_csc_params ic_csc_rgb2ycbcr = { .scale = 1, }; -/* transparent RGB->RGB matrix for graphics combining */ -static const struct ic_csc_params ic_csc_rgb2rgb = { +/* + * identity matrix, used for transparent RGB->RGB graphics + * combining. + */ +static const struct ic_csc_params ic_csc_identity = { .coeff = { { 128, 0, 0 }, { 0, 128, 0 }, @@ -208,11 +213,13 @@ static const struct ic_csc_params ic_csc_rgb2rgb = { }; /* + * Inverse BT.601 encoding from YUV limited range to RGB full range: + * * R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */ This looks correct. -static const struct ic_csc_params ic_csc_ycbcr2rgb = { +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .coeff = { { 149, 0, 204 }, { 149, 462, 408 }, @@ -238,11 +245,11 @@ static int init_csc(struct ipu_ic *ic, (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) - params = _csc_ycbcr2rgb; + params = _csc_ycbcr2rgb_bt601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) - params = _csc_rgb2ycbcr; + params = _csc_rgb2ycbcr_bt601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) - params = _csc_rgb2rgb; + params = _csc_identity; else { dev_err(priv->ipu->dev, "Unsupported color space conversion\n"); return -EINVAL; regards Philipp ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v4 3/4] gpu: ipu-v3: ipu-ic: Add support for BT.709 encoding
On 2/11/19 2:12 AM, Philipp Zabel wrote: On Fri, 2019-02-08 at 17:47 -0800, Steve Longerbeam wrote: Pass v4l2 encoding enum to the ipu_ic task init functions, and add support for the BT.709 encoding and inverse encoding matrices. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- Changes in v4: - fix compile error. Chnges in v3: - none. Changes in v2: - only return "Unsupported YCbCr encoding" error if inf != outf, since if inf == outf, the identity matrix can be used. Reported by Tim Harvey. --- drivers/gpu/ipu-v3/ipu-ic.c | 71 +++-- drivers/gpu/ipu-v3/ipu-image-convert.c | 1 + drivers/staging/media/imx/imx-ic-prpencvf.c | 4 +- include/video/imx-ipu-v3.h | 5 +- 4 files changed, 71 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index e459615a49a1..c5f83d7e357f 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -212,6 +212,23 @@ static const struct ic_csc_params ic_csc_identity = { .scale = 2, }; +/* + * BT.709 encoding from RGB full range to YUV limited range: + * + * Y = R * .2126 + G * .7152 + B * .0722; + * U = R * -.1146 + G * -.3854 + B * .5000 + 128.; + * V = R * .5000 + G * -.4542 + B * -.0458 + 128.; This is a conversion to YUV full range. Limited range should be: YÂ Â Â R * .1826Â + G *Â .6142 + B *Â Â .0620 + 16; U = R * -.1007Â + G * -.3385 + B *Â Â .4392 + 128; VÂ Â Â R * .4392Â + G * -.3990 + B * -.0402 + 128; Yep, I fixed these to encode to limited range YUV, and ... + */ +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt709 = { + .coeff = { + { 54, 183, 18 }, + { 483, 413, 128 }, + { 128, 396, 500 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + /* * Inverse BT.601 encoding from YUV limited range to RGB full range: * @@ -229,12 +246,31 @@ static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .scale = 2, }; +/* + * Inverse BT.709 encoding from YUV limited range to RGB full range: + * + * R = (1. * (Y - 16)) + (1.5748 * (Cr - 128)); + * G = (1. * (Y - 16)) - (0.1873 * (Cb - 128)) - (0.4681 * (Cr - 128)); + * B = (1. * (Y - 16)) + (1.8556 * (Cb - 128); The coefficients look like full range again, conversion from limited range YUV should look like: R = (1.1644 * (Y - 16)) + (1.7927 * (Cr - 128)); G = (1.1644 * (Y - 16)) - (0.2132 * (Cb - 128)) - (0.5329 * (Cr - 128)); B = (1.1644 * (Y - 16)) + (2.1124 * (Cb - 128); fixed these to inverse encode from limited range YUV. + */ +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt709 = { + .coeff = { + { 128, 0, 202 }, + { 128, 488, 452 }, + { 128, 238, 0 }, + }, + .offset = { -435, 136, -507 }, + .scale = 2, +}; + static int init_csc(struct ipu_ic *ic, enum ipu_color_space inf, enum ipu_color_space outf, + enum v4l2_ycbcr_encoding encoding, Should we support YUV BT.601 <-> YUV REC.709 conversions? That would require separate encodings for input and output. How about if we pass the input and output encodings to the init ic task functions, but for now require they be the same? We can support transcoding in a later series. Also, this might be a good time to think about adding quantization range parameters as well. Again, I think for now, just include input/output quantization but require full range for RGB and limited range for YUV. But that really balloons the arguments to ipu_ic_task_init_*(). Should we create an ipu_ic_task_init structure? Steve ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v3 3/4] gpu: ipu-v3: ipu-ic: Add support for BT.709 encoding
On 2/8/19 4:20 PM, Tim Harvey wrote: On Fri, Feb 8, 2019 at 11:28 AM Steve Longerbeam wrote: if (inf == outf) params = _csc_identity; else if (inf == IPUV3_COLORSPACE_YUV) - params = _csc_ycbcr2rgb_bt601; + params = _csc_ycbcr2rgb; Steve, compile issue... params = params_yuv2rgb; else - params = _csc_rgb2ycbcr_bt601; + params = _csc_rgb2ycbcr; params = params_rgb2yuv; Wow, did I not even compile test that? Must be my head cold :-/ Sending v4. But, I'm still failing when using the mem2mem element (gst-launch-1.0 v4l2src device=/dev/video4 ! v4l2video8convert output-io-mode=dmabuf-import ! fbdevsink) with 'Unsupported YCbCr encoding' because of inf=IPU_COLORSPACE_YCBCR outf=IPU_COLORSPACE_RGB and a seemingly unset encoding being passed in. It looks like maybe something in the mem2mem driver isn't defaulting encoding. The call path is (v4l2_m2m_streamon -> device_run -> ipu_image_convert_queue -> convert_start -> ipu_ic_task_init_rsc -> init_csc). Looking at v7 of the mem2mem driver, it will set ycbcr_enc at the output side to V4L2_YCBCR_ENC_DEFAULT if colorspace is default. So colorspace will need to be set to something non-default in addition to setting ycbcr_enc, at the output side. I don't know whether gstreamer v4l2videoNconvertelement will do this, but you could hack the driver for now to get around it, and let Philipp know this may need a workaround in mem2mem for v8. Steve ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v4 2/4] gpu: ipu-v3: ipu-ic: Simplify selection of encoding matrix
Simplify the selection of the Y'CbCr encoding matrices in init_csc(). A side-effect of this change is that init_csc() now allows YUV->YUV using the identity matrix, intead of returning error. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-ic.c | 12 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 3ef61f0b509b..e459615a49a1 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -244,16 +244,12 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); - if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) + if (inf == outf) + params = _csc_identity; + else if (inf == IPUV3_COLORSPACE_YUV) params = _csc_ycbcr2rgb_bt601; - else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) + else params = _csc_rgb2ycbcr_bt601; - else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) - params = _csc_identity; - else { - dev_err(priv->ipu->dev, "Unsupported color space conversion\n"); - return -EINVAL; - } /* Cast to unsigned */ c = (const u16 (*)[3])params->coeff; -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 3/4] gpu: ipu-v3: ipu-ic: Add support for BT.709 encoding
Pass v4l2 encoding enum to the ipu_ic task init functions, and add support for the BT.709 encoding and inverse encoding matrices. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- Changes in v2: - only return "Unsupported YCbCr encoding" error if inf != outf, since if inf == outf, the identity matrix can be used. Reported by Tim Harvey. --- drivers/gpu/ipu-v3/ipu-ic.c | 71 +++-- drivers/gpu/ipu-v3/ipu-image-convert.c | 1 + drivers/staging/media/imx/imx-ic-prpencvf.c | 4 +- include/video/imx-ipu-v3.h | 5 +- 4 files changed, 71 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index e459615a49a1..0d57ca7ba18e 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -212,6 +212,23 @@ static const struct ic_csc_params ic_csc_identity = { .scale = 2, }; +/* + * BT.709 encoding from RGB full range to YUV limited range: + * + * Y = R * .2126 + G * .7152 + B * .0722; + * U = R * -.1146 + G * -.3854 + B * .5000 + 128.; + * V = R * .5000 + G * -.4542 + B * -.0458 + 128.; + */ +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt709 = { + .coeff = { + { 54, 183, 18 }, + { 483, 413, 128 }, + { 128, 396, 500 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + /* * Inverse BT.601 encoding from YUV limited range to RGB full range: * @@ -229,12 +246,31 @@ static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .scale = 2, }; +/* + * Inverse BT.709 encoding from YUV limited range to RGB full range: + * + * R = (1. * (Y - 16)) + (1.5748 * (Cr - 128)); + * G = (1. * (Y - 16)) - (0.1873 * (Cb - 128)) - (0.4681 * (Cr - 128)); + * B = (1. * (Y - 16)) + (1.8556 * (Cb - 128); + */ +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt709 = { + .coeff = { + { 128, 0, 202 }, + { 128, 488, 452 }, + { 128, 238, 0 }, + }, + .offset = { -435, 136, -507 }, + .scale = 2, +}; + static int init_csc(struct ipu_ic *ic, enum ipu_color_space inf, enum ipu_color_space outf, + enum v4l2_ycbcr_encoding encoding, int csc_index) { struct ipu_ic_priv *priv = ic->priv; + const struct ic_csc_params *params_rgb2yuv, *params_yuv2rgb; const struct ic_csc_params *params; u32 __iomem *base; const u16 (*c)[3]; @@ -244,12 +280,30 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); + switch (encoding) { + case V4L2_YCBCR_ENC_601: + params_rgb2yuv = _csc_rgb2ycbcr_bt601; + params_yuv2rgb = _csc_ycbcr2rgb_bt601; + break; + case V4L2_YCBCR_ENC_709: + params_rgb2yuv = _csc_rgb2ycbcr_bt709; + params_yuv2rgb = _csc_ycbcr2rgb_bt709; + break; + default: + if (inf != outf) { + dev_err(priv->ipu->dev, + "Unsupported YCbCr encoding\n"); + return -EINVAL; + } + break; + } + if (inf == outf) params = _csc_identity; else if (inf == IPUV3_COLORSPACE_YUV) - params = _csc_ycbcr2rgb_bt601; + params = _csc_ycbcr2rgb; else - params = _csc_rgb2ycbcr_bt601; + params = _csc_rgb2ycbcr; /* Cast to unsigned */ c = (const u16 (*)[3])params->coeff; @@ -390,6 +444,7 @@ EXPORT_SYMBOL_GPL(ipu_ic_task_disable); int ipu_ic_task_graphics_init(struct ipu_ic *ic, enum ipu_color_space in_g_cs, + enum v4l2_ycbcr_encoding encoding, bool galpha_en, u32 galpha, bool colorkey_en, u32 colorkey) { @@ -408,7 +463,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, if (!(ic_conf & ic->bit->ic_conf_csc1_en)) { /* need transparent CSC1 conversion */ ret = init_csc(ic, IPUV3_COLORSPACE_RGB, - IPUV3_COLORSPACE_RGB, 0); + IPUV3_COLORSPACE_RGB, encoding, 0); if (ret) goto unlock; } @@ -416,7 +471,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, ic->g_in_cs = in_g_cs; if (ic->g_in_cs != ic->out_cs) { - ret = init_csc(ic, ic->g_in_cs, ic->out_cs, 1); + ret = init_csc(ic, ic->g_in_cs, ic->out_cs, encoding, 1); if (ret) goto unlock; } @@ -450,6 +505,7 @@ int ipu_ic_task_init_rsc(struct ipu_ic *ic,
[PATCH v3 2/4] gpu: ipu-v3: ipu-ic: Simplify selection of encoding matrix
Simplify the selection of the Y'CbCr encoding matrices in init_csc(). A side-effect of this change is that init_csc() now allows YUV->YUV using the identity matrix, intead of returning error. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-ic.c | 12 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 3ef61f0b509b..e459615a49a1 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -244,16 +244,12 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); - if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) + if (inf == outf) + params = _csc_identity; + else if (inf == IPUV3_COLORSPACE_YUV) params = _csc_ycbcr2rgb_bt601; - else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) + else params = _csc_rgb2ycbcr_bt601; - else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) - params = _csc_identity; - else { - dev_err(priv->ipu->dev, "Unsupported color space conversion\n"); - return -EINVAL; - } /* Cast to unsigned */ c = (const u16 (*)[3])params->coeff; -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 3/4] gpu: ipu-v3: ipu-ic: Add support for BT.709 encoding
From: Steve Longerbeam Pass v4l2 encoding enum to the ipu_ic task init functions, and add support for the BT.709 encoding and inverse encoding matrices. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- Changes in v2: - only return "Unsupported YCbCr encoding" error if inf != outf, since if inf == outf, the identity matrix can be used. Reported by Tim Harvey. --- drivers/gpu/ipu-v3/ipu-ic.c | 71 +++-- drivers/gpu/ipu-v3/ipu-image-convert.c | 1 + drivers/staging/media/imx/imx-ic-prpencvf.c | 4 +- include/video/imx-ipu-v3.h | 5 +- 4 files changed, 71 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index e459615a49a1..0d57ca7ba18e 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -212,6 +212,23 @@ static const struct ic_csc_params ic_csc_identity = { .scale = 2, }; +/* + * BT.709 encoding from RGB full range to YUV limited range: + * + * Y = R * .2126 + G * .7152 + B * .0722; + * U = R * -.1146 + G * -.3854 + B * .5000 + 128.; + * V = R * .5000 + G * -.4542 + B * -.0458 + 128.; + */ +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt709 = { + .coeff = { + { 54, 183, 18 }, + { 483, 413, 128 }, + { 128, 396, 500 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + /* * Inverse BT.601 encoding from YUV limited range to RGB full range: * @@ -229,12 +246,31 @@ static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .scale = 2, }; +/* + * Inverse BT.709 encoding from YUV limited range to RGB full range: + * + * R = (1. * (Y - 16)) + (1.5748 * (Cr - 128)); + * G = (1. * (Y - 16)) - (0.1873 * (Cb - 128)) - (0.4681 * (Cr - 128)); + * B = (1. * (Y - 16)) + (1.8556 * (Cb - 128); + */ +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt709 = { + .coeff = { + { 128, 0, 202 }, + { 128, 488, 452 }, + { 128, 238, 0 }, + }, + .offset = { -435, 136, -507 }, + .scale = 2, +}; + static int init_csc(struct ipu_ic *ic, enum ipu_color_space inf, enum ipu_color_space outf, + enum v4l2_ycbcr_encoding encoding, int csc_index) { struct ipu_ic_priv *priv = ic->priv; + const struct ic_csc_params *params_rgb2yuv, *params_yuv2rgb; const struct ic_csc_params *params; u32 __iomem *base; const u16 (*c)[3]; @@ -244,12 +280,30 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); + switch (encoding) { + case V4L2_YCBCR_ENC_601: + params_rgb2yuv = _csc_rgb2ycbcr_bt601; + params_yuv2rgb = _csc_ycbcr2rgb_bt601; + break; + case V4L2_YCBCR_ENC_709: + params_rgb2yuv = _csc_rgb2ycbcr_bt709; + params_yuv2rgb = _csc_ycbcr2rgb_bt709; + break; + default: + if (inf != outf) { + dev_err(priv->ipu->dev, + "Unsupported YCbCr encoding\n"); + return -EINVAL; + } + break; + } + if (inf == outf) params = _csc_identity; else if (inf == IPUV3_COLORSPACE_YUV) - params = _csc_ycbcr2rgb_bt601; + params = _csc_ycbcr2rgb; else - params = _csc_rgb2ycbcr_bt601; + params = _csc_rgb2ycbcr; /* Cast to unsigned */ c = (const u16 (*)[3])params->coeff; @@ -390,6 +444,7 @@ EXPORT_SYMBOL_GPL(ipu_ic_task_disable); int ipu_ic_task_graphics_init(struct ipu_ic *ic, enum ipu_color_space in_g_cs, + enum v4l2_ycbcr_encoding encoding, bool galpha_en, u32 galpha, bool colorkey_en, u32 colorkey) { @@ -408,7 +463,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, if (!(ic_conf & ic->bit->ic_conf_csc1_en)) { /* need transparent CSC1 conversion */ ret = init_csc(ic, IPUV3_COLORSPACE_RGB, - IPUV3_COLORSPACE_RGB, 0); + IPUV3_COLORSPACE_RGB, encoding, 0); if (ret) goto unlock; } @@ -416,7 +471,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, ic->g_in_cs = in_g_cs; if (ic->g_in_cs != ic->out_cs) { - ret = init_csc(ic, ic->g_in_cs, ic->out_cs, 1); + ret = init_csc(ic, ic->g_in_cs, ic->out_cs, encoding, 1); if (ret) goto unlock; } @@ -450,6 +505,7 @@ int ipu_ic_task_init_rsc(s
[PATCH v2 1/4] gpu: ipu-v3: ipu-ic: Rename yuv2rgb encoding matrices
From: Steve Longerbeam The ycbcr2rgb and inverse rgb2ycbcr matrices define the BT.601 encoding coefficients, so rename them to indicate that. And add some comments to make clear these are BT.601 coefficients encoding between YUV limited range and RGB full range. The ic_csc_rgb2rgb matrix is just an identity matrix, so rename to ic_csc_identity. No functional changes. Signed-off-by: Steve Longerbeam --- Changes in v2: - rename ic_csc_rgb2rgb matrix to ic_csc_identity. --- drivers/gpu/ipu-v3/ipu-ic.c | 21 ++--- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 594c3cbc8291..3ef61f0b509b 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -183,11 +183,13 @@ struct ic_csc_params { }; /* + * BT.601 encoding from RGB full range to YUV limited range: + * * Y = R * .299 + G * .587 + B * .114; * U = R * -.169 + G * -.332 + B * .500 + 128.; * V = R * .500 + G * -.419 + B * -.0813 + 128.; */ -static const struct ic_csc_params ic_csc_rgb2ycbcr = { +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt601 = { .coeff = { { 77, 150, 29 }, { 469, 427, 128 }, @@ -197,8 +199,11 @@ static const struct ic_csc_params ic_csc_rgb2ycbcr = { .scale = 1, }; -/* transparent RGB->RGB matrix for graphics combining */ -static const struct ic_csc_params ic_csc_rgb2rgb = { +/* + * identity matrix, used for transparent RGB->RGB graphics + * combining. + */ +static const struct ic_csc_params ic_csc_identity = { .coeff = { { 128, 0, 0 }, { 0, 128, 0 }, @@ -208,11 +213,13 @@ static const struct ic_csc_params ic_csc_rgb2rgb = { }; /* + * Inverse BT.601 encoding from YUV limited range to RGB full range: + * * R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */ -static const struct ic_csc_params ic_csc_ycbcr2rgb = { +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .coeff = { { 149, 0, 204 }, { 149, 462, 408 }, @@ -238,11 +245,11 @@ static int init_csc(struct ipu_ic *ic, (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) - params = _csc_ycbcr2rgb; + params = _csc_ycbcr2rgb_bt601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) - params = _csc_rgb2ycbcr; + params = _csc_rgb2ycbcr_bt601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) - params = _csc_rgb2rgb; + params = _csc_identity; else { dev_err(priv->ipu->dev, "Unsupported color space conversion\n"); return -EINVAL; -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v4 1/4] gpu: ipu-v3: ipu-ic: Rename yuv2rgb encoding matrices
The ycbcr2rgb and inverse rgb2ycbcr matrices define the BT.601 encoding coefficients, so rename them to indicate that. And add some comments to make clear these are BT.601 coefficients encoding between YUV limited range and RGB full range. The ic_csc_rgb2rgb matrix is just an identity matrix, so rename to ic_csc_identity. No functional changes. Signed-off-by: Steve Longerbeam --- Changes in v2: - rename ic_csc_rgb2rgb matrix to ic_csc_identity. --- drivers/gpu/ipu-v3/ipu-ic.c | 21 ++--- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 594c3cbc8291..3ef61f0b509b 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -183,11 +183,13 @@ struct ic_csc_params { }; /* + * BT.601 encoding from RGB full range to YUV limited range: + * * Y = R * .299 + G * .587 + B * .114; * U = R * -.169 + G * -.332 + B * .500 + 128.; * V = R * .500 + G * -.419 + B * -.0813 + 128.; */ -static const struct ic_csc_params ic_csc_rgb2ycbcr = { +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt601 = { .coeff = { { 77, 150, 29 }, { 469, 427, 128 }, @@ -197,8 +199,11 @@ static const struct ic_csc_params ic_csc_rgb2ycbcr = { .scale = 1, }; -/* transparent RGB->RGB matrix for graphics combining */ -static const struct ic_csc_params ic_csc_rgb2rgb = { +/* + * identity matrix, used for transparent RGB->RGB graphics + * combining. + */ +static const struct ic_csc_params ic_csc_identity = { .coeff = { { 128, 0, 0 }, { 0, 128, 0 }, @@ -208,11 +213,13 @@ static const struct ic_csc_params ic_csc_rgb2rgb = { }; /* + * Inverse BT.601 encoding from YUV limited range to RGB full range: + * * R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */ -static const struct ic_csc_params ic_csc_ycbcr2rgb = { +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .coeff = { { 149, 0, 204 }, { 149, 462, 408 }, @@ -238,11 +245,11 @@ static int init_csc(struct ipu_ic *ic, (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) - params = _csc_ycbcr2rgb; + params = _csc_ycbcr2rgb_bt601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) - params = _csc_rgb2ycbcr; + params = _csc_rgb2ycbcr_bt601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) - params = _csc_rgb2rgb; + params = _csc_identity; else { dev_err(priv->ipu->dev, "Unsupported color space conversion\n"); return -EINVAL; -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v4 3/4] gpu: ipu-v3: ipu-ic: Add support for BT.709 encoding
Pass v4l2 encoding enum to the ipu_ic task init functions, and add support for the BT.709 encoding and inverse encoding matrices. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- Changes in v4: - fix compile error. Chnges in v3: - none. Changes in v2: - only return "Unsupported YCbCr encoding" error if inf != outf, since if inf == outf, the identity matrix can be used. Reported by Tim Harvey. --- drivers/gpu/ipu-v3/ipu-ic.c | 71 +++-- drivers/gpu/ipu-v3/ipu-image-convert.c | 1 + drivers/staging/media/imx/imx-ic-prpencvf.c | 4 +- include/video/imx-ipu-v3.h | 5 +- 4 files changed, 71 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index e459615a49a1..c5f83d7e357f 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -212,6 +212,23 @@ static const struct ic_csc_params ic_csc_identity = { .scale = 2, }; +/* + * BT.709 encoding from RGB full range to YUV limited range: + * + * Y = R * .2126 + G * .7152 + B * .0722; + * U = R * -.1146 + G * -.3854 + B * .5000 + 128.; + * V = R * .5000 + G * -.4542 + B * -.0458 + 128.; + */ +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt709 = { + .coeff = { + { 54, 183, 18 }, + { 483, 413, 128 }, + { 128, 396, 500 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + /* * Inverse BT.601 encoding from YUV limited range to RGB full range: * @@ -229,12 +246,31 @@ static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .scale = 2, }; +/* + * Inverse BT.709 encoding from YUV limited range to RGB full range: + * + * R = (1. * (Y - 16)) + (1.5748 * (Cr - 128)); + * G = (1. * (Y - 16)) - (0.1873 * (Cb - 128)) - (0.4681 * (Cr - 128)); + * B = (1. * (Y - 16)) + (1.8556 * (Cb - 128); + */ +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt709 = { + .coeff = { + { 128, 0, 202 }, + { 128, 488, 452 }, + { 128, 238, 0 }, + }, + .offset = { -435, 136, -507 }, + .scale = 2, +}; + static int init_csc(struct ipu_ic *ic, enum ipu_color_space inf, enum ipu_color_space outf, + enum v4l2_ycbcr_encoding encoding, int csc_index) { struct ipu_ic_priv *priv = ic->priv; + const struct ic_csc_params *params_rgb2yuv, *params_yuv2rgb; const struct ic_csc_params *params; u32 __iomem *base; const u16 (*c)[3]; @@ -244,12 +280,30 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); + switch (encoding) { + case V4L2_YCBCR_ENC_601: + params_rgb2yuv = _csc_rgb2ycbcr_bt601; + params_yuv2rgb = _csc_ycbcr2rgb_bt601; + break; + case V4L2_YCBCR_ENC_709: + params_rgb2yuv = _csc_rgb2ycbcr_bt709; + params_yuv2rgb = _csc_ycbcr2rgb_bt709; + break; + default: + if (inf != outf) { + dev_err(priv->ipu->dev, + "Unsupported YCbCr encoding\n"); + return -EINVAL; + } + break; + } + if (inf == outf) params = _csc_identity; else if (inf == IPUV3_COLORSPACE_YUV) - params = _csc_ycbcr2rgb_bt601; + params = params_yuv2rgb; else - params = _csc_rgb2ycbcr_bt601; + params = params_rgb2yuv; /* Cast to unsigned */ c = (const u16 (*)[3])params->coeff; @@ -390,6 +444,7 @@ EXPORT_SYMBOL_GPL(ipu_ic_task_disable); int ipu_ic_task_graphics_init(struct ipu_ic *ic, enum ipu_color_space in_g_cs, + enum v4l2_ycbcr_encoding encoding, bool galpha_en, u32 galpha, bool colorkey_en, u32 colorkey) { @@ -408,7 +463,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, if (!(ic_conf & ic->bit->ic_conf_csc1_en)) { /* need transparent CSC1 conversion */ ret = init_csc(ic, IPUV3_COLORSPACE_RGB, - IPUV3_COLORSPACE_RGB, 0); + IPUV3_COLORSPACE_RGB, encoding, 0); if (ret) goto unlock; } @@ -416,7 +471,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, ic->g_in_cs = in_g_cs; if (ic->g_in_cs != ic->out_cs) { - ret = init_csc(ic, ic->g_in_cs, ic->out_cs, 1); + ret = init_csc(ic, ic->g_in_cs, ic->out_cs, encoding, 1); if (ret) goto unlock; } @@ -450,6 +505,7 @@
Re: [PATCH 2/3] gpu: ipu-v3: ipu-ic: Add support for BT.709 encoding
On 2/8/19 8:24 AM, Tim Harvey wrote: On Sun, Feb 3, 2019 at 11:48 AM Steve Longerbeam wrote: Pass v4l2 encoding enum to the ipu_ic task init functions, and add support for the BT.709 encoding and inverse encoding matrices. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-ic.c | 67 ++--- drivers/gpu/ipu-v3/ipu-image-convert.c | 1 + drivers/staging/media/imx/imx-ic-prpencvf.c | 4 +- include/video/imx-ipu-v3.h | 5 +- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 35ae86ff0585..63362b4fff81 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -199,6 +199,23 @@ static const struct ic_csc_params ic_csc_rgb2ycbcr_bt601 = { .scale = 1, }; +/* + * BT.709 encoding from RGB full range to YUV limited range: + * + * Y = R * .2126 + G * .7152 + B * .0722; + * U = R * -.1146 + G * -.3854 + B * .5000 + 128.; + * V = R * .5000 + G * -.4542 + B * -.0458 + 128.; + */ +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt709 = { + .coeff = { + { 54, 183, 18 }, + { 483, 413, 128 }, + { 128, 396, 500 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + /* transparent RGB->RGB matrix for graphics combining */ static const struct ic_csc_params ic_csc_rgb2rgb = { .coeff = { @@ -226,12 +243,31 @@ static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .scale = 2, }; +/* + * Inverse BT.709 encoding from YUV limited range to RGB full range: + * + * R = (1. * (Y - 16)) + (1.5748 * (Cr - 128)); + * G = (1. * (Y - 16)) - (0.1873 * (Cb - 128)) - (0.4681 * (Cr - 128)); + * B = (1. * (Y - 16)) + (1.8556 * (Cb - 128); + */ +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt709 = { + .coeff = { + { 128, 0, 202 }, + { 128, 488, 452 }, + { 128, 238, 0 }, + }, + .offset = { -435, 136, -507 }, + .scale = 2, +}; + static int init_csc(struct ipu_ic *ic, enum ipu_color_space inf, enum ipu_color_space outf, + enum v4l2_ycbcr_encoding encoding, int csc_index) { struct ipu_ic_priv *priv = ic->priv; + const struct ic_csc_params *params_rgb2yuv, *params_yuv2rgb; const struct ic_csc_params *params; u32 __iomem *base; const u16 (*c)[3]; @@ -241,10 +277,24 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); + switch (encoding) { + case V4L2_YCBCR_ENC_601: + params_rgb2yuv = _csc_rgb2ycbcr_bt601; + params_yuv2rgb = _csc_ycbcr2rgb_bt601; + break; + case V4L2_YCBCR_ENC_709: + params_rgb2yuv = _csc_rgb2ycbcr_bt709; + params_yuv2rgb = _csc_ycbcr2rgb_bt709; + break; + default: + dev_err(priv->ipu->dev, "Unsupported YCbCr encoding\n"); + return -EINVAL; + } + Steve, This will fail for RGB to RGB with 'Unsupported YCbCr encoding. We need to account for the RGB->RGB case. How about something like: Thanks for reporting Tim I rather keep the check for supported encoding, and instead get rid of "Unsupported color space conversion" error, because that is the YUV->YUV case which can be allowed using the identity matrix. Steve static int init_csc(struct ipu_ic *ic, enum ipu_color_space inf, enum ipu_color_space outf, + enum v4l2_ycbcr_encoding encoding, int csc_index) { struct ipu_ic_priv *priv = ic->priv; - const struct ic_csc_params *params; + const struct ic_csc_params *params = NULL; u32 __iomem *base; const u16 (*c)[3]; const u16 *a; @@ -241,13 +276,18 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); - if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) - params = _csc_ycbcr2rgb_bt601; - else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) - params = _csc_rgb2ycbcr_bt601; + if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) { + params = (encoding == V4L2_YCBCR_ENC_601) ? + _csc_ycbcr2rgb_bt601 : _csc_ycbcr2rgb_bt709; + } + else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) { + params = (encoding == V4L2_YCBCR_ENC_601) ? + _csc_rgb2ycbcr_bt601 : _csc_rgb2ycbcr_bt709; + } el
[PATCH v3 1/4] gpu: ipu-v3: ipu-ic: Rename yuv2rgb encoding matrices
The ycbcr2rgb and inverse rgb2ycbcr matrices define the BT.601 encoding coefficients, so rename them to indicate that. And add some comments to make clear these are BT.601 coefficients encoding between YUV limited range and RGB full range. The ic_csc_rgb2rgb matrix is just an identity matrix, so rename to ic_csc_identity. No functional changes. Signed-off-by: Steve Longerbeam --- Changes in v2: - rename ic_csc_rgb2rgb matrix to ic_csc_identity. --- drivers/gpu/ipu-v3/ipu-ic.c | 21 ++--- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 594c3cbc8291..3ef61f0b509b 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -183,11 +183,13 @@ struct ic_csc_params { }; /* + * BT.601 encoding from RGB full range to YUV limited range: + * * Y = R * .299 + G * .587 + B * .114; * U = R * -.169 + G * -.332 + B * .500 + 128.; * V = R * .500 + G * -.419 + B * -.0813 + 128.; */ -static const struct ic_csc_params ic_csc_rgb2ycbcr = { +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt601 = { .coeff = { { 77, 150, 29 }, { 469, 427, 128 }, @@ -197,8 +199,11 @@ static const struct ic_csc_params ic_csc_rgb2ycbcr = { .scale = 1, }; -/* transparent RGB->RGB matrix for graphics combining */ -static const struct ic_csc_params ic_csc_rgb2rgb = { +/* + * identity matrix, used for transparent RGB->RGB graphics + * combining. + */ +static const struct ic_csc_params ic_csc_identity = { .coeff = { { 128, 0, 0 }, { 0, 128, 0 }, @@ -208,11 +213,13 @@ static const struct ic_csc_params ic_csc_rgb2rgb = { }; /* + * Inverse BT.601 encoding from YUV limited range to RGB full range: + * * R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */ -static const struct ic_csc_params ic_csc_ycbcr2rgb = { +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .coeff = { { 149, 0, 204 }, { 149, 462, 408 }, @@ -238,11 +245,11 @@ static int init_csc(struct ipu_ic *ic, (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) - params = _csc_ycbcr2rgb; + params = _csc_ycbcr2rgb_bt601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) - params = _csc_rgb2ycbcr; + params = _csc_rgb2ycbcr_bt601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) - params = _csc_rgb2rgb; + params = _csc_identity; else { dev_err(priv->ipu->dev, "Unsupported color space conversion\n"); return -EINVAL; -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 2/4] gpu: ipu-v3: ipu-ic: Simplify selection of encoding matrix
Simplify the selection of the Y'CbCr encoding matrices in init_csc(). A side-effect of this change is that init_csc() now allows YUV->YUV using the identity matrix, intead of returning error. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-ic.c | 12 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 3ef61f0b509b..e459615a49a1 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -244,16 +244,12 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); - if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) + if (inf == outf) + params = _csc_identity; + else if (inf == IPUV3_COLORSPACE_YUV) params = _csc_ycbcr2rgb_bt601; - else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) + else params = _csc_rgb2ycbcr_bt601; - else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) - params = _csc_identity; - else { - dev_err(priv->ipu->dev, "Unsupported color space conversion\n"); - return -EINVAL; - } /* Cast to unsigned */ c = (const u16 (*)[3])params->coeff; -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 1/3] gpu: ipu-v3: ipu-ic: Rename yuv2rgb encoding matrices
The ycbcr2rgb and inverse rgb2ycbcr matrices define the BT.601 encoding coefficients, so rename them to indicate that. And add some comments to make clear these are BT.601 coefficients encoding between YUV limited range and RGB full range. No functional changes. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-ic.c | 12 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 594c3cbc8291..35ae86ff0585 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -183,11 +183,13 @@ struct ic_csc_params { }; /* + * BT.601 encoding from RGB full range to YUV limited range: + * * Y = R * .299 + G * .587 + B * .114; * U = R * -.169 + G * -.332 + B * .500 + 128.; * V = R * .500 + G * -.419 + B * -.0813 + 128.; */ -static const struct ic_csc_params ic_csc_rgb2ycbcr = { +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt601 = { .coeff = { { 77, 150, 29 }, { 469, 427, 128 }, @@ -208,11 +210,13 @@ static const struct ic_csc_params ic_csc_rgb2rgb = { }; /* + * Inverse BT.601 encoding from YUV limited range to RGB full range: + * * R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */ -static const struct ic_csc_params ic_csc_ycbcr2rgb = { +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .coeff = { { 149, 0, 204 }, { 149, 462, 408 }, @@ -238,9 +242,9 @@ static int init_csc(struct ipu_ic *ic, (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) - params = _csc_ycbcr2rgb; + params = _csc_ycbcr2rgb_bt601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) - params = _csc_rgb2ycbcr; + params = _csc_rgb2ycbcr_bt601; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) params = _csc_rgb2rgb; else { -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 2/3] gpu: ipu-v3: ipu-ic: Add support for BT.709 encoding
Pass v4l2 encoding enum to the ipu_ic task init functions, and add support for the BT.709 encoding and inverse encoding matrices. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-ic.c | 67 ++--- drivers/gpu/ipu-v3/ipu-image-convert.c | 1 + drivers/staging/media/imx/imx-ic-prpencvf.c | 4 +- include/video/imx-ipu-v3.h | 5 +- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 35ae86ff0585..63362b4fff81 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -199,6 +199,23 @@ static const struct ic_csc_params ic_csc_rgb2ycbcr_bt601 = { .scale = 1, }; +/* + * BT.709 encoding from RGB full range to YUV limited range: + * + * Y = R * .2126 + G * .7152 + B * .0722; + * U = R * -.1146 + G * -.3854 + B * .5000 + 128.; + * V = R * .5000 + G * -.4542 + B * -.0458 + 128.; + */ +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt709 = { + .coeff = { + { 54, 183, 18 }, + { 483, 413, 128 }, + { 128, 396, 500 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + /* transparent RGB->RGB matrix for graphics combining */ static const struct ic_csc_params ic_csc_rgb2rgb = { .coeff = { @@ -226,12 +243,31 @@ static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .scale = 2, }; +/* + * Inverse BT.709 encoding from YUV limited range to RGB full range: + * + * R = (1. * (Y - 16)) + (1.5748 * (Cr - 128)); + * G = (1. * (Y - 16)) - (0.1873 * (Cb - 128)) - (0.4681 * (Cr - 128)); + * B = (1. * (Y - 16)) + (1.8556 * (Cb - 128); + */ +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt709 = { + .coeff = { + { 128, 0, 202 }, + { 128, 488, 452 }, + { 128, 238, 0 }, + }, + .offset = { -435, 136, -507 }, + .scale = 2, +}; + static int init_csc(struct ipu_ic *ic, enum ipu_color_space inf, enum ipu_color_space outf, + enum v4l2_ycbcr_encoding encoding, int csc_index) { struct ipu_ic_priv *priv = ic->priv; + const struct ic_csc_params *params_rgb2yuv, *params_yuv2rgb; const struct ic_csc_params *params; u32 __iomem *base; const u16 (*c)[3]; @@ -241,10 +277,24 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); + switch (encoding) { + case V4L2_YCBCR_ENC_601: + params_rgb2yuv = _csc_rgb2ycbcr_bt601; + params_yuv2rgb = _csc_ycbcr2rgb_bt601; + break; + case V4L2_YCBCR_ENC_709: + params_rgb2yuv = _csc_rgb2ycbcr_bt709; + params_yuv2rgb = _csc_ycbcr2rgb_bt709; + break; + default: + dev_err(priv->ipu->dev, "Unsupported YCbCr encoding\n"); + return -EINVAL; + } + if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) - params = _csc_ycbcr2rgb_bt601; + params = params_yuv2rgb; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) - params = _csc_rgb2ycbcr_bt601; + params = params_rgb2yuv; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) params = _csc_rgb2rgb; else { @@ -391,6 +441,7 @@ EXPORT_SYMBOL_GPL(ipu_ic_task_disable); int ipu_ic_task_graphics_init(struct ipu_ic *ic, enum ipu_color_space in_g_cs, + enum v4l2_ycbcr_encoding encoding, bool galpha_en, u32 galpha, bool colorkey_en, u32 colorkey) { @@ -409,7 +460,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, if (!(ic_conf & ic->bit->ic_conf_csc1_en)) { /* need transparent CSC1 conversion */ ret = init_csc(ic, IPUV3_COLORSPACE_RGB, - IPUV3_COLORSPACE_RGB, 0); + IPUV3_COLORSPACE_RGB, encoding, 0); if (ret) goto unlock; } @@ -417,7 +468,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, ic->g_in_cs = in_g_cs; if (ic->g_in_cs != ic->out_cs) { - ret = init_csc(ic, ic->g_in_cs, ic->out_cs, 1); + ret = init_csc(ic, ic->g_in_cs, ic->out_cs, encoding, 1); if (ret) goto unlock; } @@ -451,6 +502,7 @@ int ipu_ic_task_init_rsc(struct ipu_ic *ic, int out_width, int out_height, enum ipu_color_space in_cs, e
[PATCH v8 03/11] gpu: ipu-v3: Add planar support to interlaced scan
To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Acked-by: Philipp Zabel --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 +++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index 163fadb8a33a..d047a6867c59 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -277,9 +277,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -290,9 +291,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41caba05d..af7224846bd5 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -412,7 +412,8 @@ static int prp_setup_channel(struct prp_priv *priv, if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field) && channel == priv->out_ch) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline); + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index c2a8d9cd31b7..da4808348845 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -512,7 +512,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field)) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index bbc8481f567d..c887f4bee5f8 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -258,7 +258,8 @@ void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride); void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch); void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf); void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride); +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat); void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id); int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch); void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize); -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v7 03/11] gpu: ipu-v3: Add planar support to interlaced scan
To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Acked-by: Philipp Zabel --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 +++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index 163fadb8a33a..d047a6867c59 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -277,9 +277,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -290,9 +291,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41caba05d..af7224846bd5 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -412,7 +412,8 @@ static int prp_setup_channel(struct prp_priv *priv, if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field) && channel == priv->out_ch) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline); + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index c2a8d9cd31b7..da4808348845 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -512,7 +512,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field)) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index bbc8481f567d..c887f4bee5f8 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -258,7 +258,8 @@ void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride); void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch); void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf); void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride); +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat); void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id); int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch); void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize); -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v7 02/11] gpu: ipu-csi: Swap fields according to input/output field types
The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- Changes since v5: - Convert to const the infmt, outfmt, and mbus_cfg pointer args to ipu_csi_init_interface(), suggested by Philipp Zabel. - Bring back if_fmt local var and don't copy outfmt to local stack in csi_setup(), suggested by Philipp. Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 126 +++--- drivers/staging/media/imx/imx-media-csi.c | 7 +- include/video/imx-ipu-v3.h| 5 +- 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..d1e575571a8d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,12 +325,21 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -struct v4l2_mbus_config *mbus_cfg, -struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *mbus_fmt) { int ret; @@ -374,22 +383,76 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int +ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, - struct v4l2_mbus_config *mbus_cfg, -
[PATCH v8 02/11] gpu: ipu-csi: Swap fields according to input/output field types
The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Acked-by: Philipp Zabel --- Changes since v5: - Convert to const the infmt, outfmt, and mbus_cfg pointer args to ipu_csi_init_interface(), suggested by Philipp Zabel. - Bring back if_fmt local var and don't copy outfmt to local stack in csi_setup(), suggested by Philipp. Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 126 +++--- drivers/staging/media/imx/imx-media-csi.c | 7 +- include/video/imx-ipu-v3.h| 5 +- 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..d1e575571a8d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,12 +325,21 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -struct v4l2_mbus_config *mbus_cfg, -struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *mbus_fmt) { int ret; @@ -374,22 +383,76 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int +ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, - struct v4l2_mbus_config *m
[PATCH v6 02/12] gpu: ipu-csi: Swap fields according to input/output field types
The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- Changes since v5: - Convert to const the infmt, outfmt, and mbus_cfg pointer args to ipu_csi_init_interface(), suggested by Philipp Zabel. - Bring back if_fmt local var and don't copy outfmt to local stack in csi_setup(), suggested by Philipp. Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 126 +++--- drivers/staging/media/imx/imx-media-csi.c | 7 +- include/video/imx-ipu-v3.h| 5 +- 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..d1e575571a8d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,12 +325,21 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -struct v4l2_mbus_config *mbus_cfg, -struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *mbus_fmt) { int ret; @@ -374,22 +383,76 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int +ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, - struct v4l2_mbus_config *mbus_cfg, -
[PATCH v6 03/12] gpu: ipu-v3: Add planar support to interlaced scan
To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Acked-by: Philipp Zabel --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 +++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index 163fadb8a33a..d047a6867c59 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -277,9 +277,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -290,9 +291,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41caba05d..af7224846bd5 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -412,7 +412,8 @@ static int prp_setup_channel(struct prp_priv *priv, if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field) && channel == priv->out_ch) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline); + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index c2a8d9cd31b7..da4808348845 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -512,7 +512,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field)) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index bbc8481f567d..c887f4bee5f8 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -258,7 +258,8 @@ void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride); void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch); void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf); void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride); +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat); void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id); int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch); void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize); -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v6] gpu: ipu-csi: Swap fields according to input/output field types
Please disregard. This patch can't be submitted stand-alone, I will re-submit as part of a v6 of "imx-media: Fixes for interlaced capture" patchset. Steve On 12/14/18 3:46 PM, Steve Longerbeam wrote: The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- Changes since v5: - Convert to const the infmt, outfmt, and mbus_cfg pointer args to ipu_csi_init_interface(), suggested by Philipp Zabel. - Bring back if_fmt local var and don't copy outfmt to local stack in csi_setup(), suggested by Philipp. Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 126 +++--- drivers/staging/media/imx/imx-media-csi.c | 7 +- include/video/imx-ipu-v3.h| 5 +- 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..d1e575571a8d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,12 +325,21 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -struct v4l2_mbus_config *mbus_cfg, -struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *mbus_fmt) { int ret; @@ -374,22 +383,76 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int +ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE
Re: [PATCH v5 02/12] gpu: ipu-csi: Swap fields according to input/output field types
On 12/13/18 4:59 AM, Philipp Zabel wrote: Hi Steve, On Tue, 2018-10-16 at 17:00 -0700, Steve Longerbeam wrote: The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam --- Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp Zabel. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 119 +++--- drivers/staging/media/imx/imx-media-csi.c | 17 +--- include/video/imx-ipu-v3.h| 3 +- 3 files changed, 88 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..4a15e513fa05 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,6 +325,15 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ @@ -374,22 +383,75 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt, infmt and outfmt parameters could be const. Agreed, I will convert these pointer args to const. And since we are changing the API to ipu_csi_init_interface() anyway, I went ahead and converted the mbus_cfg, infmt, and outfmt pointer args to const there as well. + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt) + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt) { struct ipu_csi_bus_config cfg; unsi
[PATCH v6] gpu: ipu-csi: Swap fields according to input/output field types
The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- Changes since v5: - Convert to const the infmt, outfmt, and mbus_cfg pointer args to ipu_csi_init_interface(), suggested by Philipp Zabel. - Bring back if_fmt local var and don't copy outfmt to local stack in csi_setup(), suggested by Philipp. Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 126 +++--- drivers/staging/media/imx/imx-media-csi.c | 7 +- include/video/imx-ipu-v3.h| 5 +- 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..d1e575571a8d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,12 +325,21 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -struct v4l2_mbus_config *mbus_cfg, -struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *mbus_fmt) { int ret; @@ -374,22 +383,76 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int +ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, - struct v4l2_mbus_config *mbus_cfg, -
Re: [PATCH v5 02/12] gpu: ipu-csi: Swap fields according to input/output field types
Hi Philipp, can you review this patch and give it your ack? Thanks, Steve On 10/16/18 5:00 PM, Steve Longerbeam wrote: The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam --- Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp Zabel. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 119 +++--- drivers/staging/media/imx/imx-media-csi.c | 17 +--- include/video/imx-ipu-v3.h| 3 +- 3 files changed, 88 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..4a15e513fa05 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,6 +325,15 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ @@ -374,22 +383,75 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt) + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt) { struct ipu_csi_bus_config cfg; unsigned long flags; u32 width, height, data = 0; + v4l2_std_id std; int ret; - ret = fill_csi_bus_cfg(, mbus_cfg, mbus_fmt); + ret = fill_csi_bus_cfg(, mbus_cfg, infmt); if (ret < 0) return ret;
[PATCH v5 03/12] gpu: ipu-v3: Add planar support to interlaced scan
To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Acked-by: Philipp Zabel --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 +++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index a9d2501500a1..d41df8034c5b 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -273,9 +273,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -286,9 +287,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41caba05d..af7224846bd5 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -412,7 +412,8 @@ static int prp_setup_channel(struct prp_priv *priv, if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field) && channel == priv->out_ch) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline); + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 7ecbd4d76d09..4aa20ae72608 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -512,7 +512,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field)) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index f44a35192313..e888c66b9d9d 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -255,7 +255,8 @@ void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride); void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch); void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf); void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride); +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat); void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id); int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch); void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize); -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH] gpu: ipu-v3: Fix CSI offsets for imx53
The CSI offsets are wrong for both CSI0 and CSI1. They are at physical address 0x1e03 and 0x1e038000 respectively. Fixes: 2ffd48f2e7 ("gpu: ipu-v3: Add Camera Sensor Interface unit") Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index 48685cddbad1..f487f25ed577 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -910,8 +910,8 @@ static struct ipu_devtype ipu_type_imx53 = { .cpmem_ofs = 0x0700, .srm_ofs = 0x0704, .tpm_ofs = 0x0706, - .csi0_ofs = 0x0703, - .csi1_ofs = 0x07038000, + .csi0_ofs = 0x0603, + .csi1_ofs = 0x06038000, .ic_ofs = 0x0602, .disp0_ofs = 0x0604, .disp1_ofs = 0x06048000, -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v5 02/12] gpu: ipu-csi: Swap fields according to input/output field types
The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam --- Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp Zabel. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 119 +++--- drivers/staging/media/imx/imx-media-csi.c | 17 +--- include/video/imx-ipu-v3.h| 3 +- 3 files changed, 88 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..4a15e513fa05 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,6 +325,15 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ @@ -374,22 +383,75 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt) + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt) { struct ipu_csi_bus_config cfg; unsigned long flags; u32 width, height, data = 0; + v4l2_std_id std; int ret; - ret = fill_csi_bus_cfg(, mbus_cfg, mbus_fmt); + ret = fill_csi_bus_cfg(, mbus_cfg, infmt); if (ret < 0) return ret; /* set default sensor frame width and height */ - width = mbus_fmt->width; - height = mbus_fmt->height; + w
Re: [PATCH v4 02/11] gpu: ipu-csi: Swap fields according to input/output field types
Hi Philipp, On 10/05/2018 02:44 AM, Philipp Zabel wrote: Hi Steve, On Thu, 2018-10-04 at 11:53 -0700, Steve Longerbeam wrote: + + /* framelines for NTSC / PAL */ + height = (std & V4L2_STD_525_60) ? 525 : 625; I think this is a bit convoluted. Instead of initializing std, then possibly changing it, and then comparing to the inital value, and then checking it again to determine the new height, why not just: if (width == 720 && height == 480) { std = V4L2_STD_NTSC; height = 525; } else if (width == 720 && height == 576) { std = V4L2_STD_PAL; height = 625; } else { dev_err(csi->ipu->dev, "Unsupported interlaced video mode\n"); ret = -EINVAL; goto out_unlock; } ? Yes that was a bit convoluted, fixed. /* * if cycles is set, we need to handle this over multiple cycles as * generic/bayer data */ - if (is_parallel_bus(>upstream_ep) && incc->cycles) { - if_fmt.width *= incc->cycles; If the input format width passed to ipu_csi_init_interface is not multiplied by the number of cycles per pixel anymore, width in the CSI_SENS_FRM_SIZE register will be set to the unmultiplied value from infmt. This breaks 779680e2e793 ("media: imx: add support for RGB565_2X8 on parallel bus"). Oops, that was a mistake, thanks for catching, fixed. Steve ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v4 03/11] gpu: ipu-v3: Add planar support to interlaced scan
On 10/05/2018 02:48 AM, Philipp Zabel wrote: On Thu, 2018-10-04 at 11:53 -0700, Steve Longerbeam wrote: To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 +++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index a9d2501500a1..d41df8034c5b 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -273,9 +273,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -286,9 +287,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); [...] Reviewed-by: Philipp Zabel and Acked-by: Philipp Zabel to be merged with the rest of the series via the media tree. I'll take care not to introduce nontrivial conflicts in imx-drm. Ok thanks. Hans, for v5 I will just include the two IPU patches as before. As Philipp stated, he is OK with merging them to the media tree (after his ack of course), along with the rest of the patches in this series. Steve ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v4 03/11] gpu: ipu-v3: Add planar support to interlaced scan
To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 +++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index a9d2501500a1..d41df8034c5b 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -273,9 +273,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -286,9 +287,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41caba05d..af7224846bd5 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -412,7 +412,8 @@ static int prp_setup_channel(struct prp_priv *priv, if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field) && channel == priv->out_ch) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline); + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index ad66f007d395..5e3aa4f3a1dd 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -512,7 +512,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field)) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index f44a35192313..e888c66b9d9d 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -255,7 +255,8 @@ void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride); void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch); void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf); void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride); +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat); void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id); int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch); void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize); -- 2.17.1 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v4 02/11] gpu: ipu-csi: Swap fields according to input/output field types
The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-csi.c | 132 +++--- drivers/staging/media/imx/imx-media-csi.c | 13 +-- include/video/imx-ipu-v3.h| 3 +- 3 files changed, 97 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index 954eefe144e2..759fcd724ff9 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,6 +325,15 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ @@ -374,22 +383,75 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt) + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt) { struct ipu_csi_bus_config cfg; unsigned long flags; u32 width, height, data = 0; + v4l2_std_id std; int ret; - ret = fill_csi_bus_cfg(, mbus_cfg, mbus_fmt); + ret = fill_csi_bus_cfg(, mbus_cfg, infmt); if (ret < 0) return ret; /* set default sensor frame width and height */ - width = mbus_fmt->width; - height = mbus_fmt->height; + width = infmt->width; + height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + height *= 2; /* Set the CSI_SENS_CONF
Re: [PATCH 4.19 regression fix 2/2] staging: vboxvideo: Change address of scanout buffer on page-flip
Hi Hans, On 09/10/2018 11:30 AM, Hans de Goede wrote: Commit 2408898e3b6c ("staging: vboxvideo: Add page-flip support") only calls vbox_crtc_do_set_base() on page-flips, but despite that function's name it only pins the new fb, unpins the old fb and sets vbox_crtc->fb_offset. It does not program the hardware to scan out at the new vbox_crtc->fb_offset value. Has that always been the case of vbox_crtc_do_set_base()? Or was there a recent commit that changed that behavior? I tested this patch using a Weston EGL mock navigation test app around 4.14 time-frame, that exercises page flip and it was scanning out the new fb, but maybe what I was looking at was a scan-out of an old/now stale fb from a previous page-flip. In any case thanks for fixing. Steve This was causing only every other frame (assuming page-flipping between 2 buffers) to be shown since we kept scanning out of the old (now unpinned!) buffer. This commit fixes this by adding code to vbox_crtc_page_flip() to tell the hardware to scanout from the new fb_offset. Fixes: 2408898e3b6c ("staging: vboxvideo: Add page-flip support") Cc: Steve Longerbeam Signed-off-by: Hans de Goede --- drivers/staging/vboxvideo/vbox_mode.c | 5 + 1 file changed, 5 insertions(+) diff --git a/drivers/staging/vboxvideo/vbox_mode.c b/drivers/staging/vboxvideo/vbox_mode.c index a83eac8668d0..79836c8fb909 100644 --- a/drivers/staging/vboxvideo/vbox_mode.c +++ b/drivers/staging/vboxvideo/vbox_mode.c @@ -323,6 +323,11 @@ static int vbox_crtc_page_flip(struct drm_crtc *crtc, if (rc) return rc; + mutex_lock(>hw_mutex); + vbox_set_view(crtc); + vbox_do_modeset(crtc, >mode); + mutex_unlock(>hw_mutex); + spin_lock_irqsave(>event_lock, flags); if (event) ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH 0/2] staging: vboxvideo: Add page-flip support
Hi all, On 08/07/2018 02:57 PM, Steve Longerbeam wrote: On 08/06/2018 01:34 AM, Daniel Vetter wrote: On Fri, Jul 20, 2018 at 10:17:29AM -0700, Steve Longerbeam wrote: Adds crtc page-flip support by passing the new requested framebuffer to vbox_crtc_do_set_base(). There is no attempt to support vblank interrupts, so this page-flip implementation does not try to sync the page-flip to vertical blanking, so expect tearing effects. Is it possible to access the host vblank intervals in VBOX? I think it'd be great to move vboxvideo over to atomic instead, which is required for destaging anyway, and would give you page flip support for free. Hi Daniel, yes I agree vboxvideo needs to move to the DRM atomic framework. Irrespective of moving vboxvideo to atomic framework, I still need to get an answer to the question, is it possible to access the host vblank intervals in order to sync page-flip to vblank, to prevent tearing. Is this possible somehow, perhaps by calling into the VBOX hypervisor? Thanks, Steve ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH 0/2] staging: vboxvideo: Add page-flip support
On 08/06/2018 01:34 AM, Daniel Vetter wrote: On Fri, Jul 20, 2018 at 10:17:29AM -0700, Steve Longerbeam wrote: Adds crtc page-flip support by passing the new requested framebuffer to vbox_crtc_do_set_base(). There is no attempt to support vblank interrupts, so this page-flip implementation does not try to sync the page-flip to vertical blanking, so expect tearing effects. Is it possible to access the host vblank intervals in VBOX? I think it'd be great to move vboxvideo over to atomic instead, which is required for destaging anyway, and would give you page flip support for free. Hi Daniel, yes I agree vboxvideo needs to move to the DRM atomic framework. Steve Steve Longerbeam (2): staging: vboxvideo: Pass a new framebuffer to vbox_crtc_do_set_base staging: vboxvideo: Add page-flip support drivers/staging/vboxvideo/vbox_mode.c | 34 +++--- 1 file changed, 31 insertions(+), 3 deletions(-) -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v3 02/14] gpu: ipu-csi: Check for field type alternate
Hi Philipp, On 08/02/2018 02:42 AM, Philipp Zabel wrote: Hi Steve, On Wed, 2018-08-01 at 12:12 -0700, Steve Longerbeam wrote: When the CSI is receiving from a bt.656 bus, include a check for field type 'alternate' when determining whether to set CSI clock mode to CCIR656_INTERLACED or CCIR656_PROGRESSIVE. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-csi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index caa05b0..5450a2d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -339,7 +339,8 @@ static void fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, break; case V4L2_MBUS_BT656: csicfg->ext_vsync = 0; - if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field)) + if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field) || + mbus_fmt->field == V4L2_FIELD_ALTERNATE) csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; else csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; this patch is already merged in v4.18-rc7. Ah, I just noticed that after a fetch from kernel.org, thanks. Steve ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v3 05/14] gpu: ipu-v3: Allow negative offsets for interlaced scanning
Hi Philipp, On 08/02/2018 02:46 AM, Philipp Zabel wrote: On Wed, 2018-08-01 at 12:12 -0700, Steve Longerbeam wrote: From: Philipp Zabel The IPU also supports interlaced buffers that start with the bottom field. To achieve this, the the base address EBA has to be increased by a stride length and the interlace offset ILO has to be set to the negative stride. Signed-off-by: Philipp Zabel Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-cpmem.c | 15 +-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index e68e473..8cd9e37 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -269,9 +269,20 @@ EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) { + u32 ilo, sly; + + if (stride < 0) { + stride = -stride; + ilo = 0x10 - (stride / 8); + } else { + ilo = stride / 8; + } + + sly = (stride * 2) - 1; + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); - ipu_ch_param_write_field(ch, IPU_FIELD_ILO, stride / 8); - ipu_ch_param_write_field(ch, IPU_FIELD_SLY, (stride * 2) - 1); + ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); + ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); This patch is merged in drm-next: 4e3c5d7e05be ("gpu: ipu-v3: Allow negative offsets for interlaced scanning") I don't see it in drm-next, but I see it in linux-next/master. Thanks. Steve ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 03/14] gpu: ipu-csi: Swap fields according to input/output field types
The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-csi.c | 133 +- drivers/staging/media/imx/imx-media-csi.c | 13 +-- include/video/imx-ipu-v3.h| 3 +- 3 files changed, 98 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index 5450a2d..7a845c9 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -315,6 +315,15 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code) return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ @@ -358,19 +367,73 @@ static void fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, } } +static int ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt) + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt) { struct ipu_csi_bus_config cfg; unsigned long flags; u32 width, height, data = 0; + v4l2_std_id std; + int ret = 0; - fill_csi_bus_cfg(, mbus_cfg, mbus_fmt); + fill_csi_bus_cfg(, mbus_cfg, infmt); /* set default sensor frame width and height */ - width = mbus_fmt->width; - height = mbus_fmt->height; + width = infmt->width; + height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + height *= 2; /* Set the CSI_SENS_CONF register remaining fields */ data |= cfg.data_width <
[PATCH v3 06/14] gpu: ipu-v3: Add planar support to interlaced scan
To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 -- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index 8cd9e37..eae0f63 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -267,9 +267,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -280,9 +281,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41ca..af72248 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -412,7 +412,8 @@ static int prp_setup_channel(struct prp_priv *priv, if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field) && channel == priv->out_ch) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline); + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 2fdd21d..1c468ec 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -509,7 +509,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field)) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index f44a351..e888c66 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -255,7 +255,8 @@ void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride); void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch); void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf); void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride); +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat); void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id); int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch); void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize); -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 02/14] gpu: ipu-csi: Check for field type alternate
When the CSI is receiving from a bt.656 bus, include a check for field type 'alternate' when determining whether to set CSI clock mode to CCIR656_INTERLACED or CCIR656_PROGRESSIVE. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-csi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index caa05b0..5450a2d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -339,7 +339,8 @@ static void fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, break; case V4L2_MBUS_BT656: csicfg->ext_vsync = 0; - if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field)) + if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field) || + mbus_fmt->field == V4L2_FIELD_ALTERNATE) csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; else csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 04/14] gpu: ipu-v3: Fix U/V offset macros for planar 4:2:0
The U and V offset macros for planar 4:2:0 (U_OFFSET, V_OFFSET, and UV_OFFSET), are not correct. The height component to the offset was calculated as: (pix->width * y / 4) But this does not produce correct offsets for odd values of y (luma line #). The luma line # must be decimated by two to produce the correct U/V line #, so the correct formula is: (pix->width * (y / 2) / 2) Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-cpmem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index 9f2d9ec..e68e473 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -530,17 +530,17 @@ static const struct ipu_rgb def_bgra_16 = { #define Y_OFFSET(pix, x, y)((x) + pix->width * (y)) #define U_OFFSET(pix, x, y)((pix->width * pix->height) + \ -(pix->width * (y) / 4) + (x) / 2) +(pix->width * ((y) / 2) / 2) + (x) / 2) #define V_OFFSET(pix, x, y)((pix->width * pix->height) + \ (pix->width * pix->height / 4) + \ -(pix->width * (y) / 4) + (x) / 2) +(pix->width * ((y) / 2) / 2) + (x) / 2) #define U2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ (pix->width * (y) / 2) + (x) / 2) #define V2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ (pix->width * pix->height / 2) + \ (pix->width * (y) / 2) + (x) / 2) #define UV_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -(pix->width * (y) / 2) + (x)) +(pix->width * ((y) / 2)) + (x)) #define UV2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ (pix->width * y) + (x)) -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 05/14] gpu: ipu-v3: Allow negative offsets for interlaced scanning
From: Philipp Zabel The IPU also supports interlaced buffers that start with the bottom field. To achieve this, the the base address EBA has to be increased by a stride length and the interlace offset ILO has to be set to the negative stride. Signed-off-by: Philipp Zabel Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-cpmem.c | 15 +-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index e68e473..8cd9e37 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -269,9 +269,20 @@ EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) { + u32 ilo, sly; + + if (stride < 0) { + stride = -stride; + ilo = 0x10 - (stride / 8); + } else { + ilo = stride / 8; + } + + sly = (stride * 2) - 1; + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); - ipu_ch_param_write_field(ch, IPU_FIELD_ILO, stride / 8); - ipu_ch_param_write_field(ch, IPU_FIELD_SLY, (stride * 2) - 1); + ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); + ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 1/2] staging: vboxvideo: Pass a new framebuffer to vbox_crtc_do_set_base
This modifies vbox_crtc_do_set_base() to take a new framebuffer to be activated, instead of the existing framebuffer attached to the crtc. This change allows the function to be given the new framebuffer from a page-flip request. Signed-off-by: Steve Longerbeam --- drivers/staging/vboxvideo/vbox_mode.c | 8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/vboxvideo/vbox_mode.c b/drivers/staging/vboxvideo/vbox_mode.c index b265fe9..688e80d 100644 --- a/drivers/staging/vboxvideo/vbox_mode.c +++ b/drivers/staging/vboxvideo/vbox_mode.c @@ -222,7 +222,9 @@ static bool vbox_set_up_input_mapping(struct vbox_private *vbox) } static int vbox_crtc_do_set_base(struct drm_crtc *crtc, -struct drm_framebuffer *old_fb, int x, int y) +struct drm_framebuffer *old_fb, +struct drm_framebuffer *new_fb, +int x, int y) { struct vbox_private *vbox = crtc->dev->dev_private; struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); @@ -245,7 +247,7 @@ static int vbox_crtc_do_set_base(struct drm_crtc *crtc, vbox_bo_unreserve(bo); } - vbox_fb = to_vbox_framebuffer(CRTC_FB(crtc)); + vbox_fb = to_vbox_framebuffer(new_fb); obj = vbox_fb->obj; bo = gem_to_vbox_bo(obj); @@ -281,7 +283,7 @@ static int vbox_crtc_do_set_base(struct drm_crtc *crtc, static int vbox_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { - return vbox_crtc_do_set_base(crtc, old_fb, x, y); + return vbox_crtc_do_set_base(crtc, old_fb, CRTC_FB(crtc), x, y); } static int vbox_crtc_mode_set(struct drm_crtc *crtc, -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 0/2] staging: vboxvideo: Add page-flip support
Adds crtc page-flip support by passing the new requested framebuffer to vbox_crtc_do_set_base(). There is no attempt to support vblank interrupts, so this page-flip implementation does not try to sync the page-flip to vertical blanking, so expect tearing effects. Is it possible to access the host vblank intervals in VBOX? Steve Longerbeam (2): staging: vboxvideo: Pass a new framebuffer to vbox_crtc_do_set_base staging: vboxvideo: Add page-flip support drivers/staging/vboxvideo/vbox_mode.c | 34 +++--- 1 file changed, 31 insertions(+), 3 deletions(-) -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 2/2] staging: vboxvideo: Add page-flip support
Adds crtc page-flip support by passing the new requested framebuffer to vbox_crtc_do_set_base(). Note there is no attempt to support vblank interrupts, it's not not known how to do this in VBOX or if it is even possible. Since this page-flip implementation does not try to sync the page-flip to vertical blanking, tearing effects are possible. Signed-off-by: Steve Longerbeam --- drivers/staging/vboxvideo/vbox_mode.c | 26 ++ 1 file changed, 26 insertions(+) diff --git a/drivers/staging/vboxvideo/vbox_mode.c b/drivers/staging/vboxvideo/vbox_mode.c index 688e80d..285d8ad 100644 --- a/drivers/staging/vboxvideo/vbox_mode.c +++ b/drivers/staging/vboxvideo/vbox_mode.c @@ -308,6 +308,31 @@ static int vbox_crtc_mode_set(struct drm_crtc *crtc, return ret; } +static int vbox_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t page_flip_flags, + struct drm_modeset_acquire_ctx *ctx) +{ + struct vbox_private *vbox = crtc->dev->dev_private; + struct drm_device *drm = vbox->dev; + unsigned long flags; + int rc; + + rc = vbox_crtc_do_set_base(crtc, CRTC_FB(crtc), fb, 0, 0); + if (rc) + return rc; + + spin_lock_irqsave(>event_lock, flags); + + if (event) + drm_crtc_send_vblank_event(crtc, event); + + spin_unlock_irqrestore(>event_lock, flags); + + return 0; +} + static void vbox_crtc_disable(struct drm_crtc *crtc) { } @@ -346,6 +371,7 @@ static const struct drm_crtc_funcs vbox_crtc_funcs = { .reset = vbox_crtc_reset, .set_config = drm_crtc_helper_set_config, /* .gamma_set = vbox_crtc_gamma_set, */ + .page_flip = vbox_crtc_page_flip, .destroy = vbox_crtc_destroy, }; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH] gpu: ipu-v3: Fix CSI selection for VDIC
From: Marek Vasut <ma...@denx.de> The description of the CSI_SEL bit in the i.MX6 reference manual is incorrect. It states "This bit defines which CSI is the input to the IC. This bit is effective only if IC_INPUT is bit cleared". From experiment it was found this is in fact not correct. The CSI_SEL bit selects which CSI is input to _both_ the VDIC _and_ the IC. If the IC_INPUT bit is set so that the IC is receiving from the VDIC, the IC ignores the CSI_SEL bit, but CSI_SEL still selects which CSI the VDIC receives from in that case. Signed-off-by: Marek Vasut <ma...@denx.de> Signed-off-by: Steve Longerbeam <steve_longerb...@mentor.com> --- drivers/gpu/ipu-v3/ipu-common.c | 15 --- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index 16d5568..2fb5f43 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -725,15 +725,16 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) spin_lock_irqsave(>lock, flags); val = ipu_cm_read(ipu, IPU_CONF); - if (vdi) { + if (vdi) val |= IPU_CONF_IC_INPUT; - } else { + else val &= ~IPU_CONF_IC_INPUT; - if (csi_id == 1) - val |= IPU_CONF_CSI_SEL; - else - val &= ~IPU_CONF_CSI_SEL; - } + + if (csi_id == 1) + val |= IPU_CONF_CSI_SEL; + else + val &= ~IPU_CONF_CSI_SEL; + ipu_cm_write(ipu, val, IPU_CONF); spin_unlock_irqrestore(>lock, flags); -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH] gpu: ipu-v3: vdic: include AUTO field order bit in ipu_vdi_set_field_order
The field order selection in VDIC_C register uses different bits depending on whether the VDIC is receiving from a CSI ("AUTO") or from memory ("MAN"). Since the VDIC cannot receive from both CSI and memory at the same time, set or clear both field order bits to cover both cases. Signed-off-by: Steve Longerbeam <steve_longerb...@mentor.com> --- drivers/gpu/ipu-v3/ipu-vdi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-vdi.c b/drivers/gpu/ipu-v3/ipu-vdi.c index f27bf5a..a663893 100644 --- a/drivers/gpu/ipu-v3/ipu-vdi.c +++ b/drivers/gpu/ipu-v3/ipu-vdi.c @@ -88,9 +88,9 @@ void ipu_vdi_set_field_order(struct ipu_vdi *vdi, v4l2_std_id std, u32 field) reg = ipu_vdi_read(vdi, VDI_C); if (top_field_0) - reg &= ~VDI_C_TOP_FIELD_MAN_1; + reg &= ~(VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1); else - reg |= VDI_C_TOP_FIELD_MAN_1; + reg |= VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1; ipu_vdi_write(vdi, reg, VDI_C); spin_unlock_irqrestore(>lock, flags); -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH] gpu: ipu-v3: export ipu_csi_set_downsize
On 02/16/2017 03:40 AM, Philipp Zabel wrote: This function will be used by the media drivers and needs to be exported to allow them to be built as modules. Reported-by: Russell King <li...@armlinux.org.uk> Fixes: 867341b95891 ("gpu: ipu-v3: add ipu_csi_set_downsize") Signed-off-by: Philipp Zabel <p.za...@pengutronix.de> --- drivers/gpu/ipu-v3/ipu-csi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index 63c7292f427a2..24e12b87a0cbe 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -544,6 +544,7 @@ void ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert) spin_unlock_irqrestore(>lock, flags); } +EXPORT_SYMBOL_GPL(ipu_csi_set_downsize); void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active, u32 r_value, u32 g_value, u32 b_value, Acked-by: Steve Longerbeam <steve_longerb...@mentor.com> ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH] gpu: ipu-v3: Stop overwriting pdev->dev.of_node of child devices
On 02/16/2017 07:42 AM, Philipp Zabel wrote: Setting dev->of_node changes the modalias and breaks module autoloading. Since there is an of_node field in the platform data passed to child devices, we don't even need this anymore. Suggested-by: Russell King <li...@armlinux.org.uk> Signed-off-by: Philipp Zabel <p.za...@pengutronix.de> --- drivers/gpu/ipu-v3/ipu-common.c | 6 -- 1 file changed, 6 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index 97218af4fe75c..8368e6f766ee5 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -1238,12 +1238,6 @@ static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) platform_device_put(pdev); goto err_register; } - - /* -* Set of_node only after calling platform_device_add. Otherwise -* the platform:imx-ipuv3-crtc modalias won't be used. -*/ - pdev->dev.of_node = of_node; } return 0; Acked-by: Steve Longerbeam <steve_longerb...@mentor.com> ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 12/12] gpu: ipu-v3: Add smfc and ic client devices
Adds IPU client devices for the SMFC and IC task units. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-common.c | 87 +++-- include/video/imx-ipu-v3.h | 3 ++ 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index b6ca36b..729581d 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -1161,18 +1161,77 @@ static struct ipu_platform_reg client_reg[] = { .pdata = { .type = IPU_CSI, .csi = 0, - .dma[0] = IPUV3_CHANNEL_CSI0, - .dma[1] = -EINVAL, + .dma[0] = -EINVAL, }, .name = "imx-ipuv3-csi", }, { .pdata = { .type = IPU_CSI, .csi = 1, + .dma[0] = -EINVAL, + }, + .name = "imx-ipuv3-csi", + }, { + .pdata = { + .type = IPU_SMFC, + .smfc = 0, + .dma[0] = IPUV3_CHANNEL_CSI0, + .dma[1] = -EINVAL, + }, + .name = "imx-ipuv3-smfc", + }, { + .pdata = { + .type = IPU_SMFC, + .smfc = 1, .dma[0] = IPUV3_CHANNEL_CSI1, .dma[1] = -EINVAL, }, - .name = "imx-ipuv3-csi", + .name = "imx-ipuv3-smfc", + }, { + .pdata = { + .type = IPU_IC, + .ic_task = IC_TASK_ENCODER, + .ic = 0, + .dma[0] = IPUV3_CHANNEL_IC_PRP_ENC_MEM, + .dma[1] = -EINVAL, + }, + .name = "imx-ipuv3-ic", + }, { + .pdata = { + .type = IPU_IC, + .ic_task = IC_TASK_VIEWFINDER, + .ic = 0, + .dma[0] = IPUV3_CHANNEL_IC_PRP_VF_MEM, + .dma[1] = -EINVAL, + }, + .name = "imx-ipuv3-ic", + }, { + .pdata = { + .type = IPU_IC, + .ic_task = IC_TASK_POST_PROCESSOR, + .ic = 0, + .dma[0] = IPUV3_CHANNEL_IC_PP_MEM, + .dma[1] = -EINVAL, + }, + .name = "imx-ipuv3-ic", + }, { + .pdata = { + .type = IPU_IC, + .ic_task = IC_TASK_POST_PROCESSOR, + .ic = 1, + .dma[0] = IPUV3_CHANNEL_IC_PP_MEM, + .dma[1] = -EINVAL, + }, + .name = "imx-ipuv3-ic", + }, { + .pdata = { + .type = IPU_IC, + .ic_task = IC_TASK_POST_PROCESSOR, + .ic = 2, + .dma[0] = IPUV3_CHANNEL_IC_PP_MEM, + .dma[1] = -EINVAL, + }, + .name = "imx-ipuv3-ic", }, { .pdata = { .type = IPU_DI, @@ -1213,6 +1272,28 @@ of_get_ipu_client_node(struct ipu_soc *ipu, struct ipu_platform_reg *reg) "ipu%d_csi", ipu->id + 1); client_id = reg->pdata.csi; break; + case IPU_SMFC: + snprintf(node_name, sizeof(node_name), "ipu%d_smfc", +ipu->id + 1); + client_id = reg->pdata.smfc; + break; + case IPU_IC: + switch (reg->pdata.ic_task) { + case IC_TASK_ENCODER: + snprintf(node_name, sizeof(node_name), +"ipu%d_ic_prpenc", ipu->id + 1); + break; + case IC_TASK_VIEWFINDER: + snprintf(node_name, sizeof(node_name), +"ipu%d_ic_prpvf", ipu->id + 1); + break; + case IC_TASK_POST_PROCESSOR: + snprintf(node_name, sizeof(node_name), +"ipu%d_ic_pp", ipu->id + 1); + break; + } + client_id = reg->pdata.ic; + break; case IPU_DI: snprintf(node_name, sizeof(node_name), "ipu%d_di", ipu->id + 1); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index 7709af7..4e70ca4 100644 --- a/include/video/imx-ipu-v3.
[PATCH 11/12] gpu: ipu-v3: lookup ipu client nodes by name
To allow for IPU clients containing multiple ports, they are no longer a single port node name, but have a name of the format "ipu_". So we can no longer use of_graph_get_port_by_id() to lookup the client node. Create the function of_get_ipu_client_node() that looks up the client node by node name and unit id. The ipu_unit_type enumeration is added to the client_reg[] entries to compose the node names. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-common.c | 55 +++-- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index 97218af..b6ca36b 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -1159,6 +1159,7 @@ struct ipu_platform_reg { static struct ipu_platform_reg client_reg[] = { { .pdata = { + .type = IPU_CSI, .csi = 0, .dma[0] = IPUV3_CHANNEL_CSI0, .dma[1] = -EINVAL, @@ -1166,6 +1167,7 @@ static struct ipu_platform_reg client_reg[] = { .name = "imx-ipuv3-csi", }, { .pdata = { + .type = IPU_CSI, .csi = 1, .dma[0] = IPUV3_CHANNEL_CSI1, .dma[1] = -EINVAL, @@ -1173,6 +1175,7 @@ static struct ipu_platform_reg client_reg[] = { .name = "imx-ipuv3-csi", }, { .pdata = { + .type = IPU_DI, .di = 0, .dc = 5, .dp = IPU_DP_FLOW_SYNC_BG, @@ -1182,6 +1185,7 @@ static struct ipu_platform_reg client_reg[] = { .name = "imx-ipuv3-crtc", }, { .pdata = { + .type = IPU_DI, .di = 1, .dc = 1, .dp = -EINVAL, @@ -1195,6 +1199,46 @@ static struct ipu_platform_reg client_reg[] = { static DEFINE_MUTEX(ipu_client_id_mutex); static int ipu_client_id; +static struct device_node * +of_get_ipu_client_node(struct ipu_soc *ipu, struct ipu_platform_reg *reg) +{ + struct device *dev = ipu->dev; + struct device_node *client; + char node_name[32]; + u32 id, client_id = 0; + + switch (reg->pdata.type) { + case IPU_CSI: + snprintf(node_name, sizeof(node_name), +"ipu%d_csi", ipu->id + 1); + client_id = reg->pdata.csi; + break; + case IPU_DI: + snprintf(node_name, sizeof(node_name), +"ipu%d_di", ipu->id + 1); + client_id = reg->pdata.di; + break; + default: + client = NULL; + goto out; + } + + for_each_child_of_node(dev->of_node, client) { + if (client->name && + (of_node_cmp(client->name, node_name) == 0)) { + of_property_read_u32(client, "reg", ); + if (id == client_id) + break; + } + } +out: + if (!client) + dev_info(dev, "no %s%d node in %s, not using %s%d\n", +node_name, client_id, dev->of_node->full_name, +node_name, client_id); + return client; +} + static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) { struct device *dev = ipu->dev; @@ -1211,15 +1255,10 @@ static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) struct platform_device *pdev; struct device_node *of_node; - /* Associate subdevice with the corresponding port node */ - of_node = of_graph_get_port_by_id(dev->of_node, i); - if (!of_node) { - dev_info(dev, -"no port@%d node in %s, not using %s%d\n", -i, dev->of_node->full_name, -(i / 2) ? "DI" : "CSI", i % 2); + /* Associate subdevice with the corresponding client node */ + of_node = of_get_ipu_client_node(ipu, reg); + if (!of_node) continue; - } pdev = platform_device_alloc(reg->name, id++); if (!pdev) { -- 2.7.4
[PATCH 10/12] gpu: ipu-v3: Add ipu_unit_type enumeration
Adds an enumeration of the major IPUv3 subunits. Provide that info in struct ipu_client_platformdata to more easily determine the IPU client type. Signed-off-by: Steve Longerbeam --- include/video/imx-ipu-v3.h | 18 ++ 1 file changed, 18 insertions(+) diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index 53cd07c..7709af7 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -28,6 +28,23 @@ enum ipuv3_type { IPUV3H, }; +/* + * Enumeration of the major IPU subunits + */ +enum ipu_unit_type { + IPU_IDMAC = 0, + IPU_CM, + IPU_CSI, + IPU_SMFC, + IPU_IC, + IPU_VDI, + IPU_IRT, + IPU_DC, + IPU_DI, + IPU_DP, + IPU_DMFC, +}; + #define IPU_PIX_FMT_GBR24 v4l2_fourcc('G', 'B', 'R', '3') /* @@ -399,6 +416,7 @@ int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, bool hflip, bool vflip); struct ipu_client_platformdata { + enum ipu_unit_type type; int csi; int di; int dc; -- 2.7.4
[PATCH 09/12] ARM: dts: imx6-sabreauto: add the ADV7180 video decoder
Enables the ADV7180 decoder sensor. The ADV7180 connects to the parallel-bus mux input on ipu1_csi0_mux. On the sabreauto, two analog video inputs are routed to the ADV7180, composite on Ain1, and composite on Ain3. Those inputs are defined via inputs and input-names under the ADV7180 node. The ADV7180 power pin is via max7310_b port expander. Signed-off-by: Steve Longerbeam --- arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 62 1 file changed, 62 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi index d74882a..9222026 100644 --- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi @@ -147,10 +147,48 @@ gpio-controller; #gpio-cells = <2>; }; + + camera: adv7180 at 21 { + compatible = "adi,adv7180"; + reg = <0x21>; + powerdown-gpios = <_b 2 GPIO_ACTIVE_LOW>; + interrupt-parent = <>; + interrupts = <27 0x8>; + inputs = <0x00 0x02>; + input-names = "ADV7180 Composite on Ain1", + "ADV7180 Composite on Ain3"; + + port { + adv7180_to_ipu1_csi0_mux: endpoint { + remote-endpoint = <_csi0_mux_from_parallel_sensor>; + bus-width = <8>; + }; + }; + }; }; }; }; +_smfc0 { + fim { + enable = <1>; + }; +}; + +_csi0_from_ipu1_csi0_mux { + bus-width = <8>; +}; + +_csi0_mux_from_parallel_sensor { + remote-endpoint = <_to_ipu1_csi0_mux>; + bus-width = <8>; +}; + +_csi0 { + pinctrl-names = "default"; + pinctrl-0 = <_ipu1_csi0>; +}; + { assigned-clocks = < IMX6QDL_PLL4_BYPASS_SRC>, < IMX6QDL_PLL4_BYPASS>, @@ -451,6 +489,30 @@ >; }; + pinctrl_ipu1_csi0: ipu1grp-csi0 { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT4__IPU1_CSI0_DATA04 0x8000 + MX6QDL_PAD_CSI0_DAT5__IPU1_CSI0_DATA05 0x8000 + MX6QDL_PAD_CSI0_DAT6__IPU1_CSI0_DATA06 0x8000 + MX6QDL_PAD_CSI0_DAT7__IPU1_CSI0_DATA07 0x8000 + MX6QDL_PAD_CSI0_DAT8__IPU1_CSI0_DATA08 0x8000 + MX6QDL_PAD_CSI0_DAT9__IPU1_CSI0_DATA09 0x8000 + MX6QDL_PAD_CSI0_DAT10__IPU1_CSI0_DATA10 0x8000 + MX6QDL_PAD_CSI0_DAT11__IPU1_CSI0_DATA11 0x8000 + MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x8000 + MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x8000 + MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x8000 + MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x8000 + MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x8000 + MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x8000 + MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x8000 + MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x8000 + MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x8000 + MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x8000 + MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x8000 + >; + }; + pinctrl_pwm3: pwm1grp { fsl,pins = < MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x1b0b1 -- 2.7.4
[PATCH 08/12] ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture
Add pinctrl groups for both GPT input capture channels. Signed-off-by: Steve Longerbeam --- arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 12 1 file changed, 12 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi index 944a4fa..d74882a 100644 --- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi @@ -457,6 +457,18 @@ >; }; + pinctrl_gpt_input_capture0: gptinputcapture0grp { + fsl,pins = < + MX6QDL_PAD_SD1_DAT0__GPT_CAPTURE1 0x8000 + >; + }; + + pinctrl_gpt_input_capture1: gptinputcapture1grp { + fsl,pins = < + MX6QDL_PAD_SD1_DAT1__GPT_CAPTURE2 0x8000 + >; + }; + pinctrl_spdif: spdifgrp { fsl,pins = < MX6QDL_PAD_KEY_COL3__SPDIF_IN 0x1b0b0 -- 2.7.4
[PATCH 07/12] ARM: dts: imx6-sabreauto: add reset-gpios property for max7310_b
The reset pin to the port expander chip (MAX7310) is controlled by a gpio, so define a reset-gpios property to control it. There are three MAX7310's on the SabreAuto CPU card (max7310_[abc]), but all use the same pin for their reset. Since all can't acquire the same pin, assign it to max7310_b, that chip is needed by more functions (usb and adv7180). Signed-off-by: Steve Longerbeam --- arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 9 + 1 file changed, 9 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi index 043d20c..944a4fa 100644 --- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi @@ -136,6 +136,9 @@ reg = <0x32>; gpio-controller; #gpio-cells = <2>; + pinctrl-names = "default"; + pinctrl-0 = <_max7310>; + reset-gpios = < 15 GPIO_ACTIVE_LOW>; }; max7310_c: gpio at 34 { @@ -442,6 +445,12 @@ >; }; + pinctrl_max7310: max7310grp { + fsl,pins = < + MX6QDL_PAD_SD2_DAT0__GPIO1_IO15 0x8000 + >; + }; + pinctrl_pwm3: pwm1grp { fsl,pins = < MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x1b0b1 -- 2.7.4
[PATCH 06/12] ARM: dts: imx6-sabreauto: create i2cmux for i2c3
The sabreauto uses a steering pin to select between the SDA signal on i2c3 bus, and a data-in pin for an SPI NOR chip. Use i2cmux to control this steering pin. Idle state of the i2cmux selects SPI NOR. This is not a classic way to use i2cmux, since one side of the mux selects something other than an i2c bus, but it works and is probably the cleanest solution. Note that if one thread is attempting to access SPI NOR while another thread is accessing i2c3, the SPI NOR access will fail since the i2cmux has selected the SDA pin rather than SPI NOR data-in. This couldn't be avoided in any case, the board is not designed to allow concurrent i2c3 and SPI NOR functions (and the default device-tree does not enable SPI NOR anyway). Devices hanging off i2c3 should now be defined under i2cmux, so that the steering pin can be properly controlled to access those devices. The port expanders (MAX7310) are thus moved into i2cmux. Signed-off-by: Steve Longerbeam --- arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 65 +--- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi index e000e6f..043d20c 100644 --- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi @@ -108,6 +108,44 @@ default-brightness-level = <7>; status = "okay"; }; + + i2cmux { + compatible = "i2c-mux-gpio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <_i2c3mux>; + mux-gpios = < 4 0>; + i2c-parent = <>; + idle-state = <0>; + + i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + max7310_a: gpio at 30 { + compatible = "maxim,max7310"; + reg = <0x30>; + gpio-controller; + #gpio-cells = <2>; + }; + + max7310_b: gpio at 32 { + compatible = "maxim,max7310"; + reg = <0x32>; + gpio-controller; + #gpio-cells = <2>; + }; + + max7310_c: gpio at 34 { + compatible = "maxim,max7310"; + reg = <0x34>; + gpio-controller; + #gpio-cells = <2>; + }; + }; + }; }; { @@ -291,27 +329,6 @@ pinctrl-names = "default"; pinctrl-0 = <_i2c3>; status = "okay"; - - max7310_a: gpio at 30 { - compatible = "maxim,max7310"; - reg = <0x30>; - gpio-controller; - #gpio-cells = <2>; - }; - - max7310_b: gpio at 32 { - compatible = "maxim,max7310"; - reg = <0x32>; - gpio-controller; - #gpio-cells = <2>; - }; - - max7310_c: gpio at 34 { - compatible = "maxim,max7310"; - reg = <0x34>; - gpio-controller; - #gpio-cells = <2>; - }; }; { @@ -419,6 +436,12 @@ >; }; + pinctrl_i2c3mux: i2c3muxgrp { + fsl,pins = < + MX6QDL_PAD_EIM_A24__GPIO5_IO04 0x8000 + >; + }; + pinctrl_pwm3: pwm1grp { fsl,pins = < MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x1b0b1 -- 2.7.4
[PATCH 05/12] ARM: dts: imx6-sabresd: add OV5642 and OV5640 camera sensors
Enables the OV5642 parallel-bus sensor, and the OV5640 MIPI CSI-2 sensor. The OV5642 connects to the parallel-bus mux input port on ipu1_csi0_mux. The OV5640 connects to the input port on the MIPI CSI-2 receiver on mipi_csi. It is set to transmit over MIPI virtual channel 1. Until the OV5652 sensor module compatible with the SabreSD becomes available for testing, the ov5642 node is currently disabled. Signed-off-by: Steve Longerbeam --- arch/arm/boot/dts/imx6dl-sabresd.dts | 5 ++ arch/arm/boot/dts/imx6q-sabresd.dts| 5 ++ arch/arm/boot/dts/imx6qdl-sabresd.dtsi | 114 - 3 files changed, 123 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6dl-sabresd.dts b/arch/arm/boot/dts/imx6dl-sabresd.dts index 1e45f2f..6cf7a50 100644 --- a/arch/arm/boot/dts/imx6dl-sabresd.dts +++ b/arch/arm/boot/dts/imx6dl-sabresd.dts @@ -15,3 +15,8 @@ model = "Freescale i.MX6 DualLite SABRE Smart Device Board"; compatible = "fsl,imx6dl-sabresd", "fsl,imx6dl"; }; + +_csi1_from_ipu1_csi1_mux { + data-lanes = <0 1>; + clock-lanes = <2>; +}; diff --git a/arch/arm/boot/dts/imx6q-sabresd.dts b/arch/arm/boot/dts/imx6q-sabresd.dts index 9cbdfe7..8c1d7ad 100644 --- a/arch/arm/boot/dts/imx6q-sabresd.dts +++ b/arch/arm/boot/dts/imx6q-sabresd.dts @@ -23,3 +23,8 @@ { status = "okay"; }; + +_csi1_from_mipi_vc1 { + data-lanes = <0 1>; + clock-lanes = <2>; +}; diff --git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi index 8e9e0d9..e36e1e7 100644 --- a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi @@ -10,6 +10,7 @@ * http://www.gnu.org/copyleft/gpl.html */ +#include #include #include @@ -146,6 +147,33 @@ }; }; +_csi0_from_ipu1_csi0_mux { + bus-width = <8>; + data-shift = <12>; /* Lines 19:12 used */ + hsync-active = <1>; + vsync-active = <1>; +}; + +_csi0_mux_from_parallel_sensor { + remote-endpoint = <_to_ipu1_csi0_mux>; +}; + +_csi0 { + pinctrl-names = "default"; + pinctrl-0 = <_ipu1_csi0>; +}; + +_csi { + status = "okay"; +}; + +/* Incoming port from sensor */ +_csi_from_mipi_sensor { + remote-endpoint = <_to_mipi_csi>; + data-lanes = <0 1>; + clock-lanes = <2>; +}; + { pinctrl-names = "default"; pinctrl-0 = <_audmux>; @@ -214,7 +242,33 @@ 0x8014 /* 4:FN_DMICCDAT */ 0x /* 5:Default */ >; - }; + }; + + camera: ov5642 at 3c { + compatible = "ovti,ov5642"; + pinctrl-names = "default"; + pinctrl-0 = <_ov5642>; + clocks = < IMX6QDL_CLK_CKO>; + clock-names = "xclk"; + reg = <0x3c>; + xclk = <2400>; + DOVDD-supply = <_reg>; /* 1.8v */ + AVDD-supply = <_reg>; /* 2.8v, rev C board is VGEN3 + rev B board is VGEN5 */ + DVDD-supply = <_reg>; /* 1.5v*/ + pwdn-gpios = < 16 GPIO_ACTIVE_HIGH>; /* SD1_DAT0 */ + reset-gpios = < 17 GPIO_ACTIVE_LOW>; /* SD1_DAT1 */ + status = "disabled"; + + port { + ov5642_to_ipu1_csi0_mux: endpoint { + remote-endpoint = <_csi0_mux_from_parallel_sensor>; + bus-width = <8>; + hsync-active = <1>; + vsync-active = <1>; + }; + }; + }; }; { @@ -322,6 +376,34 @@ }; }; }; + + mipi_camera: ov5640 at 3c { + compatible = "ovti,ov5640_mipi"; + pinctrl-names = "default"; + pinctrl-0 = <_ov5640>; + reg = <0x3c>; + clocks = < IMX6QDL_CLK_CKO>; + clock-names = "xclk"; + xclk = <2400>; + DOVDD-supply = <_reg>; /* 1.8v */ + AVDD-supply = <_reg>; /* 2.8v, rev C board is VGEN3 + rev B board is VGEN5 */ + DVDD-supply = <_reg>; /* 1.5v*/ + pwdn-gpios = < 19 GPIO_ACTIVE_HIGH>; /* SD1_DAT2 */ + reset-gpios = < 20 GPIO_ACTIVE_LOW>; /* SD1_CLK */ + + port { + #address-cells = <1>; + #size-cells = <0&
[PATCH 04/12] ARM: dts: imx6-sabrelite: add OV5642 and OV5640 camera sensors
Enables the OV5642 parallel-bus sensor, and the OV5640 MIPI CSI-2 sensor. Both hang off the same i2c2 bus, so they require different (and non- default) i2c slave addresses. The OV5642 connects to the parallel-bus mux input port on ipu1_csi0_mux. The OV5640 connects to the input port on the MIPI CSI-2 receiver on mipi_csi. It is set to transmit over MIPI virtual channel 1. Note there is a pin conflict with GPIO6. This pin functions as a power input pin to the OV5642, but ENET uses it as the h/w workaround for erratum ERR006687, to wake-up the ARM cores on normal RX and TX packet done events (see 6261c4c8). So workaround 6261c4c8 is reverted here to support the OV5642, and the "fsl,err006687-workaround-present" boolean also must be removed. The result is that the CPUidle driver will no longer allow entering the deep idle states on the sabrelite. Signed-off-by: Steve Longerbeam --- arch/arm/boot/dts/imx6dl-sabrelite.dts | 5 ++ arch/arm/boot/dts/imx6q-sabrelite.dts| 6 ++ arch/arm/boot/dts/imx6qdl-sabrelite.dtsi | 122 ++- 3 files changed, 129 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/imx6dl-sabrelite.dts b/arch/arm/boot/dts/imx6dl-sabrelite.dts index 0f06ca5..fec2524 100644 --- a/arch/arm/boot/dts/imx6dl-sabrelite.dts +++ b/arch/arm/boot/dts/imx6dl-sabrelite.dts @@ -48,3 +48,8 @@ model = "Freescale i.MX6 DualLite SABRE Lite Board"; compatible = "fsl,imx6dl-sabrelite", "fsl,imx6dl"; }; + +_csi1_from_ipu1_csi1_mux { + data-lanes = <0 1>; + clock-lanes = <2>; +}; diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts index 66d10d8..9e2d26d 100644 --- a/arch/arm/boot/dts/imx6q-sabrelite.dts +++ b/arch/arm/boot/dts/imx6q-sabrelite.dts @@ -52,3 +52,9 @@ { status = "okay"; }; + +_csi1_from_mipi_vc1 { + data-lanes = <0 1>; + clock-lanes = <2>; +}; + diff --git a/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi b/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi index 81dd6cd..d7fcb1a2 100644 --- a/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi @@ -39,6 +39,8 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ + +#include #include #include @@ -96,6 +98,15 @@ }; }; + mipi_xclk: mipi_xclk { + compatible = "pwm-clock"; + #clock-cells = <0>; + clock-frequency = <2200>; + clock-output-names = "mipi_pwm3"; + pwms = < 0 45>; /* 1 / 45 ns = 22 MHz */ + status = "okay"; + }; + gpio-keys { compatible = "gpio-keys"; pinctrl-names = "default"; @@ -220,6 +231,22 @@ }; }; +_csi0_from_ipu1_csi0_mux { + bus-width = <8>; + data-shift = <12>; /* Lines 19:12 used */ + hsync-active = <1>; + vync-active = <1>; +}; + +_csi0_mux_from_parallel_sensor { + remote-endpoint = <_to_ipu1_csi0_mux>; +}; + +_csi0 { + pinctrl-names = "default"; + pinctrl-0 = <_ipu1_csi0>; +}; + { pinctrl-names = "default"; pinctrl-0 = <_audmux>; @@ -271,9 +298,6 @@ txd1-skew-ps = <0>; txd2-skew-ps = <0>; txd3-skew-ps = <0>; - interrupts-extended = < 6 IRQ_TYPE_LEVEL_HIGH>, - < 0 119 IRQ_TYPE_LEVEL_HIGH>; - fsl,err006687-workaround-present; status = "okay"; }; @@ -302,6 +326,52 @@ pinctrl-names = "default"; pinctrl-0 = <_i2c2>; status = "okay"; + + camera: ov5642 at 42 { + compatible = "ovti,ov5642"; + pinctrl-names = "default"; + pinctrl-0 = <_ov5642>; + clocks = < IMX6QDL_CLK_CKO2>; + clock-names = "xclk"; + reg = <0x42>; + xclk = <2400>; + reset-gpios = < 8 GPIO_ACTIVE_LOW>; + pwdn-gpios = < 6 GPIO_ACTIVE_HIGH>; + gp-gpios = < 16 GPIO_ACTIVE_HIGH>; + + port { + ov5642_to_ipu1_csi0_mux: endpoint { + remote-endpoint = <_csi0_mux_from_parallel_sensor>; + bus-width = <8>; + hsync-active = <1>; + vsync-active = <1>; + }; + }; + }; + + mipi_camera: ov5640 at 40 { + compatible = "ovti,ov5640_mipi"; + pinctrl-names = "default&q
[PATCH 03/12] ARM: dts: imx6qdl: add video capture devices and connections
From: Philipp Zabel <p.za...@pengutronix.de> This patch adds the IPU subunit devices involved in video capture and image conversion, and defines all the possible hardware connections between them via OF graphs. External to the IPU: Video input multiplexers are defined that multiplex inputs from camera sensors and the MIPI-CSI2 gasket, to the IPU CSIs. On i.MX6Q/D two two-input multiplexers in front of IPU1 CSI0 and IPU2 CSI1 allow to select between CSI0/1 parallel input pads and the MIPI CSI-2 virtual channels 0/3. On i.MX6DL/S two five-input multiplexers in front of IPU1 CSI0 and IPU1 CSI1 allow to select between CSI0/1 parallel input pads and any of the four MIPI CSI-2 virtual channels. Internal to the IPU: The IPU CSI, SMFC, IC-PRPENC, IC-PRPVF, and IC-PP subunits are added as children of the IPUs, along with the hardware-supported connections between them. Finally, a media device node is defined. A video camera interface and mem2mem device are defined as children of the media device. Signed-off-by: Steve Longerbeam Signed-off-by: Philipp Zabel --- arch/arm/boot/dts/imx6dl.dtsi | 190 arch/arm/boot/dts/imx6q.dtsi | 487 + arch/arm/boot/dts/imx6qdl.dtsi | 368 +++ 3 files changed, 1045 insertions(+) diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi index 1ade195..4bab076 100644 --- a/arch/arm/boot/dts/imx6dl.dtsi +++ b/arch/arm/boot/dts/imx6dl.dtsi @@ -109,6 +109,120 @@ compatible = "fsl,imx-gpu-subsystem"; cores = <_2d>, <_3d>; }; + + ipu1_csi0_mux: ipu1_csi0_mux at 34 { + compatible = "imx-video-mux"; + reg = <0x34 0x07>; + gpr = <>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + sink-ports = <5>; + + port at 0 { + reg = <0>; + + ipu1_csi0_mux_from_mipi_vc0: endpoint { + remote-endpoint = <_vc0_to_ipu1_csi0_mux>; + }; + }; + + port at 1 { + reg = <1>; + + ipu1_csi0_mux_from_mipi_vc1: endpoint { + remote-endpoint = <_vc1_to_ipu1_csi0_mux>; + }; + }; + + port at 2 { + reg = <2>; + + ipu1_csi0_mux_from_mipi_vc2: endpoint { + remote-endpoint = <_vc2_to_ipu1_csi0_mux>; + }; + }; + + port at 3 { + reg = <3>; + + ipu1_csi0_mux_from_mipi_vc3: endpoint { + remote-endpoint = <_vc3_to_ipu1_csi0_mux>; + }; + }; + + port at 4 { + reg = <4>; + + ipu1_csi0_mux_from_parallel_sensor: endpoint { + }; + }; + + port at 5 { + reg = <5>; + + ipu1_csi0_mux_to_ipu1_csi0: endpoint { + remote-endpoint = <_csi0_from_ipu1_csi0_mux>; + }; + }; + }; + + ipu1_csi1_mux: ipu1_csi1_mux at 34 { + compatible = "imx-video-mux"; + reg = <0x34 0x38>; + gpr = <>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + sink-ports = <5>; + + port at 0 { + reg = <0>; + + ipu1_csi1_mux_from_mipi_vc0: endpoint { + remote-endpoint = <_vc0_to_ipu1_csi1_mux>; + }; + }; + + port at 1 { + reg = <1>; + + ipu1_csi1_mux_from_mipi_vc1: endpoint { + remote-endpoint = <_vc1_to_ipu1_csi1_mux>; + }; + }; + + port at 2 { + reg = <2>; + + ipu1_csi1_mux_from_mipi_vc2: endpoint { + remote-endpoint = <_vc2_to_ipu1_csi1_mux>; + }; + }; + + port at 3 { + reg = <3>; + + ipu1_csi1_mux_from_mipi_vc3: endpoint { + remote-endpoint = <_vc3_to_ipu1_csi1_mux>; + }; + }; + + port at 4 { + reg = <4>
[PATCH 02/12] ARM: dts: imx6qdl: rename ipu client nodes
To allow for IPU client devices that are composed of more than one port for input and output (SMFC and IC), change the nodes from being a single port node to nodes that can contain multiple ports. Rename the nodes to use the following format: "ipu_". The IPUv3 driver will then need to lookup the client nodes by name rather than by port id. Signed-off-by: Steve Longerbeam --- arch/arm/boot/dts/imx6q.dtsi | 12 ++-- arch/arm/boot/dts/imx6qdl.dtsi | 12 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index e9a5d0b..2b261ba 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -141,18 +141,18 @@ clock-names = "bus", "di0", "di1"; resets = < 4>; - ipu2_csi0: port at 0 { + ipu2_csi0: ipu2_csi at 0 { reg = <0>; }; - ipu2_csi1: port at 1 { + ipu2_csi1: ipu2_csi at 1 { reg = <1>; }; - ipu2_di0: port at 2 { + ipu2_di0: ipu2_di at 0 { #address-cells = <1>; #size-cells = <0>; - reg = <2>; + reg = <0>; ipu2_di0_disp0: disp0-endpoint { }; @@ -174,10 +174,10 @@ }; }; - ipu2_di1: port at 3 { + ipu2_di1: ipu2_di at 1 { #address-cells = <1>; #size-cells = <0>; - reg = <3>; + reg = <1>; ipu2_di1_hdmi: hdmi-endpoint { remote-endpoint = <_mux_3>; diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index e01e5d5..2465187 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -1226,18 +1226,18 @@ clock-names = "bus", "di0", "di1"; resets = < 2>; - ipu1_csi0: port at 0 { + ipu1_csi0: ipu1_csi at 0 { reg = <0>; }; - ipu1_csi1: port at 1 { + ipu1_csi1: ipu1_csi at 1 { reg = <1>; }; - ipu1_di0: port at 2 { + ipu1_di0: ipu1_di at 0 { #address-cells = <1>; #size-cells = <0>; - reg = <2>; + reg = <0>; ipu1_di0_disp0: disp0-endpoint { }; @@ -1259,10 +1259,10 @@ }; }; - ipu1_di1: port at 3 { + ipu1_di1: ipu1_di at 1 { #address-cells = <1>; #size-cells = <0>; - reg = <3>; + reg = <1>; ipu1_di1_disp1: disp1-endpoint { }; -- 2.7.4
[PATCH 01/12] ARM: dts: imx6qdl: Add compatible, clocks, irqs to MIPI CSI-2 node
Add to the MIPI CSI2 receiver node: compatible string, interrupt sources, clocks. Signed-off-by: Steve Longerbeam --- arch/arm/boot/dts/imx6qdl.dtsi | 7 +++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index b13b0b2..e01e5d5 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -1121,7 +1121,14 @@ }; mipi_csi: mipi at 021dc000 { + compatible = "fsl,imx-mipi-csi2"; reg = <0x021dc000 0x4000>; + interrupts = <0 100 0x04>, <0 101 0x04>; + clocks = < IMX6QDL_CLK_HSI_TX>, +< IMX6QDL_CLK_VIDEO_27M>, +< IMX6QDL_CLK_EIM_SEL>; + clock-names = "dphy_clk", "cfg_clk", "pix_clk"; + status = "disabled"; }; mipi_dsi: mipi at 021e { -- 2.7.4
[PATCH 00/12] i.MX media devices and connections
Hi Philipp, Sascha, Shawn, et al, I've been working for the past few months on a media driver for i.MX. In addition to the media entities for the IPU-external units involved with video capture (video mux and MIPI CSI-2 receiver), I've created media entities for the IPU CSI, SMFC, and IC subunits. The IC entities carry out scaling, CSC, horizontal/vertical flip, and rotation. In addition, the IC-PRPVF entity carries out motion compensated de-interlace. The following series adds the OF device nodes and graphs that define all the possible hardware connections supported by the i.MX involved in video capture and image conversion. Here are some of the pipelines defined by the OF graphs: CSI -> IC-PRPENC CSI -> IC-PRPVF CSI -> IC-PRPVF -> IC-PP CSI -> SMFC CSI -> SMFC -> IC-PRPVF CSI -> SMFC -> IC-PP CSI -> SMFC -> IC-PRPVF -> IC-PP You will notice that three IC-PP nodes are defined (ipu1_ic_pp0, ipu1_ic_pp1, ipu1_ic_pp2, and same for ipu2). The reason for that is that the IC-PP media entity uses the new ipu-image-conversion API, which allows for multiple conversion contexts to be created. Each IC-PP entity thus creates its own conversion context, and there can be any number of IC-PP entities instantiated as needed by the OF graph. Camera sensor nodes are also added for the SabreAuto, SabreSD, and SabreLite reference platforms. The media driver is now in fairly good shape. It parses the OF graphs to create the media pads and links. All the pipelines defined by the OF graphs have been tested and are working. My media driver work is at: git at github.com:slongerbeam/mediatree.git, branch imx-media-staging-md-v2. For an overview of the pipelines supported and usage notes for the reference boards, you can refer to Documentation/media/v4l-drivers/imx.rst. I realize there is collision here with the recent patch series posted by Philipp, particularly around the video multiplexer and mipi csi-2 receiver subdevs and OF graphs, as well as v4l2 capture drivers. Philipp Zabel (1): ARM: dts: imx6qdl: add video capture devices and connections Steve Longerbeam (11): ARM: dts: imx6qdl: Add compatible, clocks, irqs to MIPI CSI-2 node ARM: dts: imx6qdl: rename ipu client nodes ARM: dts: imx6-sabrelite: add OV5642 and OV5640 camera sensors ARM: dts: imx6-sabresd: add OV5642 and OV5640 camera sensors ARM: dts: imx6-sabreauto: create i2cmux for i2c3 ARM: dts: imx6-sabreauto: add reset-gpios property for max7310_b ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture ARM: dts: imx6-sabreauto: add the ADV7180 video decoder gpu: ipu-v3: Add ipu_unit_type enumeration gpu: ipu-v3: lookup ipu client nodes by name gpu: ipu-v3: Add smfc and ic client devices arch/arm/boot/dts/imx6dl-sabrelite.dts | 5 + arch/arm/boot/dts/imx6dl-sabresd.dts | 5 + arch/arm/boot/dts/imx6dl.dtsi| 190 arch/arm/boot/dts/imx6q-sabrelite.dts| 6 + arch/arm/boot/dts/imx6q-sabresd.dts | 5 + arch/arm/boot/dts/imx6q.dtsi | 497 ++- arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 148 +++-- arch/arm/boot/dts/imx6qdl-sabrelite.dtsi | 122 +++- arch/arm/boot/dts/imx6qdl-sabresd.dtsi | 114 ++- arch/arm/boot/dts/imx6qdl.dtsi | 385 +++- drivers/gpu/ipu-v3/ipu-common.c | 142 - include/video/imx-ipu-v3.h | 21 ++ 12 files changed, 1593 insertions(+), 47 deletions(-) -- 2.7.4
[PATCH v4 3/4] gpu: ipu-ic: Add complete image conversion support with tiling
On 09/16/2016 07:16 AM, Philipp Zabel wrote: > Hi Steve, > > thanks for the update. > > Am Mittwoch, den 14.09.2016, 18:45 -0700 schrieb Steve Longerbeam: >> I added comment headers for all the image conversion prototypes. >> It caused bloat in imx-ipu-v3.h, so I moved it to a new header: >> include/video/imx-image-convert.h, but let me know if we should put >> this somewhere else and/or under Documentation/ somewhere. > I think that is the right place already. imx-image-convert.h could be > renamed to imx-ipu-image-convert.h, to make clear that this is about the > IPU image converter. Ok, I'll send another update with the name change in the next version (v7). > >>>> +#define MIN_W 128 >>>> +#define MIN_H 128 >>> Where does this minimum come from? >> Nowhere really :) This is just some sane minimums, to pass >> to clamp_align() when aligning input/output width/height in >> ipu_image_convert_adjust(). > Let's use hardware minimum in the low level code. Sane defaults are for > the V4L2 API. Would that be 8x2 pixels per input tile? I searched the imx6 reference manual, I can't find any mention of width/height minimums for the IC. So I suppose 8x2 would be fine, or maybe 16x8, to account for planar and IRT conversions. > >>>> + if (ipu_rot_mode_is_irt(ctx->rot_mode)) { >>>> + /* this is a rotation operation, just ignore */ >>>> + spin_unlock_irqrestore(>irqlock, flags); >>>> + return IRQ_HANDLED; >>>> + } >>> Why enable the out_chan EOF irq at all when using the IRT mode? >> Because (see above), all the IPU resources that might be needed >> for any conversion context that is queued to a image conversion >> channel (IC task) are acquired when the first context is queued, >> including rotation resources. So by acquiring the non-rotation EOF >> irq, it will get fielded even for rotation conversions, so we have to >> handle it. > There is nothing wrong with acquiring the irq. It could still be > disabled while it is not needed. It would be difficult to disable the irq. Remember the irq handlers must field all EOF interrupts in an ipu_image_convert_chan (IC task). If one context in that channel disables the irq, it will break other runnings contexts in that channel that are using it. > >>>> +/* Adjusts input/output images to IPU restrictions */ >>>> +int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, >>>> + enum ipu_rotate_mode rot_mode) >>>> +{ >>>> + const struct ipu_ic_pixfmt *infmt, *outfmt; >>>> + unsigned int num_in_rows, num_in_cols; >>>> + unsigned int num_out_rows, num_out_cols; >>>> + u32 w_align, h_align; >>>> + >>>> + infmt = ipu_ic_get_format(in->pix.pixelformat); >>>> + outfmt = ipu_ic_get_format(out->pix.pixelformat); >>>> + >>>> + /* set some defaults if needed */ >>> Is this our task at all? >> ipu_image_convert_adjust() is meant to be called by v4l2 try_format(), >> which should never return EINVAL but should return a supported format >> when the passed format is not supported. So I added this here to return >> some default pixel formats and width/heights if needed. > I'd prefer to move this into the mem2mem driver try_format, then. We could move the 0 width/height checks to v4l2, but the pixel format defaults should probably remain in ipu-image-convert, since it knows what formats it supports converting to/from. Steve
[PATCH v6 3/3] gpu: ipu-v3: Add queued image conversion support
This patch implements image conversion support using the IC tasks, with tiling to support scaling to and from images up to 4096x4096. Image rotation is also supported. Image conversion requests are added to a run queue under the IC tasks. The internal API is subsystem agnostic (no V4L2 dependency except for the use of V4L2 fourcc pixel formats). Callers prepare for image conversion by calling ipu_image_convert_prepare(), which initializes the parameters of the conversion. The caller passes in the ipu and IC task to use for the conversion, the input and output image formats, a rotation mode, and a completion callback and completion context pointer: struct ipu_image_converter_ctx * ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task, struct ipu_image *in, struct ipu_image *out, enum ipu_rotate_mode rot_mode, ipu_image_converter_cb_t complete, void *complete_context); A new conversion context is created that is added to an IC task context queue. The caller is given the new conversion context, which can then be passed to the further APIs: int ipu_image_convert_queue(struct ipu_image_converter_run *run); This queues the given image conversion request run to a run queue, and starts the conversion immediately if the run queue is empty. Only the physaddr's of the input and output image buffers are needed, since the conversion context was created previously with ipu_image_convert_prepare(). When the conversion completes, the run pointer is returned to the completion callback. void ipu_image_convert_abort(struct ipu_image_converter_ctx *ctx); This will abort any active or pending conversions for this context. Any currently active or pending runs belonging to this context are returned via the completion callback with an error status. void ipu_image_convert_unprepare(struct ipu_image_converter_ctx *ctx); Unprepares the conversion context. Any active or pending runs will be aborted by calling ipu_image_convert_abort(). Signed-off-by: Steve Longerbeam --- v6: - fixed call to ipu_image_convert_adjust() in ipu_image_convert_verify(). v5: - moved all the ipu_image_convert support to ipu-image-convert.c. Prototypes moved to include/video/imx-image-convert.h with comment headers. - removed the IC_TASK_ENCODE channels from image_convert_dma_chan[] list. The encode task cannnot be used by the image converter, since this task has no memory source IDMAC channels (buffers come directly from CSI). - renamed some structs and a few of the exported APIs: struct image_converter is renamed ipu_image_convert_chan, ipu_image_convert_run() is renamed ipu_image_convert_queue(). - the ipu_ic handle passed to ipu_image_convert_prepare() is replaced by the IPU handle and IC task that are requested for carrying out the conversion. The image converter acquires the ipu_ic handle internally, in get_ipu_resources() along with the other resources required. - removed .name field in the supported pixel format list, and ipu_image_convert_enum_format() no longer takes a char* desc arg. - y_depth in 'struct ipu_image_pixfmt' replaced with boolean planar (Y pixel depth is always 8 bits). - removed dmabuf reallocation in alloc_dma_buf(). - added lockdep_assert_held() to functions that require the irqlock. - added IC task number to dev_dbg() output. - exported 'struct ipu_image_convert_run'. ipu_image_convert_queue() now takes a run pointer. Caller must dynamically allocate and fill the run with a conversion context handle and the input/output buffer dma addresses, and free the run object when it is returned in the completion callback. - ipu_image_convert_adjust() is now a void. v4: - do away with struct ipu_ic_tile_off, and move tile offsets into struct ipu_ic_tile. This paves the way for possibly allowing for each tile to have different dimensions in the future. v3: no changes v2: no changes --- drivers/gpu/ipu-v3/Makefile|3 +- drivers/gpu/ipu-v3/ipu-common.c|9 + drivers/gpu/ipu-v3/ipu-image-convert.c | 1718 drivers/gpu/ipu-v3/ipu-prv.h |5 + include/video/imx-image-convert.h | 207 5 files changed, 1941 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/ipu-v3/ipu-image-convert.c create mode 100644 include/video/imx-image-convert.h diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile index 107ec23..8ac08e3 100644 --- a/drivers/gpu/ipu-v3/Makefile +++ b/drivers/gpu/ipu-v3/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ - ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o + ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-image-convert.o \ + ipu-smfc.o diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index fe389dd..a1219fb