diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2014-10-03 22:24:46 +0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2014-10-03 22:24:46 +0400 |
commit | 447a8b858e4bda41c394b1bc7fdbc9dc0bdf44f6 (patch) | |
tree | 676e741f2552c9cb301e1e49c557b92bf8940f55 /drivers/media/platform/vsp1 | |
parent | 3049683eafdbbbd7350b0e5ca02a2d8c026a3362 (diff) | |
parent | 042e1c79166b9250edd8262bea84e1703f27ad2e (diff) | |
download | linux-447a8b858e4bda41c394b1bc7fdbc9dc0bdf44f6.tar.xz |
Merge branch 'next' into for-linus
Prepare first round of input updates for 3.18.
Diffstat (limited to 'drivers/media/platform/vsp1')
-rw-r--r-- | drivers/media/platform/vsp1/vsp1.h | 14 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_bru.c | 85 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_bru.h | 9 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_drv.c | 22 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_entity.c | 42 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_entity.h | 10 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_regs.h | 2 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_rpf.c | 72 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_rwpf.h | 2 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_sru.c | 107 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_sru.h | 1 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_uds.c | 63 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_uds.h | 6 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_video.c | 219 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_video.h | 11 | ||||
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_wpf.c | 72 |
16 files changed, 558 insertions, 179 deletions
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 6ca2cf20d545..12467191dff4 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -36,9 +36,9 @@ struct vsp1_rwpf; struct vsp1_sru; struct vsp1_uds; -#define VPS1_MAX_RPF 5 -#define VPS1_MAX_UDS 3 -#define VPS1_MAX_WPF 4 +#define VSP1_MAX_RPF 5 +#define VSP1_MAX_UDS 3 +#define VSP1_MAX_WPF 4 struct vsp1_device { struct device *dev; @@ -55,10 +55,10 @@ struct vsp1_device { struct vsp1_hsit *hst; struct vsp1_lif *lif; struct vsp1_lut *lut; - struct vsp1_rwpf *rpf[VPS1_MAX_RPF]; + struct vsp1_rwpf *rpf[VSP1_MAX_RPF]; struct vsp1_sru *sru; - struct vsp1_uds *uds[VPS1_MAX_UDS]; - struct vsp1_rwpf *wpf[VPS1_MAX_WPF]; + struct vsp1_uds *uds[VSP1_MAX_UDS]; + struct vsp1_rwpf *wpf[VSP1_MAX_WPF]; struct list_head entities; @@ -66,7 +66,7 @@ struct vsp1_device { struct media_device media_dev; }; -struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1); +int vsp1_device_get(struct vsp1_device *vsp1); void vsp1_device_put(struct vsp1_device *vsp1); static inline u32 vsp1_read(struct vsp1_device *vsp1, u32 reg) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index f80695480060..a0c1984c733e 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -18,6 +18,7 @@ #include "vsp1.h" #include "vsp1_bru.h" +#include "vsp1_rwpf.h" #define BRU_MIN_SIZE 4U #define BRU_MAX_SIZE 8190U @@ -37,19 +38,47 @@ static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data) } /* ----------------------------------------------------------------------------- - * V4L2 Subdevice Core Operations + * Controls */ -static bool bru_is_input_enabled(struct vsp1_bru *bru, unsigned int input) +static int bru_s_ctrl(struct v4l2_ctrl *ctrl) { - return media_entity_remote_pad(&bru->entity.pads[input]) != NULL; + struct vsp1_bru *bru = + container_of(ctrl->handler, struct vsp1_bru, ctrls); + + if (!vsp1_entity_is_streaming(&bru->entity)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BG_COLOR: + vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, ctrl->val | + (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT)); + break; + } + + return 0; } +static const struct v4l2_ctrl_ops bru_ctrl_ops = { + .s_ctrl = bru_s_ctrl, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + static int bru_s_stream(struct v4l2_subdev *subdev, int enable) { + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity); struct vsp1_bru *bru = to_bru(subdev); struct v4l2_mbus_framefmt *format; + unsigned int flags; unsigned int i; + int ret; + + ret = vsp1_entity_set_streaming(&bru->entity, enable); + if (ret < 0) + return ret; if (!enable) return 0; @@ -62,18 +91,19 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) * to sane default values for now. */ - /* Disable both color data normalization and dithering. */ - vsp1_bru_write(bru, VI6_BRU_INCTRL, 0); - - /* Set the background position to cover the whole output image and - * set its color to opaque black. + /* Disable dithering and enable color data normalization unless the + * format at the pipeline output is premultiplied. */ + flags = pipe->output ? pipe->output->video.format.flags : 0; + vsp1_bru_write(bru, VI6_BRU_INCTRL, + flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ? + 0 : VI6_BRU_INCTRL_NRM); + + /* Set the background position to cover the whole output image. */ vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE, (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) | (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT)); vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0); - vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, - 0xff << VI6_BRU_VIRRPF_COL_A_SHIFT); /* Route BRU input 1 as SRC input to the ROP unit and configure the ROP * unit with a NOP operation to make BRU input 1 available as the @@ -84,6 +114,7 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) VI6_BRU_ROP_AROP(VI6_ROP_NOP)); for (i = 0; i < 4; ++i) { + bool premultiplied = false; u32 ctrl = 0; /* Configure all Blend/ROP units corresponding to an enabled BRU @@ -91,11 +122,15 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) * disabled BRU inputs are used in ROP NOP mode to ignore the * SRC input. */ - if (bru_is_input_enabled(bru, i)) + if (bru->inputs[i].rpf) { ctrl |= VI6_BRU_CTRL_RBC; - else + + premultiplied = bru->inputs[i].rpf->video.format.flags + & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; + } else { ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP) | VI6_BRU_CTRL_AROP(VI6_ROP_NOP); + } /* Select the virtual RPF as the Blend/ROP unit A DST input to * serve as a background color. @@ -117,10 +152,18 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) * * DSTc = DSTc * (1 - SRCa) + SRCc * SRCa * DSTa = DSTa * (1 - SRCa) + SRCa + * + * when the SRC input isn't premultiplied, and to + * + * DSTc = DSTc * (1 - SRCa) + SRCc + * DSTa = DSTa * (1 - SRCa) + SRCa + * + * otherwise. */ vsp1_bru_write(bru, VI6_BRU_BLD(i), VI6_BRU_BLD_CCMDX_255_SRC_A | - VI6_BRU_BLD_CCMDY_SRC_A | + (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY : + VI6_BRU_BLD_CCMDY_SRC_A) | VI6_BRU_BLD_ACMDX_255_SRC_A | VI6_BRU_BLD_ACMDY_COEFY | (0xff << VI6_BRU_BLD_COEFY_SHIFT)); @@ -192,7 +235,7 @@ static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, case V4L2_SUBDEV_FORMAT_TRY: return v4l2_subdev_get_try_crop(fh, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: - return &bru->compose[pad]; + return &bru->inputs[pad].compose; default: return NULL; } @@ -391,5 +434,19 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1) vsp1_entity_init_formats(subdev, NULL); + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&bru->ctrls, 1); + v4l2_ctrl_new_std(&bru->ctrls, &bru_ctrl_ops, V4L2_CID_BG_COLOR, + 0, 0xffffff, 1, 0); + + bru->entity.subdev.ctrl_handler = &bru->ctrls; + + if (bru->ctrls.error) { + dev_err(vsp1->dev, "bru: failed to initialize controls\n"); + ret = bru->ctrls.error; + vsp1_entity_destroy(&bru->entity); + return ERR_PTR(ret); + } + return bru; } diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h index 37062704dbf6..16b1c6554911 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.h +++ b/drivers/media/platform/vsp1/vsp1_bru.h @@ -14,11 +14,13 @@ #define __VSP1_BRU_H__ #include <media/media-entity.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-subdev.h> #include "vsp1_entity.h" struct vsp1_device; +struct vsp1_rwpf; #define BRU_PAD_SINK(n) (n) #define BRU_PAD_SOURCE 4 @@ -26,7 +28,12 @@ struct vsp1_device; struct vsp1_bru { struct vsp1_entity entity; - struct v4l2_rect compose[4]; + struct v4l2_ctrl_handler ctrls; + + struct { + struct vsp1_rwpf *rpf; + struct v4l2_rect compose; + } inputs[4]; }; static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index c69ee0657f75..3e6601b5b4de 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -345,36 +345,32 @@ static int vsp1_device_init(struct vsp1_device *vsp1) * Increment the VSP1 reference count and initialize the device if the first * reference is taken. * - * Return a pointer to the VSP1 device or NULL if an error occurred. + * Return 0 on success or a negative error code otherwise. */ -struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1) +int vsp1_device_get(struct vsp1_device *vsp1) { - struct vsp1_device *__vsp1 = vsp1; - int ret; + int ret = 0; mutex_lock(&vsp1->lock); if (vsp1->ref_count > 0) goto done; ret = clk_prepare_enable(vsp1->clock); - if (ret < 0) { - __vsp1 = NULL; + if (ret < 0) goto done; - } ret = vsp1_device_init(vsp1); if (ret < 0) { clk_disable_unprepare(vsp1->clock); - __vsp1 = NULL; goto done; } done: - if (__vsp1) + if (!ret) vsp1->ref_count++; mutex_unlock(&vsp1->lock); - return __vsp1; + return ret; } /* @@ -440,19 +436,19 @@ static int vsp1_validate_platform_data(struct platform_device *pdev, return -EINVAL; } - if (pdata->rpf_count <= 0 || pdata->rpf_count > VPS1_MAX_RPF) { + if (pdata->rpf_count <= 0 || pdata->rpf_count > VSP1_MAX_RPF) { dev_err(&pdev->dev, "invalid number of RPF (%u)\n", pdata->rpf_count); return -EINVAL; } - if (pdata->uds_count <= 0 || pdata->uds_count > VPS1_MAX_UDS) { + if (pdata->uds_count <= 0 || pdata->uds_count > VSP1_MAX_UDS) { dev_err(&pdev->dev, "invalid number of UDS (%u)\n", pdata->uds_count); return -EINVAL; } - if (pdata->wpf_count <= 0 || pdata->wpf_count > VPS1_MAX_WPF) { + if (pdata->wpf_count <= 0 || pdata->wpf_count > VSP1_MAX_WPF) { dev_err(&pdev->dev, "invalid number of WPF (%u)\n", pdata->wpf_count); return -EINVAL; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 44167834285d..79af71d5e270 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -20,6 +20,42 @@ #include "vsp1.h" #include "vsp1_entity.h" +#include "vsp1_video.h" + +bool vsp1_entity_is_streaming(struct vsp1_entity *entity) +{ + bool streaming; + + mutex_lock(&entity->lock); + streaming = entity->streaming; + mutex_unlock(&entity->lock); + + return streaming; +} + +int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) +{ + int ret; + + mutex_lock(&entity->lock); + entity->streaming = streaming; + mutex_unlock(&entity->lock); + + if (!streaming) + return 0; + + if (!entity->subdev.ctrl_handler) + return 0; + + ret = v4l2_ctrl_handler_setup(entity->subdev.ctrl_handler); + if (ret < 0) { + mutex_lock(&entity->lock); + entity->streaming = false; + mutex_unlock(&entity->lock); + } + + return ret; +} /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations @@ -157,6 +193,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, if (i == ARRAY_SIZE(vsp1_routes)) return -EINVAL; + mutex_init(&entity->lock); + entity->vsp1 = vsp1; entity->source_pad = num_pads - 1; @@ -185,7 +223,11 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, void vsp1_entity_destroy(struct vsp1_entity *entity) { + if (entity->video) + vsp1_video_cleanup(entity->video); if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); media_entity_cleanup(&entity->subdev.entity); + + mutex_destroy(&entity->lock); } diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 7afbd8a7ba66..aa20aaa58208 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -14,10 +14,12 @@ #define __VSP1_ENTITY_H__ #include <linux/list.h> +#include <linux/mutex.h> #include <media/v4l2-subdev.h> struct vsp1_device; +struct vsp1_video; enum vsp1_entity_type { VSP1_ENTITY_BRU, @@ -68,6 +70,11 @@ struct vsp1_entity { struct v4l2_subdev subdev; struct v4l2_mbus_framefmt *formats; + + struct vsp1_video *video; + + struct mutex lock; /* Protects the streaming field */ + bool streaming; }; static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) @@ -89,4 +96,7 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, void vsp1_entity_init_formats(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh); +bool vsp1_entity_is_streaming(struct vsp1_entity *entity); +int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming); + #endif /* __VSP1_ENTITY_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 3e74b44286f6..55f163d32d15 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -336,7 +336,9 @@ */ #define VI6_SRU_CTRL0 0x2200 +#define VI6_SRU_CTRL0_PARAM0_MASK (0x1ff << 16) #define VI6_SRU_CTRL0_PARAM0_SHIFT 16 +#define VI6_SRU_CTRL0_PARAM1_MASK (0x1f << 8) #define VI6_SRU_CTRL0_PARAM1_SHIFT 8 #define VI6_SRU_CTRL0_MODE_UPSCALE (4 << 4) #define VI6_SRU_CTRL0_PARAM2 (1 << 3) diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index c3d98642a4aa..d14d26b718ef 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -39,6 +39,36 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data) } /* ----------------------------------------------------------------------------- + * Controls + */ + +static int rpf_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vsp1_rwpf *rpf = + container_of(ctrl->handler, struct vsp1_rwpf, ctrls); + struct vsp1_pipeline *pipe; + + if (!vsp1_entity_is_streaming(&rpf->entity)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ALPHA_COMPONENT: + vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, + ctrl->val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); + + pipe = to_vsp1_pipeline(&rpf->entity.subdev.entity); + vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, ctrl->val); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops rpf_ctrl_ops = { + .s_ctrl = rpf_s_ctrl, +}; + +/* ----------------------------------------------------------------------------- * V4L2 Subdevice Core Operations */ @@ -50,6 +80,11 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) const struct v4l2_rect *crop = &rpf->crop; u32 pstride; u32 infmt; + int ret; + + ret = vsp1_entity_set_streaming(&rpf->entity, enable); + if (ret < 0) + return ret; if (!enable) return 0; @@ -101,12 +136,13 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) (rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) | (rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT)); - /* Disable alpha, mask and color key. Set the alpha channel to a fixed - * value of 255. + /* Use the alpha channel (extended to 8 bits) when available or an + * alpha value set through the V4L2_CID_ALPHA_COMPONENT control + * otherwise. Disable color keying. */ - vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_ASEL_FIXED); - vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, - 255 << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); + vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | + (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED + : VI6_RPF_ALPH_SEL_ASEL_FIXED)); vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0); vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0); @@ -196,6 +232,20 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) vsp1_entity_init_formats(subdev, NULL); + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&rpf->ctrls, 1); + v4l2_ctrl_new_std(&rpf->ctrls, &rpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, + 0, 255, 1, 255); + + rpf->entity.subdev.ctrl_handler = &rpf->ctrls; + + if (rpf->ctrls.error) { + dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n", + index); + ret = rpf->ctrls.error; + goto error; + } + /* Initialize the video device. */ video = &rpf->video; @@ -205,7 +255,9 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) ret = vsp1_video_init(video, &rpf->entity); if (ret < 0) - goto error_video; + goto error; + + rpf->entity.video = video; /* Connect the video device to the RPF. */ ret = media_entity_create_link(&rpf->video.video.entity, 0, @@ -214,13 +266,11 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); if (ret < 0) - goto error_link; + goto error; return rpf; -error_link: - vsp1_video_cleanup(video); -error_video: - media_entity_cleanup(&rpf->entity.subdev.entity); +error: + vsp1_entity_destroy(&rpf->entity); return ERR_PTR(ret); } diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index b4fb65e58770..28dd9e7b3838 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -14,6 +14,7 @@ #define __VSP1_RWPF_H__ #include <media/media-entity.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-subdev.h> #include "vsp1.h" @@ -26,6 +27,7 @@ struct vsp1_rwpf { struct vsp1_entity entity; struct vsp1_video video; + struct v4l2_ctrl_handler ctrls; unsigned int max_width; unsigned int max_height; diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index aa0e04c56f3f..b7d3c8b9f189 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -42,38 +42,6 @@ static inline void vsp1_sru_write(struct vsp1_sru *sru, u32 reg, u32 data) #define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE + 1) -static int sru_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct vsp1_sru *sru = - container_of(ctrl->handler, struct vsp1_sru, ctrls); - - switch (ctrl->id) { - case V4L2_CID_VSP1_SRU_INTENSITY: - sru->intensity = ctrl->val; - break; - } - - return 0; -} - -static const struct v4l2_ctrl_ops sru_ctrl_ops = { - .s_ctrl = sru_s_ctrl, -}; - -static const struct v4l2_ctrl_config sru_intensity_control = { - .ops = &sru_ctrl_ops, - .id = V4L2_CID_VSP1_SRU_INTENSITY, - .name = "Intensity", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = 1, - .max = 6, - .step = 1, -}; - -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Core Operations - */ - struct vsp1_sru_param { u32 ctrl0; u32 ctrl2; @@ -110,22 +78,66 @@ static const struct vsp1_sru_param vsp1_sru_params[] = { }, }; +static int sru_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vsp1_sru *sru = + container_of(ctrl->handler, struct vsp1_sru, ctrls); + const struct vsp1_sru_param *param; + u32 value; + + switch (ctrl->id) { + case V4L2_CID_VSP1_SRU_INTENSITY: + param = &vsp1_sru_params[ctrl->val - 1]; + + value = vsp1_sru_read(sru, VI6_SRU_CTRL0); + value &= ~(VI6_SRU_CTRL0_PARAM0_MASK | + VI6_SRU_CTRL0_PARAM1_MASK); + value |= param->ctrl0; + vsp1_sru_write(sru, VI6_SRU_CTRL0, value); + + vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops sru_ctrl_ops = { + .s_ctrl = sru_s_ctrl, +}; + +static const struct v4l2_ctrl_config sru_intensity_control = { + .ops = &sru_ctrl_ops, + .id = V4L2_CID_VSP1_SRU_INTENSITY, + .name = "Intensity", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 1, + .max = 6, + .def = 1, + .step = 1, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + static int sru_s_stream(struct v4l2_subdev *subdev, int enable) { struct vsp1_sru *sru = to_sru(subdev); - const struct vsp1_sru_param *param; struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - bool upscale; u32 ctrl0; + int ret; + + ret = vsp1_entity_set_streaming(&sru->entity, enable); + if (ret < 0) + return ret; if (!enable) return 0; input = &sru->entity.formats[SRU_PAD_SINK]; output = &sru->entity.formats[SRU_PAD_SOURCE]; - upscale = input->width != output->width; - param = &vsp1_sru_params[sru->intensity]; if (input->code == V4L2_MBUS_FMT_ARGB8888_1X32) ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 @@ -133,10 +145,18 @@ static int sru_s_stream(struct v4l2_subdev *subdev, int enable) else ctrl0 = VI6_SRU_CTRL0_PARAM3; - vsp1_sru_write(sru, VI6_SRU_CTRL0, param->ctrl0 | ctrl0 | - (upscale ? VI6_SRU_CTRL0_MODE_UPSCALE : 0)); + if (input->width != output->width) + ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE; + + /* Take the control handler lock to ensure that the CTRL0 value won't be + * changed behind our back by a set control operation. + */ + mutex_lock(sru->ctrls.lock); + ctrl0 |= vsp1_sru_read(sru, VI6_SRU_CTRL0) + & (VI6_SRU_CTRL0_PARAM0_MASK | VI6_SRU_CTRL0_PARAM1_MASK); + mutex_unlock(sru->ctrls.lock); + vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); - vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2); return 0; } @@ -348,8 +368,15 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) /* Initialize the control handler. */ v4l2_ctrl_handler_init(&sru->ctrls, 1); v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL); - v4l2_ctrl_handler_setup(&sru->ctrls); + sru->entity.subdev.ctrl_handler = &sru->ctrls; + if (sru->ctrls.error) { + dev_err(vsp1->dev, "sru: failed to initialize controls\n"); + ret = sru->ctrls.error; + vsp1_entity_destroy(&sru->entity); + return ERR_PTR(ret); + } + return sru; } diff --git a/drivers/media/platform/vsp1/vsp1_sru.h b/drivers/media/platform/vsp1/vsp1_sru.h index 381870b74780..b6768bf3dc47 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.h +++ b/drivers/media/platform/vsp1/vsp1_sru.h @@ -28,7 +28,6 @@ struct vsp1_sru { struct vsp1_entity entity; struct v4l2_ctrl_handler ctrls; - unsigned int intensity; }; static inline struct vsp1_sru *to_sru(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 0293bdbb4401..de92ef4944b3 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -45,6 +45,11 @@ static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data) * Scaling Computation */ +void vsp1_uds_set_alpha(struct vsp1_uds *uds, unsigned int alpha) +{ + vsp1_uds_write(uds, VI6_UDS_ALPVAL, alpha << VI6_UDS_ALPVAL_VAL0_SHIFT); +} + /* * uds_output_size - Return the output size for an input size and scaling ratio * @input: input size in pixels @@ -105,49 +110,56 @@ static unsigned int uds_compute_ratio(unsigned int input, unsigned int output) return (input - 1) * 4096 / (output - 1); } -static void uds_compute_ratios(struct vsp1_uds *uds) -{ - struct v4l2_mbus_framefmt *input = &uds->entity.formats[UDS_PAD_SINK]; - struct v4l2_mbus_framefmt *output = - &uds->entity.formats[UDS_PAD_SOURCE]; - - uds->hscale = uds_compute_ratio(input->width, output->width); - uds->vscale = uds_compute_ratio(input->height, output->height); - - dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", - uds->hscale, uds->vscale); -} - /* ----------------------------------------------------------------------------- * V4L2 Subdevice Core Operations */ static int uds_s_stream(struct v4l2_subdev *subdev, int enable) { - const struct v4l2_mbus_framefmt *format; struct vsp1_uds *uds = to_uds(subdev); + const struct v4l2_mbus_framefmt *output; + const struct v4l2_mbus_framefmt *input; + unsigned int hscale; + unsigned int vscale; + bool multitap; if (!enable) return 0; - /* Enable multi-tap scaling. */ - vsp1_uds_write(uds, VI6_UDS_CTRL, VI6_UDS_CTRL_AON | VI6_UDS_CTRL_BC); + input = &uds->entity.formats[UDS_PAD_SINK]; + output = &uds->entity.formats[UDS_PAD_SOURCE]; + + hscale = uds_compute_ratio(input->width, output->width); + vscale = uds_compute_ratio(input->height, output->height); + + dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", hscale, vscale); + + /* Multi-tap scaling can't be enabled along with alpha scaling when + * scaling down with a factor lower than or equal to 1/2 in either + * direction. + */ + if (uds->scale_alpha && (hscale >= 8192 || vscale >= 8192)) + multitap = false; + else + multitap = true; + + vsp1_uds_write(uds, VI6_UDS_CTRL, + (uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) | + (multitap ? VI6_UDS_CTRL_BC : 0)); vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH, - (uds_passband_width(uds->hscale) + (uds_passband_width(hscale) << VI6_UDS_PASS_BWIDTH_H_SHIFT) | - (uds_passband_width(uds->vscale) + (uds_passband_width(vscale) << VI6_UDS_PASS_BWIDTH_V_SHIFT)); /* Set the scaling ratios and the output size. */ - format = &uds->entity.formats[UDS_PAD_SOURCE]; - vsp1_uds_write(uds, VI6_UDS_SCALE, - (uds->hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | - (uds->vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); + (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | + (vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE, - (format->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | - (format->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); + (output->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | + (output->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); return 0; } @@ -280,9 +292,6 @@ static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, uds_try_format(uds, fh, UDS_PAD_SOURCE, format, fmt->which); } - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) - uds_compute_ratios(uds); - return 0; } diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h index 479d12df1180..031ac0da1b66 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.h +++ b/drivers/media/platform/vsp1/vsp1_uds.h @@ -25,9 +25,7 @@ struct vsp1_device; struct vsp1_uds { struct vsp1_entity entity; - - unsigned int hscale; - unsigned int vscale; + bool scale_alpha; }; static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev) @@ -37,4 +35,6 @@ static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev) struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index); +void vsp1_uds_set_alpha(struct vsp1_uds *uds, unsigned int alpha); + #endif /* __VSP1_UDS_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 8a1253e51f04..915a20eb003e 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -31,6 +31,7 @@ #include "vsp1_bru.h" #include "vsp1_entity.h" #include "vsp1_rwpf.h" +#include "vsp1_uds.h" #include "vsp1_video.h" #define VSP1_VIDEO_DEF_FORMAT V4L2_PIX_FMT_YUYV @@ -50,70 +51,85 @@ static const struct vsp1_format_info vsp1_video_formats[] = { { V4L2_PIX_FMT_RGB332, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 8, 0, 0 }, false, false, 1, 1 }, - { V4L2_PIX_FMT_RGB444, V4L2_MBUS_FMT_ARGB8888_1X32, + 1, { 8, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_ARGB444, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_XRGB444, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1 }, - { V4L2_PIX_FMT_RGB555, V4L2_MBUS_FMT_ARGB8888_1X32, + 1, { 16, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_ARGB555, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_XRGB555, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1 }, + 1, { 16, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_RGB565, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_RGB_565, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1 }, + 1, { 16, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_BGR24, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_BGR_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 24, 0, 0 }, false, false, 1, 1 }, + 1, { 24, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_RGB24, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 24, 0, 0 }, false, false, 1, 1 }, - { V4L2_PIX_FMT_BGR32, V4L2_MBUS_FMT_ARGB8888_1X32, + 1, { 24, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_ABGR32, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, + 1, { 32, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_XBGR32, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, - 1, { 32, 0, 0 }, false, false, 1, 1 }, - { V4L2_PIX_FMT_RGB32, V4L2_MBUS_FMT_ARGB8888_1X32, + 1, { 32, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_ARGB32, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 32, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_XRGB32, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 32, 0, 0 }, false, false, 1, 1 }, + 1, { 32, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_UYVY, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, false, false, 2, 1 }, + 1, { 16, 0, 0 }, false, false, 2, 1, false }, { V4L2_PIX_FMT_VYUY, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, false, true, 2, 1 }, + 1, { 16, 0, 0 }, false, true, 2, 1, false }, { V4L2_PIX_FMT_YUYV, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, true, false, 2, 1 }, + 1, { 16, 0, 0 }, true, false, 2, 1, false }, { V4L2_PIX_FMT_YVYU, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, true, true, 2, 1 }, + 1, { 16, 0, 0 }, true, true, 2, 1, false }, { V4L2_PIX_FMT_NV12M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, false, 2, 2 }, + 2, { 8, 16, 0 }, false, false, 2, 2, false }, { V4L2_PIX_FMT_NV21M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, true, 2, 2 }, + 2, { 8, 16, 0 }, false, true, 2, 2, false }, { V4L2_PIX_FMT_NV16M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, false, 2, 1 }, + 2, { 8, 16, 0 }, false, false, 2, 1, false }, { V4L2_PIX_FMT_NV61M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, true, 2, 1 }, + 2, { 8, 16, 0 }, false, true, 2, 1, false }, { V4L2_PIX_FMT_YUV420M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 3, { 8, 8, 8 }, false, false, 2, 2 }, + 3, { 8, 8, 8 }, false, false, 2, 2, false }, }; /* @@ -181,11 +197,29 @@ static int __vsp1_video_try_format(struct vsp1_video *video, struct v4l2_pix_format_mplane *pix, const struct vsp1_format_info **fmtinfo) { + static const u32 xrgb_formats[][2] = { + { V4L2_PIX_FMT_RGB444, V4L2_PIX_FMT_XRGB444 }, + { V4L2_PIX_FMT_RGB555, V4L2_PIX_FMT_XRGB555 }, + { V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_XBGR32 }, + { V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_XRGB32 }, + }; + const struct vsp1_format_info *info; unsigned int width = pix->width; unsigned int height = pix->height; unsigned int i; + /* Backward compatibility: replace deprecated RGB formats by their XRGB + * equivalent. This selects the format older userspace applications want + * while still exposing the new format. + */ + for (i = 0; i < ARRAY_SIZE(xrgb_formats); ++i) { + if (xrgb_formats[i][0] == pix->pixelformat) { + pix->pixelformat = xrgb_formats[i][1]; + break; + } + } + /* Retrieve format information and select the default format if the * requested format isn't supported. */ @@ -273,13 +307,14 @@ vsp1_video_format_adjust(struct vsp1_video *video, * Pipeline Management */ -static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, +static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe, + struct vsp1_rwpf *input, struct vsp1_rwpf *output) { struct vsp1_entity *entity; unsigned int entities = 0; struct media_pad *pad; - bool uds_found = false; + bool bru_found = false; input->location.left = 0; input->location.top = 0; @@ -301,10 +336,15 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, */ if (entity->type == VSP1_ENTITY_BRU) { struct vsp1_bru *bru = to_bru(&entity->subdev); - struct v4l2_rect *rect = &bru->compose[pad->index]; + struct v4l2_rect *rect = + &bru->inputs[pad->index].compose; + + bru->inputs[pad->index].rpf = input; input->location.left = rect->left; input->location.top = rect->top; + + bru_found = true; } /* We've reached the WPF, we're done. */ @@ -319,9 +359,12 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, /* UDS can't be chained. */ if (entity->type == VSP1_ENTITY_UDS) { - if (uds_found) + if (pipe->uds) return -EPIPE; - uds_found = true; + + pipe->uds = entity; + pipe->uds_input = bru_found ? pipe->bru + : &input->entity; } /* Follow the source link. The link setup operations ensure @@ -340,6 +383,27 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, return 0; } +static void __vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe) +{ + if (pipe->bru) { + struct vsp1_bru *bru = to_bru(&pipe->bru->subdev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i) + bru->inputs[i].rpf = NULL; + } + + INIT_LIST_HEAD(&pipe->entities); + pipe->state = VSP1_PIPELINE_STOPPED; + pipe->buffers_ready = 0; + pipe->num_video = 0; + pipe->num_inputs = 0; + pipe->output = NULL; + pipe->bru = NULL; + pipe->lif = NULL; + pipe->uds = NULL; +} + static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, struct vsp1_video *video) { @@ -395,7 +459,7 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, * contains no loop and that all branches end at the output WPF. */ for (i = 0; i < pipe->num_inputs; ++i) { - ret = vsp1_pipeline_validate_branch(pipe->inputs[i], + ret = vsp1_pipeline_validate_branch(pipe, pipe->inputs[i], pipe->output); if (ret < 0) goto error; @@ -404,13 +468,7 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, return 0; error: - INIT_LIST_HEAD(&pipe->entities); - pipe->buffers_ready = 0; - pipe->num_video = 0; - pipe->num_inputs = 0; - pipe->output = NULL; - pipe->bru = NULL; - pipe->lif = NULL; + __vsp1_pipeline_cleanup(pipe); return ret; } @@ -441,16 +499,8 @@ static void vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe) mutex_lock(&pipe->lock); /* If we're the last user clean up the pipeline. */ - if (--pipe->use_count == 0) { - INIT_LIST_HEAD(&pipe->entities); - pipe->state = VSP1_PIPELINE_STOPPED; - pipe->buffers_ready = 0; - pipe->num_video = 0; - pipe->num_inputs = 0; - pipe->output = NULL; - pipe->bru = NULL; - pipe->lif = NULL; - } + if (--pipe->use_count == 0) + __vsp1_pipeline_cleanup(pipe); mutex_unlock(&pipe->lock); } @@ -471,7 +521,8 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) int ret; spin_lock_irqsave(&pipe->irqlock, flags); - pipe->state = VSP1_PIPELINE_STOPPING; + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; spin_unlock_irqrestore(&pipe->irqlock, flags); ret = wait_event_timeout(pipe->wq, pipe->state == VSP1_PIPELINE_STOPPED, @@ -479,7 +530,7 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) ret = ret == 0 ? -ETIMEDOUT : 0; list_for_each_entry(entity, &pipe->entities, list_pipe) { - if (entity->route) + if (entity->route && entity->route->reg) vsp1_write(entity->vsp1, entity->route->reg, VI6_DPR_NODE_UNUSED); @@ -576,6 +627,7 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) { + enum vsp1_pipeline_state state; unsigned long flags; unsigned int i; @@ -591,11 +643,13 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) spin_lock_irqsave(&pipe->irqlock, flags); + state = pipe->state; + pipe->state = VSP1_PIPELINE_STOPPED; + /* If a stop has been requested, mark the pipeline as stopped and * return. */ - if (pipe->state == VSP1_PIPELINE_STOPPING) { - pipe->state = VSP1_PIPELINE_STOPPED; + if (state == VSP1_PIPELINE_STOPPING) { wake_up(&pipe->wq); goto done; } @@ -608,6 +662,47 @@ done: spin_unlock_irqrestore(&pipe->irqlock, flags); } +/* + * Propagate the alpha value through the pipeline. + * + * As the UDS has restricted scaling capabilities when the alpha component needs + * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha + * value. The UDS then outputs a fixed alpha value which needs to be programmed + * from the input RPF alpha. + */ +void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, + struct vsp1_entity *input, + unsigned int alpha) +{ + struct vsp1_entity *entity; + struct media_pad *pad; + + pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]); + + while (pad) { + if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); + + /* The BRU background color has a fixed alpha value set to 255, + * the output alpha value is thus always equal to 255. + */ + if (entity->type == VSP1_ENTITY_BRU) + alpha = 255; + + if (entity->type == VSP1_ENTITY_UDS) { + struct vsp1_uds *uds = to_uds(&entity->subdev); + + vsp1_uds_set_alpha(uds, alpha); + break; + } + + pad = &entity->pads[entity->source_pad]; + pad = media_entity_remote_pad(pad); + } +} + /* ----------------------------------------------------------------------------- * videobuf2 Queue Operations */ @@ -654,8 +749,6 @@ static int vsp1_video_buffer_prepare(struct vb2_buffer *vb) if (vb->num_planes < format->num_planes) return -EINVAL; - buf->video = video; - for (i = 0; i < vb->num_planes; ++i) { buf->addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); buf->length[i] = vb2_plane_size(vb, i); @@ -717,6 +810,25 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) mutex_lock(&pipe->lock); if (pipe->stream_count == pipe->num_video - 1) { + if (pipe->uds) { + struct vsp1_uds *uds = to_uds(&pipe->uds->subdev); + + /* If a BRU is present in the pipeline before the UDS, + * the alpha component doesn't need to be scaled as the + * BRU output alpha value is fixed to 255. Otherwise we + * need to scale the alpha component only when available + * at the input RPF. + */ + if (pipe->uds_input->type == VSP1_ENTITY_BRU) { + uds->scale_alpha = false; + } else { + struct vsp1_rwpf *rpf = + to_rwpf(&pipe->uds_input->subdev); + + uds->scale_alpha = rpf->video.fmtinfo->alpha; + } + } + list_for_each_entry(entity, &pipe->entities, list_pipe) { vsp1_entity_route_setup(entity); @@ -744,6 +856,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) { struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); + struct vsp1_video_buffer *buffer; unsigned long flags; int ret; @@ -761,6 +874,8 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) /* Remove all buffers from the IRQ queue. */ spin_lock_irqsave(&video->irqlock, flags); + list_for_each_entry(buffer, &video->irqqueue, queue) + vb2_buffer_done(&buffer->buf, VB2_BUF_STATE_ERROR); INIT_LIST_HEAD(&video->irqqueue); spin_unlock_irqrestore(&video->irqlock, flags); } @@ -950,8 +1065,8 @@ static int vsp1_video_open(struct file *file) file->private_data = vfh; - if (!vsp1_device_get(video->vsp1)) { - ret = -EBUSY; + ret = vsp1_device_get(video->vsp1); + if (ret < 0) { v4l2_fh_del(vfh); kfree(vfh); } diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index c04d48fa2999..fd2851a82e00 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -33,6 +33,7 @@ struct vsp1_video; * @swap_uv: the U and V components are swapped (V comes before U) * @hsub: horizontal subsampling factor * @vsub: vertical subsampling factor + * @alpha: has an alpha channel */ struct vsp1_format_info { u32 fourcc; @@ -45,6 +46,7 @@ struct vsp1_format_info { bool swap_uv; unsigned int hsub; unsigned int vsub; + bool alpha; }; enum vsp1_pipeline_state { @@ -73,10 +75,12 @@ struct vsp1_pipeline { unsigned int num_video; unsigned int num_inputs; - struct vsp1_rwpf *inputs[VPS1_MAX_RPF]; + struct vsp1_rwpf *inputs[VSP1_MAX_RPF]; struct vsp1_rwpf *output; struct vsp1_entity *bru; struct vsp1_entity *lif; + struct vsp1_entity *uds; + struct vsp1_entity *uds_input; struct list_head entities; }; @@ -90,7 +94,6 @@ static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e) } struct vsp1_video_buffer { - struct vsp1_video *video; struct vb2_buffer buf; struct list_head queue; @@ -142,4 +145,8 @@ void vsp1_video_cleanup(struct vsp1_video *video); void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe); +void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, + struct vsp1_entity *input, + unsigned int alpha); + #endif /* __VSP1_VIDEO_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 1294340dcb36..6e057762c933 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -39,22 +39,56 @@ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data) } /* ----------------------------------------------------------------------------- + * Controls + */ + +static int wpf_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vsp1_rwpf *wpf = + container_of(ctrl->handler, struct vsp1_rwpf, ctrls); + u32 value; + + if (!vsp1_entity_is_streaming(&wpf->entity)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ALPHA_COMPONENT: + value = vsp1_wpf_read(wpf, VI6_WPF_OUTFMT); + value &= ~VI6_WPF_OUTFMT_PDV_MASK; + value |= ctrl->val << VI6_WPF_OUTFMT_PDV_SHIFT; + vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, value); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops wpf_ctrl_ops = { + .s_ctrl = wpf_s_ctrl, +}; + +/* ----------------------------------------------------------------------------- * V4L2 Subdevice Core Operations */ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) { + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity); struct vsp1_rwpf *wpf = to_rwpf(subdev); - struct vsp1_pipeline *pipe = - to_vsp1_pipeline(&wpf->entity.subdev.entity); struct vsp1_device *vsp1 = wpf->entity.vsp1; const struct v4l2_rect *crop = &wpf->crop; unsigned int i; u32 srcrpf = 0; u32 outfmt = 0; + int ret; + + ret = vsp1_entity_set_streaming(&wpf->entity, enable); + if (ret < 0) + return ret; if (!enable) { vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0); + vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, 0); return 0; } @@ -99,6 +133,8 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT; + if (fmtinfo->alpha) + outfmt |= VI6_WPF_OUTFMT_PXA; if (fmtinfo->swap_yc) outfmt |= VI6_WPF_OUTFMT_SPYCS; if (fmtinfo->swap_uv) @@ -111,7 +147,13 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) wpf->entity.formats[RWPF_PAD_SOURCE].code) outfmt |= VI6_WPF_OUTFMT_CSC; + /* Take the control handler lock to ensure that the PDV value won't be + * changed behind our back by a set control operation. + */ + mutex_lock(wpf->ctrls.lock); + outfmt |= vsp1_wpf_read(wpf, VI6_WPF_OUTFMT) & VI6_WPF_OUTFMT_PDV_MASK; vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt); + mutex_unlock(wpf->ctrls.lock); vsp1_write(vsp1, VI6_DPR_WPF_FPORCH(wpf->entity.index), VI6_DPR_WPF_FPORCH_FP_WPFN); @@ -207,6 +249,20 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) vsp1_entity_init_formats(subdev, NULL); + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&wpf->ctrls, 1); + v4l2_ctrl_new_std(&wpf->ctrls, &wpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, + 0, 255, 1, 255); + + wpf->entity.subdev.ctrl_handler = &wpf->ctrls; + + if (wpf->ctrls.error) { + dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", + index); + ret = wpf->ctrls.error; + goto error; + } + /* Initialize the video device. */ video = &wpf->video; @@ -216,7 +272,9 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) ret = vsp1_video_init(video, &wpf->entity); if (ret < 0) - goto error_video; + goto error; + + wpf->entity.video = video; /* Connect the video device to the WPF. All connections are immutable * except for the WPF0 source link if a LIF is present. @@ -229,15 +287,13 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) RWPF_PAD_SOURCE, &wpf->video.video.entity, 0, flags); if (ret < 0) - goto error_link; + goto error; wpf->entity.sink = &wpf->video.video.entity; return wpf; -error_link: - vsp1_video_cleanup(video); -error_video: - media_entity_cleanup(&wpf->entity.subdev.entity); +error: + vsp1_entity_destroy(&wpf->entity); return ERR_PTR(ret); } |