aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>2018-11-23 21:22:37 +0530
committerManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>2018-11-23 21:22:37 +0530
commite89b90550f3bfdf483d472e0723bcb8b57ecdd34 (patch)
tree640081094a96332d3a4b94732e881ed23e2c1a4c
parentd68335c072a803e002b21519e5ca27269e8a0bfe (diff)
download96b-common-hikey_v4l2_4.15.tar.gz
media: hisi: ISP: Add video device and some cleanupshikey_v4l2_4.15
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-rw-r--r--drivers/media/i2c/ov5645.c5
-rw-r--r--drivers/media/platform/hisi/isp/Makefile1
-rw-r--r--drivers/media/platform/hisi/isp/isp-csiphy.c8
-rw-r--r--drivers/media/platform/hisi/isp/isp-csiphy.h19
-rw-r--r--drivers/media/platform/hisi/isp/isp-cvdr.c76
-rw-r--r--drivers/media/platform/hisi/isp/isp-cvdr.h5
-rw-r--r--drivers/media/platform/hisi/isp/isp-sr.c15
-rw-r--r--drivers/media/platform/hisi/isp/isp-video.c503
-rw-r--r--drivers/media/platform/hisi/isp/isp-video.h54
-rw-r--r--drivers/media/platform/hisi/isp/isp.c57
-rw-r--r--drivers/media/platform/hisi/isp/isp.h4
11 files changed, 670 insertions, 77 deletions
diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c
index d28845f7356f..2c2c2c4f9d20 100644
--- a/drivers/media/i2c/ov5645.c
+++ b/drivers/media/i2c/ov5645.c
@@ -724,11 +724,13 @@ static int ov5645_s_power(struct v4l2_subdev *sd, int on)
mutex_lock(&ov5645->power_lock);
+ pr_info("%s: %d\n", __func__, __LINE__);
/* If the power count is modified from 0 to != 0 or from != 0 to 0,
* update the power state.
*/
if (ov5645->power_count == !on) {
if (on) {
+ pr_info("%s: %d\n", __func__, __LINE__);
ret = ov5645_set_power_on(ov5645);
if (ret < 0)
goto exit;
@@ -750,6 +752,7 @@ static int ov5645_s_power(struct v4l2_subdev *sd, int on)
goto exit;
}
} else {
+ pr_info("%s: %d\n", __func__, __LINE__);
ov5645_set_power_off(ov5645);
}
}
@@ -1054,6 +1057,7 @@ static int ov5645_s_stream(struct v4l2_subdev *subdev, int enable)
struct ov5645 *ov5645 = to_ov5645(subdev);
int ret;
+ pr_info("%s: %d\n", __func__, __LINE__);
if (enable) {
ret = ov5645_set_register_array(ov5645,
ov5645->current_mode->data,
@@ -1074,6 +1078,7 @@ static int ov5645_s_stream(struct v4l2_subdev *subdev, int enable)
if (ret < 0)
return ret;
} else {
+ pr_info("%s: %d\n", __func__, __LINE__);
ret = ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0,
OV5645_SYSTEM_CTRL0_STOP);
if (ret < 0)
diff --git a/drivers/media/platform/hisi/isp/Makefile b/drivers/media/platform/hisi/isp/Makefile
index 9fa39e3a4867..99f5ba34ea23 100644
--- a/drivers/media/platform/hisi/isp/Makefile
+++ b/drivers/media/platform/hisi/isp/Makefile
@@ -5,6 +5,7 @@ hisi-isp-objs += \
isp.o \
isp-csiphy.o \
isp-sr.o \
+ isp-video.o \
isp-cvdr.o \
obj-$(CONFIG_VIDEO_HISI_ISP) += hisi-isp.o
diff --git a/drivers/media/platform/hisi/isp/isp-csiphy.c b/drivers/media/platform/hisi/isp/isp-csiphy.c
index f6c050fc0814..6c3fae396d04 100644
--- a/drivers/media/platform/hisi/isp/isp-csiphy.c
+++ b/drivers/media/platform/hisi/isp/isp-csiphy.c
@@ -239,6 +239,7 @@ int isp_ispss_reset_all(struct isp *isp)
void isp_ispss_clk_enable(struct isp *isp)
{
+ pr_info("%s: %d\n", __func__, __LINE__);
/* enable all clock of isp sub-modules */
isp_writel(isp->ispss_ctrl, 0x010, 0xffffffff);
isp_writel(isp->ispss_ctrl, 0x014, 0xffffffff);
@@ -263,7 +264,8 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on)
struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
struct isp *isp = csiphy->isp;
int ret = 0;
-
+
+ pr_info("%s: %d\n", __func__, __LINE__);
ret = isp_enable_clocks(isp->clks, isp->dev);
if (ret < 0)
return ret;
@@ -289,10 +291,12 @@ static int csiphy_set_stream(struct v4l2_subdev *sd, int enable)
struct isp *isp = csiphy->isp;
int ret;
+ pr_info("%s: %d\n", __func__, __LINE__);
isp_ispss_reset_all(isp);
mdelay(100);
- ret = csi2if_enable(csiphy, 2, 0);
+ pr_info("%s: Data lane count is: %d\n", __func__, csiphy->cfg.num_data);
+ ret = csi2if_enable(csiphy, csiphy->cfg.num_data, 0);
if (ret < 0)
return ret;
diff --git a/drivers/media/platform/hisi/isp/isp-csiphy.h b/drivers/media/platform/hisi/isp/isp-csiphy.h
index 2382ca2de585..77941bbf6fcd 100644
--- a/drivers/media/platform/hisi/isp/isp-csiphy.h
+++ b/drivers/media/platform/hisi/isp/isp-csiphy.h
@@ -88,25 +88,9 @@
#define LANE3_DESKEW_2 (0x6B)
#define LANE3_DESKEW_3 (0x6C)
-struct csiphy_lane {
- u8 pos;
- u8 pol;
-};
-
-struct csiphy_lanes_cfg {
- int num_data;
- struct csiphy_lane *data;
- struct csiphy_lane clk;
-};
-
-struct csiphy_csi2_cfg {
- struct csiphy_lanes_cfg lane_cfg;
-};
-
struct csiphy_config {
- u8 combo_mode;
u8 csid_id;
- struct csiphy_csi2_cfg *csi2;
+ int num_data;
};
struct csiphy_device;
@@ -146,4 +130,5 @@ int isp_csiphy_register_entity(struct csiphy_device *csiphy,
void isp_csiphy_unregister_entity(struct csiphy_device *csiphy);
+void isp_ispss_clk_enable(struct isp *isp);
#endif /* HISI_ISP_CSIPHY_H */
diff --git a/drivers/media/platform/hisi/isp/isp-cvdr.c b/drivers/media/platform/hisi/isp/isp-cvdr.c
index 666dd016028f..b6a1b2c71d70 100644
--- a/drivers/media/platform/hisi/isp/isp-cvdr.c
+++ b/drivers/media/platform/hisi/isp/isp-cvdr.c
@@ -180,6 +180,7 @@ void isp_cvdr_init(struct cvdr_device *cvdr)
prefetch_bypass = 1;
+ pr_info("%s: %d\n", __func__, __LINE__);
for (i = 0; i < CVDR_VP_WR_NBR; ++i) {
base = ((cvdr_vp_wr_bw[i].srt == CVDR_SRT) ?
cvdr->cvdr_srt : cvdr->cvdr_rt);
@@ -227,8 +228,8 @@ void isp_cvdr_init(struct cvdr_device *cvdr)
}
/*
- * cvdr_set_power - Power on/off CSIPHY module
- * @sd: CSIPHY V4L2 subdevice
+ * cvdr_set_power - Power on/off CVDR module
+ * @sd: CVDR V4L2 subdevice
* @on: Requested power state
*
* Return 0 on success or a negative error code otherwise
@@ -241,8 +242,8 @@ static int cvdr_set_power(struct v4l2_subdev *sd, int on)
}
/*
- * cvdr_set_stream - Enable/disable streaming on CSIPHY module
- * @sd: CSIPHY V4L2 subdevice
+ * cvdr_set_stream - Enable/disable streaming on CVDR module
+ * @sd: CVDR V4L2 subdevice
* @enable: Requested streaming state
*
* Return 0 on success or a negative error code otherwise
@@ -253,6 +254,7 @@ static int cvdr_set_stream(struct v4l2_subdev *sd, int enable)
struct isp *isp = cvdr->isp;
int ret = 0;
+ pr_info("%s: %d\n", __func__, __LINE__);
enable_irq(cvdr->irq_vic1);
isp_cvdr_init(cvdr);
isp_cvdr_config(cvdr, isp->hw_addr);
@@ -263,7 +265,7 @@ static int cvdr_set_stream(struct v4l2_subdev *sd, int enable)
/*
* cvdr_init_formats - Initialize formats on all pads
- * @sd: CSIPHY V4L2 subdevice
+ * @sd: CVDR V4L2 subdevice
* @fh: V4L2 subdev file handle
*
* Initialize all pad formats with default values.
@@ -278,10 +280,12 @@ static int cvdr_init_formats(struct v4l2_subdev *sd,
irqreturn_t isp_vic1_handler(int irq, void *dev)
{
+ pr_info("%s: %d\n", __func__, __LINE__);
struct cvdr_device *cvdr = dev;
struct isp *isp = cvdr->isp;
unsigned int val = isp_clear_irq(isp, IRQ_MERGER_DEBUG_1);
+ pr_info("%s: %d\n", __func__, __LINE__);
if (val & (1 << IRQ_MERGER_SR_4_CVDR_RT_SOF_VPWR_23_OFFSET))
isp_cvdr_config(cvdr,
isp->hw_addr + frame_num2Offset(isp, isp->frame_num + 1));
@@ -290,10 +294,9 @@ irqreturn_t isp_vic1_handler(int irq, void *dev)
}
/*
- * isp_cvdr_subdev_init - Initialize CSIPHY device structure and resources
- * @sr: CSIPHY device
- * @res: CSIPHY module resources table
- * @id: CSIPHY module id
+ * isp_cvdr_subdev_init - Initialize CVDR device structure and resources
+ * @cvdr: CVDR device
+ * @res: CVDR module resources table
*
* Return 0 on success or a negative error code otherwise
*/
@@ -307,6 +310,8 @@ int isp_cvdr_subdev_init(struct isp *isp,
int ret;
cvdr->isp = isp;
+
+
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res[0].reg);
cvdr->cvdr_rt = devm_ioremap_resource(dev, r);
if (IS_ERR(cvdr->cvdr_rt)) {
@@ -347,7 +352,7 @@ int isp_cvdr_subdev_init(struct isp *isp,
}
/*
- * cvdr_link_setup - Setup CSIPHY connections
+ * cvdr_link_setup - Setup CVDR connections
* @entity: Pointer to media entity structure
* @local: Pointer to local pad
* @remote: Pointer to remote pad
@@ -397,8 +402,8 @@ static const struct media_entity_operations cvdr_media_ops = {
};
/*
- * isp_cvdr_register_entity - Register subdev node for CSIPHY module
- * @sr: CSIPHY device
+ * isp_cvdr_register_entity - Register subdev node for CVDR module
+ * @cvdr: CVDR device
* @v4l2_dev: V4L2 device
*
* Return 0 on success or a negative error code otherwise
@@ -409,7 +414,9 @@ int isp_cvdr_register_entity(struct cvdr_device *cvdr,
struct v4l2_subdev *sd = &cvdr->subdev;
struct media_pad *pads = cvdr->pads;
struct device *dev = cvdr->isp->dev;
+ struct isp_video *video_out = &cvdr->video_out;
int ret;
+ char name[32];
v4l2_subdev_init(sd, &cvdr_v4l2_ops);
sd->internal_ops = &cvdr_v4l2_internal_ops;
@@ -424,12 +431,12 @@ int isp_cvdr_register_entity(struct cvdr_device *cvdr,
return ret;
}
- pads[ISP_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
- pads[ISP_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+ pads[ISP_CVDR_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[ISP_CVDR_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
sd->entity.function = MEDIA_ENT_F_IO_V4L;
sd->entity.ops = &cvdr_media_ops;
- ret = media_entity_pads_init(&sd->entity, ISP_CSIPHY_PADS_NUM, pads);
+ ret = media_entity_pads_init(&sd->entity, ISP_CVDR_PADS_NUM, pads);
if (ret < 0) {
dev_err(dev, "Failed to init media entity: %d\n", ret);
return ret;
@@ -438,15 +445,48 @@ int isp_cvdr_register_entity(struct cvdr_device *cvdr,
ret = v4l2_device_register_subdev(v4l2_dev, sd);
if (ret < 0) {
dev_err(dev, "Failed to register subdev: %d\n", ret);
- media_entity_cleanup(&sd->entity);
+ goto error_reg_subdev;
+ }
+
+ video_out->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ video_out->isp = cvdr->isp;
+ snprintf(name, ARRAY_SIZE(name), "%s", "hisi_video");
+ ret = isp_video_register(video_out, v4l2_dev, name);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register video node: %d\n",
+ ret);
+ goto error_reg_video;
}
+ pr_info("Linking entities: %s->%s\n", sd->entity.name, video_out->vdev.entity.name);
+ ret = media_create_pad_link(
+ &sd->entity, ISP_CVDR_PAD_SRC,
+ &video_out->vdev.entity, 0,
+ MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
+ if (ret < 0) {
+ dev_err(dev, "Failed to link %s->%s entities: %d\n",
+ sd->entity.name, video_out->vdev.entity.name,
+ ret);
+ goto error_link;
+ }
+
+ return ret;
+
+error_link:
+ isp_video_unregister(video_out);
+
+error_reg_video:
+ v4l2_device_unregister_subdev(sd);
+
+error_reg_subdev:
+ media_entity_cleanup(&sd->entity);
+
return ret;
}
/*
- * isp_cvdr_unregister_entity - Unregister CSIPHY module subdev node
- * @sr: CSIPHY device
+ * isp_cvdr_unregister_entity - Unregister CVDR module subdev node
+ * @cvdr: CVDR device
*/
void isp_cvdr_unregister_entity(struct cvdr_device *cvdr)
{
diff --git a/drivers/media/platform/hisi/isp/isp-cvdr.h b/drivers/media/platform/hisi/isp/isp-cvdr.h
index 8824a7c39ba1..632284e635e6 100644
--- a/drivers/media/platform/hisi/isp/isp-cvdr.h
+++ b/drivers/media/platform/hisi/isp/isp-cvdr.h
@@ -13,9 +13,11 @@
#include <media/v4l2-mediabus.h>
#include <media/v4l2-subdev.h>
+#include "isp-video.h"
+
#define ISP_CVDR_PAD_SINK 0
#define ISP_CVDR_PAD_SRC 1
-#define ISP_CVDR_PADS_NUM 1
+#define ISP_CVDR_PADS_NUM 2
#define CVDR_SRT_CVDR_CFG_REG 0x0
#define CVDR_RT_CVDR_CFG_REG 0x0
@@ -937,6 +939,7 @@ struct cvdr_device {
void __iomem *cvdr_srt;
void __iomem *sub_ctrl;
unsigned int irq_vic1;
+ struct isp_video video_out;
};
int isp_cvdr_subdev_init(struct isp *isp,
diff --git a/drivers/media/platform/hisi/isp/isp-sr.c b/drivers/media/platform/hisi/isp/isp-sr.c
index 14f1ac4d4cef..e34b6baaf99f 100644
--- a/drivers/media/platform/hisi/isp/isp-sr.c
+++ b/drivers/media/platform/hisi/isp/isp-sr.c
@@ -22,16 +22,18 @@
void isp_sr_config(struct sr_device *sr)
{
+ struct isp *isp = sr->isp;
int data_type = 1;
union U_ID_ROUTER_1 reg1;
union U_REFORMAT reg;
+ pr_info("%s: %d\n", __func__, __LINE__);
isp_writel(sr->base, STREAM_ROUTER_CSIFILTER_A_REG,
(0 << 6) | (YUV_DT_422_8BITS << 0));
reg1.u32 = isp_readl(sr->base, STREAM_ROUTER_ID_ROUTER_1_REG);
reg1.bits.idr_enable_6 = 1;
- reg1.bits.idr_input_stream_6 = (0 << 2) | (0 << 0);
+ reg1.bits.idr_input_stream_6 = (isp->csiphy_id << 2) | (0 << 0);
isp_writel(sr->base, STREAM_ROUTER_ID_ROUTER_1_REG, reg1.u32);
reg.u32 = 0;
@@ -46,6 +48,7 @@ void isp_sr_config(struct sr_device *sr)
void isp_sr_go(struct sr_device *sr, unsigned int go_bit)
{
+ pr_info("%s: %d\n", __func__, __LINE__);
isp_writel(sr->base, STREAM_ROUTER_CSIFILTER_GO_REG, go_bit);
}
@@ -75,6 +78,7 @@ static int sr_set_stream(struct v4l2_subdev *sd, int enable)
struct sr_device *sr = v4l2_get_subdevdata(sd);
int ret = 0;
+ pr_info("%s: %d\n", __func__, __LINE__);
enable_irq(sr->irq_frproc0);
isp_config_smmu_bypass(sr->isp);
isp_sr_config(sr);
@@ -102,13 +106,14 @@ static int sr_init_formats(struct v4l2_subdev *sd,
irqreturn_t isp_frproc0_handler(int irq, void *dev)
{
+ pr_info("%s: %d\n", __func__, __LINE__);
struct sr_device *sr = dev;
struct isp *isp = sr->isp;
unsigned int val = isp_clear_irq(sr->isp, IRQ_MERGER_FRPROC_0);
if (val & (1 << IRQ_MERGER_SR_4_CVDR_RT_EOF_VPWR_23_OFFSET)) {
// FIXME: Use CSI index
- isp_sr_go(sr, (1 << 0));
+ isp_sr_go(sr, (1 << (4 * isp->csiphy_id)));
isp->frame_num++;
}
@@ -230,12 +235,6 @@ int isp_sr_register_entity(struct sr_device *sr,
ISP_SR_NAME);
v4l2_set_subdevdata(sd, sr);
- ret = sr_init_formats(sd, NULL);
- if (ret < 0) {
- dev_err(dev, "Failed to init format: %d\n", ret);
- return ret;
- }
-
pads[ISP_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
pads[ISP_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
diff --git a/drivers/media/platform/hisi/isp/isp-video.c b/drivers/media/platform/hisi/isp/isp-video.c
new file mode 100644
index 000000000000..6ce04d3e7767
--- /dev/null
+++ b/drivers/media/platform/hisi/isp/isp-video.c
@@ -0,0 +1,503 @@
+#include <linux/slab.h>
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "isp-video.h"
+#include "isp.h"
+
+/*
+ * struct isp_format_info - ISP media bus format information
+ * @code: V4L2 media bus format code
+ * @pixelformat: V4L2 pixel format FCC identifier
+ * @planes: Number of planes
+ * @hsub: Horizontal subsampling (for each plane)
+ * @vsub: Vertical subsampling (for each plane)
+ * @bpp: Bits per pixel when stored in memory (for each plane)
+ */
+struct isp_format_info {
+ char *name;
+ u32 fourcc;
+ u8 depth;
+ u8 ybpp;
+};
+
+static const struct isp_format_info dummy_format[] = {
+ {
+ .name = "YUV 4:2:2 planar, Y/Cb/Cr",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .depth = 16,
+ .ybpp = 1,
+ },
+};
+
+/* -----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+static struct v4l2_subdev *video_remote_subdev(struct isp_video *video,
+ u32 *pad)
+{
+ struct media_pad *remote;
+
+ pr_info("%s: %d\n", __func__, __LINE__);
+ remote = media_entity_remote_pad(&video->pad);
+
+ if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
+ return NULL;
+
+ if (pad)
+ *pad = remote->index;
+
+ return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+/* -----------------------------------------------------------------------------
+ * Video queue operations
+ */
+
+static int video_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct isp_video *video = vb2_get_drv_priv(q);
+ struct isp_format_info *fmt = video->formats;
+ const struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix;
+
+ pr_info("%s: %d\n", __func__, __LINE__);
+ *num_planes = 1;
+
+ sizes[0] = (pix->width * pix->height * fmt->depth) / 8;
+
+ return 0;
+}
+
+static int video_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct isp_video *video = vb2_get_drv_priv(vb->vb2_queue);
+ const struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix;
+
+ pr_info("%s: %d\n", __func__, __LINE__);
+ vb2_set_plane_payload(vb, 0, video->payload);
+
+ vbuf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static void video_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct isp_video *video = vb2_get_drv_priv(vb->vb2_queue);
+ struct isp_buffer *buffer = container_of(vbuf, struct isp_buffer,
+ vb);
+ pr_info("%s: %d\n", __func__, __LINE__);
+ /* TODO: Push the buffer to hardware */
+}
+
+static int video_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct isp_video *video = vb2_get_drv_priv(q);
+ struct video_device *vdev = &video->vdev;
+ struct media_entity *entity;
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ pr_info("%s: %d\n", __func__, __LINE__);
+ ret = media_pipeline_start(&vdev->entity, &video->pipe);
+ if (ret < 0)
+ return ret;
+
+ pr_info("%s: %d\n", __func__, __LINE__);
+ entity = &vdev->entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_pad(pad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ ret = v4l2_subdev_call(subdev, video, s_stream, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ goto error;
+ }
+
+ pr_info("%s: %d\n", __func__, __LINE__);
+ return 0;
+
+error:
+ media_pipeline_stop(&vdev->entity);
+
+ video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED);
+
+ return ret;
+}
+
+static void video_stop_streaming(struct vb2_queue *q)
+{
+ struct isp_video *video = vb2_get_drv_priv(q);
+ struct video_device *vdev = &video->vdev;
+ struct media_entity *entity;
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+
+ entity = &vdev->entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_pad(pad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ v4l2_subdev_call(subdev, video, s_stream, 0);
+ }
+
+ media_pipeline_stop(&vdev->entity);
+
+ video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
+}
+
+static const struct vb2_ops isp_video_vb2_q_ops = {
+ .queue_setup = video_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_prepare = video_buf_prepare,
+ .buf_queue = video_buf_queue,
+ .start_streaming = video_start_streaming,
+ .stop_streaming = video_stop_streaming,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+static int video_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct isp_video *video = video_drvdata(file);
+
+ strlcpy(cap->driver, "hisi-isp", sizeof(cap->driver));
+ strlcpy(cap->card, "HiSilicon ISP", sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ dev_name(video->isp->dev));
+
+ return 0;
+}
+
+static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct isp_video *video = video_drvdata(file);
+
+ f->pixelformat = video->formats->fourcc;
+
+ return 0;
+}
+
+static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct isp_format_info *fmt = video->formats;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ pix->bytesperline = pix->width * fmt->ybpp;
+ pix->sizeimage = (pix->width * pix->height * fmt->depth) / 8;
+
+ pix->pixelformat = fmt->fourcc;
+ pix->width = pix->width;
+ pix->height = pix->height;
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_JPEG;
+
+ *f = video->active_fmt;
+
+ return 0;
+}
+
+static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ /* TODO: Evaluate and set frame format from userspace */
+ video->payload = pix->sizeimage;
+ video->active_fmt = *f;
+
+ pr_info("Payload size set to: %d\n", video->payload);
+
+ return 0;
+}
+
+static int video_enum_input(struct file *file, void *fh,
+ struct v4l2_input *input)
+{
+ if (input->index > 0)
+ return -EINVAL;
+
+ strlcpy(input->name, "camera", sizeof(input->name));
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct isp_video *video = video_drvdata(file);
+
+ return 0;
+}
+
+static int video_g_input(struct file *file, void *fh, unsigned int *input)
+{
+ *input = 0;
+
+ return 0;
+}
+
+static int video_s_input(struct file *file, void *fh, unsigned int input)
+{
+ return input == 0 ? 0 : -EINVAL;
+}
+
+static const struct v4l2_ioctl_ops isp_vid_ioctl_ops = {
+ .vidioc_querycap = video_querycap,
+ .vidioc_enum_fmt_vid_cap_mplane = video_enum_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = video_g_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = video_s_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = video_try_fmt,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_input = video_enum_input,
+ .vidioc_g_input = video_g_input,
+ .vidioc_s_input = video_s_input,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+
+static int video_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_fh *vfh;
+ int ret;
+
+ mutex_lock(&video->lock);
+
+ vfh = kzalloc(sizeof(*vfh), GFP_KERNEL);
+ if (vfh == NULL) {
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+
+ v4l2_fh_init(vfh, vdev);
+ v4l2_fh_add(vfh);
+
+ file->private_data = vfh;
+
+ ret = v4l2_pipeline_pm_use(&vdev->entity, 1);
+ if (ret < 0) {
+ dev_err(video->isp->dev, "Failed to power up pipeline: %d\n",
+ ret);
+ goto error_pm_use;
+ }
+
+ mutex_unlock(&video->lock);
+
+ return 0;
+
+error_pm_use:
+ v4l2_fh_release(file);
+
+error_alloc:
+ mutex_unlock(&video->lock);
+
+ return ret;
+}
+
+static int video_release(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ vb2_fop_release(file);
+
+ v4l2_pipeline_pm_use(&vdev->entity, 0);
+
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static const struct v4l2_file_operations isp_vid_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = video_open,
+ .release = video_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .read = vb2_fop_read,
+};
+
+/* -----------------------------------------------------------------------------
+ * CAMSS video core
+ */
+
+static void isp_video_release(struct video_device *vdev)
+{
+ struct isp_video *video = video_get_drvdata(vdev);
+
+ media_entity_cleanup(&vdev->entity);
+
+ mutex_destroy(&video->q_lock);
+ mutex_destroy(&video->lock);
+}
+
+/*
+ * isp_video_init_format - Helper function to initialize format
+ * @video: struct isp_video
+ *
+ * Initialize pad format with default value.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int isp_video_init_format(struct isp_video *video)
+{
+ int ret;
+ struct v4l2_format format = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ .fmt.pix = {
+ .width = 1920,
+ .height = 1080,
+ .pixelformat = V4L2_PIX_FMT_YUV422P,
+ },
+ };
+
+ video->active_fmt = format;
+
+ return 0;
+}
+
+/*
+ * isp_video_register - Register a video device node
+ * @video: struct isp_video
+ * @v4l2_dev: V4L2 device
+ * @name: name to be used for the video device node
+ *
+ * Initialize and register a video device node to a V4L2 device. Also
+ * initialize the vb2 queue.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+
+int isp_video_register(struct isp_video *video, struct v4l2_device *v4l2_dev,
+ const char *name)
+{
+ struct media_pad *pad = &video->pad;
+ struct video_device *vdev;
+ struct vb2_queue *q;
+ int ret;
+
+ vdev = &video->vdev;
+
+ mutex_init(&video->q_lock);
+
+ q = &video->vb2_q;
+ q->drv_priv = video;
+ q->mem_ops = &vb2_dma_sg_memops;
+ q->ops = &isp_video_vb2_q_ops;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->buf_struct_size = sizeof(struct isp_buffer);
+ q->dev = video->isp->dev;
+ q->lock = &video->q_lock;
+ ret = vb2_queue_init(q);
+ if (ret < 0) {
+ dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret);
+ goto error_vb2_init;
+ }
+
+ pad->flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vdev->entity, 1, pad);
+ if (ret < 0) {
+ dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n",
+ ret);
+ goto error_media_init;
+ }
+
+ mutex_init(&video->lock);
+
+ video->formats = dummy_format;
+ video->nformats = ARRAY_SIZE(dummy_format);
+
+ ret = isp_video_init_format(video);
+ if (ret < 0) {
+ dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret);
+ goto error_video_register;
+ }
+
+ vdev->fops = &isp_vid_fops;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ vdev->ioctl_ops = &isp_vid_ioctl_ops;
+ vdev->release = isp_video_release;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->queue = &video->vb2_q;
+ vdev->lock = &video->lock;
+ strlcpy(vdev->name, name, sizeof(vdev->name));
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ dev_err(v4l2_dev->dev, "Failed to register video device: %d\n",
+ ret);
+ goto error_video_register;
+ }
+
+ video_set_drvdata(vdev, video);
+
+ return 0;
+
+error_video_register:
+ media_entity_cleanup(&vdev->entity);
+ mutex_destroy(&video->lock);
+error_media_init:
+ vb2_queue_release(&video->vb2_q);
+error_vb2_init:
+ mutex_destroy(&video->q_lock);
+
+ return ret;
+}
+
+void isp_video_stop_streaming(struct isp_video *video)
+{
+ if (vb2_is_streaming(&video->vb2_q))
+ vb2_queue_release(&video->vb2_q);
+}
+
+void isp_video_unregister(struct isp_video *video)
+{
+ video_unregister_device(&video->vdev);
+}
diff --git a/drivers/media/platform/hisi/isp/isp-video.h b/drivers/media/platform/hisi/isp/isp-video.h
new file mode 100644
index 000000000000..1ca7015f1da3
--- /dev/null
+++ b/drivers/media/platform/hisi/isp/isp-video.h
@@ -0,0 +1,54 @@
+#ifndef ISP_VIDEO_H
+#define ISP_VIDEO_H
+
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-mediabus.h>
+#include <media/videobuf2-v4l2.h>
+
+struct isp_buffer {
+ struct vb2_v4l2_buffer vb;
+ dma_addr_t addr[3];
+ struct list_head queue;
+};
+
+struct isp_video;
+
+struct isp_video_ops {
+ int (*queue_buffer)(struct isp_video *vid, struct isp_buffer *buf);
+ int (*flush_buffers)(struct isp_video *vid,
+ enum vb2_buffer_state state);
+};
+
+struct isp_format_info;
+
+struct isp_video {
+ struct isp *isp;
+ struct vb2_queue vb2_q;
+ struct video_device vdev;
+ struct media_pad pad;
+ struct v4l2_format active_fmt;
+ enum v4l2_buf_type type;
+ struct media_pipeline pipe;
+ const struct isp_video_ops *ops;
+ struct mutex lock;
+ struct mutex q_lock;
+ unsigned int bpl_alignment;
+ unsigned int line_based;
+ unsigned int nformats;
+ unsigned int payload;
+ const struct isp_format_info *formats;
+};
+
+void isp_video_stop_streaming(struct isp_video *video);
+
+int isp_video_register(struct isp_video *video, struct v4l2_device *v4l2_dev,
+ const char *name);
+
+void isp_video_unregister(struct isp_video *video);
+
+#endif /* ISP_VIDEO_H */
diff --git a/drivers/media/platform/hisi/isp/isp.c b/drivers/media/platform/hisi/isp/isp.c
index f71fbe6fbaf2..7b0cceee74ca 100644
--- a/drivers/media/platform/hisi/isp/isp.c
+++ b/drivers/media/platform/hisi/isp/isp.c
@@ -96,17 +96,20 @@ int frame_num2Offset(struct isp *isp, int frame_num)
void isp_config_smmu_bypass(struct isp *isp)
{
+ pr_info("%s: %d\n", __func__, __LINE__);
isp_writel(isp->smmu_ctrl, 0x0, 0x1);
}
void isp_ispss_enable_irq(struct isp *isp)
{
+ pr_info("%s: %d\n", __func__, __LINE__);
isp_writel(isp->irq_merger2, IRQ_MERGER_IMSC_DEBUG1, 0xFFFFFFFF);
isp_writel(isp->irq_merger2, IRQ_MERGER_IMSC_FRPROC0, 0xFFFFFFFF);
}
void isp_ispss_clear_irq_state(struct isp *isp)
{
+ pr_info("%s: %d\n", __func__, __LINE__);
isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_DEBUG0, 0xFFFFFFFF);
isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_DEBUG1, 0xFFFFFFFF);
isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_DEBUG2, 0xFFFFFFFF);
@@ -226,32 +229,25 @@ int isp_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock)
*/
static int isp_of_parse_endpoint_node(struct device *dev,
struct device_node *node,
- struct isp_async_subdev *csd)
+ struct isp_async_subdev *csd,
+ struct v4l2_async_notifier *async)
{
- struct csiphy_lanes_cfg *lncfg = &csd->interface.csi2.lane_cfg;
+ struct csiphy_config *cfg = &csd->interface.cfg;
struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2;
- struct v4l2_fwnode_endpoint vep = { { 0 } };
+ struct v4l2_fwnode_endpoint vep = { .bus_type = 0 };
+ struct isp *isp = container_of(async, struct isp, notifier);
unsigned int i;
v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep);
- csd->interface.csiphy_id = vep.base.port;
+ /* CSI2PHY port id */
+ isp->csiphy_id = vep.base.port;
+ pr_info("CSIPHY ID: %d\n", isp->csiphy_id);
mipi_csi2 = &vep.bus.mipi_csi2;
- lncfg->clk.pos = mipi_csi2->clock_lane;
- lncfg->clk.pol = mipi_csi2->lane_polarities[0];
- lncfg->num_data = mipi_csi2->num_data_lanes;
-
- lncfg->data = devm_kcalloc(dev,
- lncfg->num_data, sizeof(*lncfg->data),
- GFP_KERNEL);
- if (!lncfg->data)
- return -ENOMEM;
- for (i = 0; i < lncfg->num_data; i++) {
- lncfg->data[i].pos = mipi_csi2->data_lanes[i];
- lncfg->data[i].pol = mipi_csi2->lane_polarities[i + 1];
- }
+ /* CSI2PHY data lanes */
+ cfg->num_data = mipi_csi2->num_data_lanes;
return 0;
}
@@ -299,7 +295,7 @@ static int isp_of_parse_ports(struct device *dev,
notifier->subdevs[i++] = &csd->asd;
- ret = isp_of_parse_endpoint_node(dev, node, csd);
+ ret = isp_of_parse_endpoint_node(dev, node, csd, notifier);
if (ret < 0) {
of_node_put(node);
return ret;
@@ -453,10 +449,11 @@ static int isp_subdev_notifier_bound(struct v4l2_async_notifier *async,
struct isp *isp = container_of(async, struct isp, notifier);
struct isp_async_subdev *csd =
container_of(asd, struct isp_async_subdev, asd);
- u8 id = csd->interface.csiphy_id;
+ u8 id = isp->csiphy_id;
struct csiphy_device *csiphy = &isp->csiphy[id];
- csiphy->cfg.csi2 = &csd->interface.csi2;
+ pr_info("Notifier bound CSIPHY id: %d\n", id);
+ csiphy->cfg.num_data = csd->interface.cfg.num_data;
subdev->host_priv = csiphy;
return 0;
@@ -469,6 +466,7 @@ static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async)
struct v4l2_subdev *sd;
int ret;
+ pr_info("Notifier complete\n");
list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
if (sd->host_priv) {
struct media_entity *sensor = &sd->entity;
@@ -594,31 +592,26 @@ static int isp_probe(struct platform_device *pdev)
if (ret < 0)
goto err_cleanup;
- isp->virt_addr = dma_alloc_coherent(dev, isp->pool_size,
- &(isp->hw_addr), GFP_KERNEL);
- if (!isp->virt_addr) {
- pr_err("dma_alloc_coherent (ISP) alloc err!\n");
- ret = -ENOMEM;
- goto err_cleanup;
- }
-
- memset(isp->virt_addr, 0, isp->pool_size);
+ pr_info("%s: %d\n", __func__, __LINE__);
ret = isp_of_parse_ports(dev, &isp->notifier);
if (ret < 0) {
goto err_cleanup;
}
+ pr_info("%s: %d\n", __func__, __LINE__);
ret = isp_init_subdevices(isp);
if (ret < 0)
goto err_cleanup;
+ pr_info("%s: %d\n", __func__, __LINE__);
isp->media_dev.dev = isp->dev;
strlcpy(isp->media_dev.model, "HiSilicon ISP",
sizeof(isp->media_dev.model));
isp->media_dev.ops = &isp_media_ops;
media_device_init(&isp->media_dev);
+ pr_info("%s: %d\n", __func__, __LINE__);
/* FIXME: Get these from userspace */
isp->frame_size = 0x3F4800;
isp->frame_count = 4;
@@ -630,11 +623,15 @@ static int isp_probe(struct platform_device *pdev)
goto err_cleanup;
}
+ pr_info("%s: %d\n", __func__, __LINE__);
ret = isp_register_entities(isp);
if (ret < 0)
goto err_register_entities;
+ pr_info("%s: %d\n", __func__, __LINE__);
+
if (isp->notifier.num_subdevs) {
+ pr_info("%s: %d\n", __func__, __LINE__);
isp->notifier.ops = &isp_subdev_notifier_ops;
ret = v4l2_async_notifier_register(&isp->v4l2_dev,
@@ -646,6 +643,7 @@ static int isp_probe(struct platform_device *pdev)
goto err_register_subdevs;
}
} else {
+ pr_info("%s: %d\n", __func__, __LINE__);
ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
if (ret < 0) {
dev_err(dev, "Failed to register subdev nodes: %d\n",
@@ -661,6 +659,7 @@ static int isp_probe(struct platform_device *pdev)
}
}
+ pr_info("%s: %d\n", __func__, __LINE__);
return 0;
err_register_subdevs:
diff --git a/drivers/media/platform/hisi/isp/isp.h b/drivers/media/platform/hisi/isp/isp.h
index dc9aa590d615..2803e23e4d1e 100644
--- a/drivers/media/platform/hisi/isp/isp.h
+++ b/drivers/media/platform/hisi/isp/isp.h
@@ -130,6 +130,7 @@ struct isp {
void *virt_addr;
dma_addr_t hw_addr;
int csiphy_num;
+ u8 csiphy_id;
unsigned int pool_size;
unsigned int frame_size;
unsigned int frame_count;
@@ -137,8 +138,7 @@ struct isp {
};
struct isp_camera_interface {
- u8 csiphy_id;
- struct csiphy_csi2_cfg csi2;
+ struct csiphy_config cfg;
};
struct isp_async_subdev {